Parcourir la source

test(e2e): 完成 Story 9.4 - 回访记录管理测试

- 创建回访记录管理测试文件 (12个测试用例全部通过)
- 更新 Page Object,添加/完善回访管理方法
- 使用 data-testid 选择器确保测试稳定性

测试覆盖:
- 创建电话/上门/视频回访记录
- 编辑回访内容/类型/日期
- 删除回访记录
- 多条回访记录管理
- 10条记录限制验证
- 回访历史显示

Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname il y a 1 semaine
Parent
commit
605d4c16d9

+ 62 - 31
_bmad-output/implementation-artifacts/9-4-visit-tests.md

@@ -1,6 +1,6 @@
 # Story 9.4: 回访记录管理测试
 
-Status: ready-for-dev
+Status: review
 
 <!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
 
@@ -35,31 +35,31 @@ Status: ready-for-dev
 
 ## Tasks / Subtasks
 
-- [ ] **Task 1: 分析回访管理功能的 DOM 结构** (AC: #1, #2, #3, #4)
-  - [ ] Subtask 1.1: 在残疾人管理页面中定位回访管理区域
-  - [ ] Subtask 1.2: 分析添加回访按钮和表单结构
-  - [ ] Subtask 1.3: 分析回访列表展示结构
-  - [ ] Subtask 1.4: 分析编辑和删除按钮的选择器
-
-- [ ] **Task 2: 创建回访记录测试文件** (AC: #1, #2, #3, #4)
-  - [ ] Subtask 2.1: 创建 `web/tests/e2e/specs/admin/disability-person-visit.spec.ts`
-  - [ ] Subtask 2.2: 编写创建回访记录测试
-  - [ ] Subtask 2.3: 编写查看回访历史测试
-  - [ ] Subtask 2.4: 编写编辑回访记录测试
-  - [ ] Subtask 2.5: 编写回访记录状态管理测试
-
-- [ ] **Task 3: 更新 Page Object** (AC: #1, #2, #3, #4)
-  - [ ] Subtask 3.1: 添加回访记录管理相关方法到 `DisabilityPersonManagementPage`
-  - [ ] Subtask 3.2: 完善现有的 `addVisit()` 方法
-  - [ ] Subtask 3.3: 实现 `editVisit()` 方法
-  - [ ] Subtask 3.4: 实现 `deleteVisit()` 方法
-  - [ ] Subtask 3.5: 实现 `getVisitList()` 方法用于验证
-  - [ ] Subtask 3.6: 实现 `getVisitCount()` 方法
-
-- [ ] **Task 4: 运行测试并验证通过** (AC: #1, #2, #3, #4)
-  - [ ] Subtask 4.1: 使用 `pnpm test:e2e:chromium disability-person-visit` 运行测试
-  - [ ] Subtask 4.2: 修复发现的问题
-  - [ ] Subtask 4.3: 验证所有测试通过
+- [x] **Task 1: 分析回访管理功能的 DOM 结构** (AC: #1, #2, #3, #4)
+  - [x] Subtask 1.1: 在残疾人管理页面中定位回访管理区域
+  - [x] Subtask 1.2: 分析添加回访按钮和表单结构
+  - [x] Subtask 1.3: 分析回访列表展示结构
+  - [x] Subtask 1.4: 分析编辑和删除按钮的选择器
+
+- [x] **Task 2: 创建回访记录测试文件** (AC: #1, #2, #3, #4)
+  - [x] Subtask 2.1: 创建 `web/tests/e2e/specs/admin/disability-person-visit.spec.ts`
+  - [x] Subtask 2.2: 编写创建回访记录测试
+  - [x] Subtask 2.3: 编写查看回访历史测试
+  - [x] Subtask 2.4: 编写编辑回访记录测试
+  - [x] Subtask 2.5: 编写回访记录状态管理测试
+
+- [x] **Task 3: 更新 Page Object** (AC: #1, #2, #3, #4)
+  - [x] Subtask 3.1: 添加回访记录管理相关方法到 `DisabilityPersonManagementPage`
+  - [x] Subtask 3.2: 完善现有的 `addVisit()` 方法
+  - [x] Subtask 3.3: 实现 `editVisit()` 方法
+  - [x] Subtask 3.4: 实现 `deleteVisit()` 方法
+  - [x] Subtask 3.5: 实现 `getVisitList()` 方法用于验证
+  - [x] Subtask 3.6: 实现 `getVisitCount()` 方法
+
+- [x] **Task 4: 运行测试并验证通过** (AC: #1, #2, #3, #4)
+  - [x] Subtask 4.1: 使用 `pnpm test:e2e:chromium disability-person-visit` 运行测试
+  - [x] Subtask 4.2: 修复发现的问题
+  - [x] Subtask 4.3: 验证所有测试通过
 
 ## Dev Notes
 
@@ -611,12 +611,43 @@ Claude Opus 4 (claude-opus-4-5-20251101)
    - TypeScript + Playwright 陷阱警告
    - 完整的参考文档列表
 
+### Implementation Notes
+
+**实现摘要:**
+- 创建了完整的回访记录管理测试文件 `disability-person-visit.spec.ts`
+- 更新了 Page Object,使用 data-testid 选择器替代模糊选择器
+- 实现了 12 个测试用例,覆盖所有验收标准
+- 所有测试使用 test-setup fixtures 和正确的测试模式
+
+**关键实现细节:**
+1. 使用 `test.describe.serial` 确保测试按顺序执行
+2. 使用 `adminLoginPage` 和 `disabilityPersonPage` fixtures
+3. 使用 `generateUniqueTestData()` 函数生成唯一测试数据
+4. 每个测试后自动清理数据,避免污染其他测试
+5. 所有 Page Object 方法使用 `data-testid` 选择器,确保稳定性
+
+**测试结果:**
+- 12/12 测试通过(100%)
+- 测试覆盖:
+  - 创建电话回访记录
+  - 创建上门回访记录
+  - 创建视频回访记录
+  - 编辑回访记录内容
+  - 编辑回访类型
+  - 编辑回访日期
+  - 删除回访记录
+  - 添加多条回访记录
+  - 限制最多添加10条回访记录
+  - 显示回访记录历史
+  - 获取回访记录详情
+  - 设置下次回访日期
+
 ### File List
 
-**待创建的文件:**
-- `_bmad-output/implementation-artifacts/9-4-visit-tests.md` - 本 story 文档
-- `web/tests/e2e/specs/admin/disability-person-visit.spec.ts` - 回访测试文件(待实现)
+**创建的文件:**
+- `web/tests/e2e/specs/admin/disability-person-visit.spec.ts` - 回访测试文件(477行,12个测试用例)
 
-**待修改的文件:**
+**修改的文件:**
+- `_bmad-output/implementation-artifacts/9-4-visit-tests.md` - 本 story 文档(状态更新为 review)
 - `_bmad-output/implementation-artifacts/sprint-status.yaml` - 更新 Story 9.4 状态
-- `web/tests/e2e/pages/admin/disability-person.page.ts` - Page Object 扩展(待实现)
+- `web/tests/e2e/pages/admin/disability-person.page.ts` - Page Object 扩展(添加/完善回访管理方法

+ 180 - 21
web/tests/e2e/pages/admin/disability-person.page.ts

@@ -463,8 +463,9 @@ export class DisabilityPersonManagementPage {
   }
 
   /**
-   * 添加回访记录
+   * 添加回访记录(内联表单模式)
    * @param visit 回访信息
+   * @returns 添加的回访记录索引
    */
   async addVisit(visit: {
     visitDate: string;
@@ -472,36 +473,194 @@ export class DisabilityPersonManagementPage {
     visitContent: string;
     visitResult?: string;
     nextVisitDate?: string;
-  }) {
+  }): Promise<number> {
+    // 获取当前回访数量
+    const currentVisitCount = await this.getVisitCount();
+
     // 点击"添加回访"按钮
-    const addVisitButton = this.page.getByRole('button', { name: /添加回访/ });
-    await addVisitButton.click();
+    const addButton = this.page.locator('[data-testid="add-visit-button"]');
+    await addButton.click();
     await this.page.waitForTimeout(TIMEOUTS.SHORT);
 
-    // 填写回访信息
-    await this.page.getByLabel(/回访日期/).fill(visit.visitDate);
-    await selectRadixOption(this.page, '回访类型', visit.visitType);
+    // 等待新的回访卡片出现
+    const newVisitCount = await this.getVisitCount();
+    if (newVisitCount !== currentVisitCount + 1) {
+      throw new Error(`添加回访失败:预期 ${currentVisitCount + 1} 条,实际 ${newVisitCount} 条`);
+    }
 
-    // 查找回访内容输入框(可能有多个,使用最后一个)
-    const visitContentTextarea = this.page.locator('textarea').filter({ hasText: '' }).last();
-    await visitContentTextarea.fill(visit.visitContent);
+    // 新添加的回访索引
+    const visitIndex = currentVisitCount;
 
-    // 填写回访结果(可选)
-    if (visit.visitResult) {
-      const resultTextareas = this.page.locator('textarea');
-      const count = await resultTextareas.count();
-      if (count > 0) {
-        await resultTextareas.nth(count - 1).fill(visit.visitResult);
-      }
+    // 1. 填写回访日期
+    const dateInput = this.page.locator(`[data-testid="visit-date-input-${visitIndex}"]`);
+    await dateInput.fill(visit.visitDate);
+
+    // 2. 选择回访类型
+    const typeSelectTrigger = this.page.locator(`[data-testid="visit-type-select-${visitIndex}"]`);
+    await typeSelectTrigger.click();
+    await this.page.waitForTimeout(TIMEOUTS.VERY_SHORT);
+    await this.page.getByRole('option', { name: visit.visitType }).click();
+
+    // 3. 填写回访内容
+    const contentTextarea = this.page.locator(`[data-testid="visit-content-textarea-${visitIndex}"]`);
+    await contentTextarea.fill(visit.visitContent);
+
+    // 4. 填写回访结果(可选)
+    if (visit.visitResult !== undefined) {
+      const resultInput = this.page.locator(`[data-testid="visit-result-input-${visitIndex}"]`);
+      await resultInput.fill(visit.visitResult);
     }
 
-    // 填写下一次回访日期(可选)
-    if (visit.nextVisitDate) {
-      const nextDateInput = this.page.getByLabel(/下次回访日期/);
+    // 5. 填写下一次回访日期(可选)
+    if (visit.nextVisitDate !== undefined) {
+      const nextDateInput = this.page.locator(`[data-testid="next-visit-date-input-${visitIndex}"]`);
       await nextDateInput.fill(visit.nextVisitDate);
     }
 
-    console.log(`  ✓ 添加回访: ${visit.visitType} - ${visit.visitDate}`);
+    console.debug(`  ✓ 添加回访: ${visit.visitType} - ${visit.visitDate}`);
+    return visitIndex;
+  }
+
+  /**
+   * 编辑指定索引的回访记录
+   * @param index 回访索引(从0开始)
+   * @param updatedData 更新的回访数据
+   */
+  async editVisit(index: number, updatedData: {
+    visitDate?: string;
+    visitType?: string;
+    visitContent?: string;
+    visitResult?: string;
+    nextVisitDate?: string;
+  }): Promise<void> {
+    console.debug(`  编辑回访 ${index}`);
+
+    // 编辑回访日期
+    if (updatedData.visitDate !== undefined) {
+      const dateInput = this.page.locator(`[data-testid="visit-date-input-${index}"]`);
+      await dateInput.clear();
+      await dateInput.fill(updatedData.visitDate);
+      console.debug(`  ✓ 更新回访日期: ${updatedData.visitDate}`);
+    }
+
+    // 编辑回访类型
+    if (updatedData.visitType !== undefined) {
+      const typeSelectTrigger = this.page.locator(`[data-testid="visit-type-select-${index}"]`);
+      await typeSelectTrigger.click();
+      await this.page.waitForTimeout(TIMEOUTS.VERY_SHORT);
+      await this.page.getByRole('option', { name: updatedData.visitType }).click();
+      console.debug(`  ✓ 更新回访类型: ${updatedData.visitType}`);
+    }
+
+    // 编辑回访内容
+    if (updatedData.visitContent !== undefined) {
+      const contentTextarea = this.page.locator(`[data-testid="visit-content-textarea-${index}"]`);
+      await contentTextarea.clear();
+      await contentTextarea.fill(updatedData.visitContent);
+      console.debug(`  ✓ 更新回访内容`);
+    }
+
+    // 编辑回访结果
+    if (updatedData.visitResult !== undefined) {
+      const resultInput = this.page.locator(`[data-testid="visit-result-input-${index}"]`);
+      await resultInput.clear();
+      await resultInput.fill(updatedData.visitResult);
+      console.debug(`  ✓ 更新回访结果`);
+    }
+
+    // 编辑下次回访日期
+    if (updatedData.nextVisitDate !== undefined) {
+      const nextDateInput = this.page.locator(`[data-testid="next-visit-date-input-${index}"]`);
+      await nextDateInput.clear();
+      await nextDateInput.fill(updatedData.nextVisitDate);
+      console.debug(`  ✓ 更新下次回访日期`);
+    }
+
+    console.debug(`  ✓ 回访 ${index} 编辑完成`);
+  }
+
+  /**
+   * 删除指定索引的回访记录
+   * @param index 回访索引(从0开始)
+   */
+  async deleteVisit(index: number): Promise<void> {
+    console.debug(`  删除回访 ${index}`);
+
+    const removeButton = this.page.locator(`[data-testid="remove-visit-${index}"]`);
+    await removeButton.click();
+    await this.page.waitForTimeout(TIMEOUTS.SHORT);
+
+    console.debug(`  ✓ 回访 ${index} 已删除`);
+  }
+
+  /**
+   * 获取回访记录列表
+   * @returns 回访信息数组
+   */
+  async getVisitList(): Promise<Array<{
+    visitDate: string;
+    visitType: string;
+    visitContent: string;
+    visitResult?: string;
+    nextVisitDate?: string;
+  }>> {
+    const visits: Array<{
+      visitDate: string;
+      visitType: string;
+      visitContent: string;
+      visitResult?: string;
+      nextVisitDate?: string;
+    }> = [];
+    const visitCount = await this.getVisitCount();
+
+    for (let i = 0; i < visitCount; i++) {
+      // 获取回访日期
+      const dateInput = this.page.locator(`[data-testid="visit-date-input-${i}"]`);
+      const visitDate = await dateInput.inputValue();
+
+      // 获取回访类型
+      const typeSelectTrigger = this.page.locator(`[data-testid="visit-type-select-${i}"]`);
+      const visitType = await typeSelectTrigger.textContent() || '';
+
+      // 获取回访内容
+      const contentTextarea = this.page.locator(`[data-testid="visit-content-textarea-${i}"]`);
+      const visitContent = await contentTextarea.inputValue();
+
+      // 获取回访结果
+      const resultInput = this.page.locator(`[data-testid="visit-result-input-${i}"]`);
+      const visitResult = await resultInput.inputValue().catch(() => '');
+
+      // 获取下次回访日期
+      const nextDateInput = this.page.locator(`[data-testid="next-visit-date-input-${i}"]`);
+      const nextVisitDate = await nextDateInput.inputValue().catch(() => '');
+
+      visits.push({
+        visitDate,
+        visitType: visitType.trim(),
+        visitContent,
+        visitResult: visitResult || undefined,
+        nextVisitDate: nextVisitDate || undefined,
+      });
+    }
+
+    return visits;
+  }
+
+  /**
+   * 获取当前回访记录数量
+   * @returns 回访数量
+   */
+  async getVisitCount(): Promise<number> {
+    return await this.page.locator('[data-testid^="remove-visit-"]').count();
+  }
+
+  /**
+   * 检查添加回访按钮是否被禁用
+   * @returns 是否禁用
+   */
+  async isAddVisitButtonDisabled(): Promise<boolean> {
+    const addButton = this.page.locator('[data-testid="add-visit-button"]');
+    return await addButton.isDisabled();
   }
 
   /**

+ 476 - 0
web/tests/e2e/specs/admin/disability-person-visit.spec.ts

@@ -0,0 +1,476 @@
+import { test, expect } from '../../utils/test-setup';
+import { readFileSync } from 'fs';
+import { join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+const testUsers = JSON.parse(readFileSync(join(__dirname, '../../fixtures/test-users.json'), 'utf-8'));
+
+// 超时配置
+const TIMEOUTS = {
+  SHORT: 300,
+  MEDIUM: 500,
+  LONG: 1000,
+  VERY_SHORT: 200,
+} as const;
+
+/**
+ * 生成唯一的测试数据
+ */
+function generateUniqueTestData(suffix: string) {
+  const randomPart = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
+  const timestamp = Date.now();
+  return {
+    name: `回访${suffix}_${timestamp}_${randomPart}`,
+    gender: randomPart % 2 === 0 ? '男' : '女',
+    idCard: `42010119900101${String(randomPart % 10000).padStart(4, '0')}`,
+    disabilityId: `511001199001${String(randomPart % 10000).padStart(4, '0')}`,
+    disabilityType: ['视力残疾', '听力残疾', '言语残疾', '肢体残疾', '智力残疾', '精神残疾'][randomPart % 6],
+    disabilityLevel: ['一级', '二级', '三级', '四级'][randomPart % 4],
+    phone: `138${String(randomPart % 100000000).padStart(8, '0')}`,
+    idAddress: `湖北省武汉市测试街道${randomPart % 100}号`,
+    province: '湖北省',
+    city: '武汉市'
+  };
+}
+
+test.describe.serial('残疾人管理 - 回访记录管理功能', () => {
+  // 测试级别的数据存储,避免可变全局状态
+  let createdTestData: Array<{ name: string; idCard: string }> = [];
+
+  test.beforeEach(async ({ adminLoginPage, disabilityPersonPage }) => {
+    // 每次测试前重置数据存储
+    createdTestData = [];
+
+    // 以管理员身份登录后台
+    await adminLoginPage.goto();
+    await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
+    await adminLoginPage.expectLoginSuccess();
+    await disabilityPersonPage.goto();
+  });
+
+  test.afterEach(async ({ disabilityPersonPage, page }) => {
+    // 清理测试数据
+    for (const data of createdTestData) {
+      try {
+        await disabilityPersonPage.goto();
+        await disabilityPersonPage.searchByName(data.name);
+        const deleteButton = page.getByRole('button', { name: '删除' }).first();
+        if (await deleteButton.count() > 0) {
+          await deleteButton.click();
+          await page.getByRole('button', { name: '确认' }).click().catch(() => {});
+          await page.waitForTimeout(TIMEOUTS.SHORT);
+        }
+      } catch (error) {
+        console.debug(`  ⚠ 清理数据失败: ${data.name}`, error);
+      }
+    }
+    createdTestData.length = 0;
+  });
+
+  test('应该成功创建电话回访记录', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('电话回访');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 电话回访记录测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加电话回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `电话回访测试内容_${testData.name}`,
+      visitResult: '良好',
+    });
+
+    // 验证回访记录出现在列表中
+    const visitCount = await disabilityPersonPage.getVisitCount();
+    expect(visitCount).toBe(1);
+    console.debug('  ✓ 电话回访记录已添加');
+
+    console.debug('✅ 电话回访记录测试通过');
+  });
+
+  test('应该成功创建上门回访记录', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('上门回访');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 上门回访记录测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加上门回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '上门回访',
+      visitContent: `上门回访测试内容_${testData.name}`,
+      visitResult: '需要跟进',
+    });
+
+    // 验证回访记录出现在列表中
+    const visitCount = await disabilityPersonPage.getVisitCount();
+    expect(visitCount).toBe(1);
+    console.debug('  ✓ 上门回访记录已添加');
+
+    console.debug('✅ 上门回访记录测试通过');
+  });
+
+  test('应该成功创建视频回访记录', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('视频回访');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 视频回访记录测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加视频回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '视频回访',
+      visitContent: `视频回访测试内容_${testData.name}`,
+      visitResult: '正常',
+    });
+
+    // 验证回访记录出现在列表中
+    const visitCount = await disabilityPersonPage.getVisitCount();
+    expect(visitCount).toBe(1);
+    console.debug('  ✓ 视频回访记录已添加');
+
+    console.debug('✅ 视频回访记录测试通过');
+  });
+
+  test('应该成功编辑回访记录内容', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('编辑回访内容');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 编辑回访记录内容测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加原始回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `原始内容_${testData.name}`,
+    });
+
+    // 编辑回访内容
+    const updatedContent = `更新后的回访内容_${testData.name}`;
+    await disabilityPersonPage.editVisit(0, { visitContent: updatedContent });
+
+    // 验证更新后的内容 - 确保旧文本完全被替换
+    const visitList = await disabilityPersonPage.getVisitList();
+    expect(visitList[0].visitContent).toContain('更新后的回访内容');
+    expect(visitList[0].visitContent).not.toContain('原始内容');
+    console.debug('  ✓ 回访内容已编辑');
+
+    console.debug('✅ 编辑回访记录内容测试通过');
+  });
+
+  test('应该成功编辑回访类型', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('编辑回访类型');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 编辑回访类型测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加原始回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `测试内容_${testData.name}`,
+    });
+
+    // 编辑回访类型
+    await disabilityPersonPage.editVisit(0, { visitType: '上门回访' });
+
+    // 验证更新后的类型
+    const visitList = await disabilityPersonPage.getVisitList();
+    expect(visitList[0].visitType).toBe('上门回访');
+    console.debug('  ✓ 回访类型已编辑');
+
+    console.debug('✅ 编辑回访类型测试通过');
+  });
+
+  test('应该成功编辑回访日期', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('编辑回访日期');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 编辑回访日期测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加原始回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `测试内容_${testData.name}`,
+    });
+
+    // 编辑回访日期为昨天
+    const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0];
+    await disabilityPersonPage.editVisit(0, { visitDate: yesterday });
+
+    // 验证更新后的日期
+    const visitList = await disabilityPersonPage.getVisitList();
+    expect(visitList[0].visitDate).toBe(yesterday);
+    console.debug('  ✓ 回访日期已编辑');
+
+    console.debug('✅ 编辑回访日期测试通过');
+  });
+
+  test('应该成功删除回访记录', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('删除回访');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 删除回访记录测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `待删除内容_${testData.name}`,
+    });
+
+    // 验证回访存在
+    let visitCount = await disabilityPersonPage.getVisitCount();
+    expect(visitCount).toBe(1);
+    console.debug('  ✓ 回访记录已添加');
+
+    // 删除回访
+    await disabilityPersonPage.deleteVisit(0);
+
+    // 验证回访已被删除
+    visitCount = await disabilityPersonPage.getVisitCount();
+    expect(visitCount).toBe(0);
+    console.debug('  ✓ 回访记录已删除');
+
+    console.debug('✅ 删除回访记录测试通过');
+  });
+
+  test('应该支持添加多条回访记录', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('多条回访');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 添加多条回访记录测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加多条回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `回访1_${testData.name}`,
+    });
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '上门回访',
+      visitContent: `回访2_${testData.name}`,
+    });
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '视频回访',
+      visitContent: `回访3_${testData.name}`,
+    });
+
+    // 验证所有回访都显示
+    const visitCount = await disabilityPersonPage.getVisitCount();
+    expect(visitCount).toBe(3);
+    console.debug('  ✓ 已添加 3 条回访记录');
+
+    console.debug('✅ 添加多条回访记录测试通过');
+  });
+
+  test('应该限制最多添加10条回访记录', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('回访限制');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 回访记录数量限制测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加10条回访记录
+    const today = new Date().toISOString().split('T')[0];
+    for (let i = 0; i < 10; i++) {
+      await disabilityPersonPage.addVisit({
+        visitDate: today,
+        visitType: '电话回访',
+        visitContent: `回访${i + 1}_${testData.name}`,
+      });
+    }
+
+    // 验证有10条回访
+    const visitCount = await disabilityPersonPage.getVisitCount();
+    expect(visitCount).toBe(10);
+    console.debug('  ✓ 已添加 10 条回访记录');
+
+    // 验证添加按钮被禁用
+    const isDisabled = await disabilityPersonPage.isAddVisitButtonDisabled();
+    expect(isDisabled).toBe(true);
+    console.debug('  ✓ 添加按钮已禁用(达到最大数量)');
+
+    console.debug('✅ 回访记录数量限制测试通过');
+  });
+
+  test('应该正确显示回访记录历史', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('回访历史');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 回访记录历史测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加两条回访
+    const today = new Date().toISOString().split('T')[0];
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `第一次回访_${testData.name}`,
+      visitResult: '良好',
+    });
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '上门回访',
+      visitContent: `第二次回访_${testData.name}`,
+      visitResult: '需要跟进',
+    });
+
+    // 验证回访列表
+    const visitList = await disabilityPersonPage.getVisitList();
+    expect(visitList).toHaveLength(2);
+    expect(visitList[0].visitContent).toContain('第一次回访');
+    expect(visitList[1].visitContent).toContain('第二次回访');
+    console.debug('  ✓ 回访记录历史正确显示');
+
+    console.debug('✅ 回访记录历史测试通过');
+  });
+
+  test('应该正确获取回访记录详情', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('回访详情');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 回访记录详情测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加完整回访
+    const today = new Date().toISOString().split('T')[0];
+    const testVisit = {
+      visitDate: today,
+      visitType: '微信回访',
+      visitContent: `完整测试_${testData.name}`,
+      visitResult: '已完成',
+    };
+
+    await disabilityPersonPage.addVisit(testVisit);
+
+    // 验证回访详情
+    const visitList = await disabilityPersonPage.getVisitList();
+    expect(visitList).toHaveLength(1);
+    expect(visitList[0].visitType).toBe('微信回访');
+    expect(visitList[0].visitResult).toBe('已完成');
+    console.debug('  ✓ 回访记录详情正确');
+
+    console.debug('✅ 回访记录详情测试通过');
+  });
+
+  test('应该支持设置下次回访日期', async ({ disabilityPersonPage }) => {
+    const testData = generateUniqueTestData('下次回访');
+    createdTestData.push({ name: testData.name, idCard: testData.idCard });
+
+    console.debug('\n========== 下次回访日期测试 ==========');
+
+    // 打开对话框并填写基本信息
+    await disabilityPersonPage.openCreateDialog();
+    await disabilityPersonPage.fillBasicForm(testData);
+
+    // 滚动到回访管理区域
+    await disabilityPersonPage.scrollToSection('回访记录管理');
+
+    // 添加带下次回访日期的回访
+    const today = new Date().toISOString().split('T')[0];
+    const nextWeek = new Date(Date.now() + 7 * 86400000).toISOString().split('T')[0];
+
+    await disabilityPersonPage.addVisit({
+      visitDate: today,
+      visitType: '电话回访',
+      visitContent: `测试内容_${testData.name}`,
+      nextVisitDate: nextWeek,
+    });
+
+    // 验证下次回访日期
+    const visitList = await disabilityPersonPage.getVisitList();
+    expect(visitList[0].nextVisitDate).toBe(nextWeek);
+    console.debug('  ✓ 下次回访日期已设置');
+
+    console.debug('✅ 下次回访日期测试通过');
+  });
+});