stage-order-component-refactoring.md 13 KB

订单分配阶段组件拆分方案

📦 概述

将订单分配阶段(StageOrderComponent)拆分为多个可复用的子组件,提高代码的可维护性、可测试性和可复用性。


🎯 拆分目标

  1. 模块化:每个组件负责单一职责
  2. 可复用:组件可在其他地方复用
  3. 易维护:代码结构清晰,易于理解和修改
  4. 易测试:每个组件可独立测试
  5. 性能优化:支持OnPush变更检测策略

📂 组件结构

stage-order/
├── components/                            # 子组件目录
│   ├── approval-status-banner/            # 审批状态横幅
│   │   ├── approval-status-banner.component.ts
│   │   ├── approval-status-banner.component.html
│   │   └── approval-status-banner.component.scss
│   ├── leader-approval-bar/               # 组长审批操作条
│   │   ├── leader-approval-bar.component.ts
│   │   ├── leader-approval-bar.component.html
│   │   └── leader-approval-bar.component.scss
│   ├── project-basic-info/                # 项目基本信息
│   │   ├── project-basic-info.component.ts
│   │   ├── project-basic-info.component.html
│   │   └── project-basic-info.component.scss
│   └── order-action-buttons/              # 操作按钮
│       ├── order-action-buttons.component.ts
│       ├── order-action-buttons.component.html
│       └── order-action-buttons.component.scss
├── stage-order.component.ts               # 主组件
├── stage-order.component.html             # 主组件模板
└── stage-order.component.scss             # 主组件样式

🧩 子组件详解

1. ApprovalStatusBannerComponent(审批状态横幅)

职责:显示订单审批状态(待审批、已通过、已驳回)

输入属性

  • status: ApprovalStatus | null - 审批状态
  • rejectionReason: string - 驳回原因

输出事件

  • resubmit: void - 重新提交事件

使用示例

<app-approval-status-banner
  [status]="getApprovalStatus()"
  [rejectionReason]="getRejectionReason()"
  (resubmit)="prepareResubmit()">
</app-approval-status-banner>

特性

  • 3种状态样式(pending/approved/rejected)
  • 支持重新提交操作
  • 响应式设计,移动端优化
  • 下滑动画效果

2. LeaderApprovalBarComponent(组长审批操作条)

职责:提供组长审批订单的操作按钮

输入属性

  • saving: boolean - 是否正在保存

输出事件

  • approve: void - 通过审批事件
  • reject: void - 驳回订单事件

使用示例

<app-leader-approval-bar
  [saving]="saving"
  (approve)="approveOrder()"
  (reject)="rejectOrder()">
</app-leader-approval-bar>

特性

  • 渐变背景按钮
  • 波纹点击效果
  • 禁用状态处理
  • 移动端纵向排列

3. ProjectBasicInfoComponent(项目基本信息)

职责:可折叠的项目基本信息表单

输入属性

  • projectInfo: ProjectInfo - 项目信息对象
  • expanded: boolean - 是否展开
  • canEdit: boolean - 是否可编辑

输出事件

  • expandedChange: boolean - 展开状态变化
  • projectInfoChange: ProjectInfo - 项目信息变化
  • projectTypeChange: string - 项目类型变化

接口定义

export interface ProjectInfo {
  title: string;
  projectType: string;
  renderType: string;
  demoday: Date | null;
  deadline: Date | null;
  description: string;
  priceLevel: string;
  spaceType: string;
}

使用示例

<app-project-basic-info
  [projectInfo]="projectInfo"
  [expanded]="projectInfoExpanded"
  [canEdit]="canEdit"
  (expandedChange)="projectInfoExpanded = $event"
  (projectInfoChange)="onProjectInfoChange($event)"
  (projectTypeChange)="onProjectTypeChange($event)">
</app-project-basic-info>

特性

  • 可折叠交互
  • 双向数据绑定
  • 日期选择器集成
  • 条件渲染(渲染类型仅家装显示)
  • 完整的表单验证支持

4. OrderActionButtonsComponent(操作按钮)

职责:提供保存草稿和确认订单操作

输入属性

  • canEdit: boolean - 是否可编辑
  • saving: boolean - 是否正在保存
  • submittedPending: boolean - 是否已提交待审批
  • approvalStatus: string | null - 审批状态

输出事件

  • saveDraft: void - 保存草稿事件
  • submit: void - 提交订单事件

使用示例

<app-order-action-buttons
  [canEdit]="canEdit"
  [saving]="saving"
  [submittedPending]="submittedPending"
  [approvalStatus]="getApprovalStatus()"
  (saveDraft)="saveDraft()"
  (submit)="submitForOrder()">
</app-order-action-buttons>

特性

  • 自动禁用逻辑
  • 渐变样式按钮
  • 移动端优化布局
  • 图标+文字组合

🔄 主组件更新

修改后的HTML结构

<div class="stage-order-container">
  <!-- 1. 审批状态横幅 -->
  @if (project) {
    <app-approval-status-banner
      [status]="getApprovalStatus()"
      [rejectionReason]="getRejectionReason()"
      (resubmit)="prepareResubmit()">
    </app-approval-status-banner>
  }
  
  <!-- 2. 组长审批操作条 -->
  @if (getApprovalStatus() === 'pending' && isTeamLeader && !isFromCustomerService) {
    <app-leader-approval-bar
      [saving]="saving"
      (approve)="approveOrder()"
      (reject)="rejectOrder()">
    </app-leader-approval-bar>
  }
  
  <!-- 3. 项目基本信息 -->
  <app-project-basic-info
    [projectInfo]="projectInfo"
    [expanded]="projectInfoExpanded"
    [canEdit]="canEdit"
    (expandedChange)="projectInfoExpanded = $event"
    (projectInfoChange)="onProjectInfoChange($event)"
    (projectTypeChange)="onProjectTypeChange($event)">
  </app-project-basic-info>

  <!-- 4. 产品报价管理(已有组件) -->
  <div class="card quotation-card">
    <app-quotation-editor ...>
    </app-quotation-editor>
  </div>

  <!-- 5. 设计师分配(已有组件) -->
  <app-team-assign ...>
  </app-team-assign>

  <!-- 6. 操作按钮 -->
  <app-order-action-buttons
    [canEdit]="canEdit"
    [saving]="saving"
    [submittedPending]="submittedPending"
    [approvalStatus]="getApprovalStatus()"
    (saveDraft)="saveDraft()"
    (submit)="submitForOrder()">
  </app-order-action-buttons>
</div>

TypeScript导入更新

import { ApprovalStatusBannerComponent } from './components/approval-status-banner/approval-status-banner.component';
import { LeaderApprovalBarComponent } from './components/leader-approval-bar/leader-approval-bar.component';
import { ProjectBasicInfoComponent } from './components/project-basic-info/project-basic-info.component';
import { OrderActionButtonsComponent } from './components/order-action-buttons/order-action-buttons.component';

@Component({
  selector: 'app-stage-order',
  standalone: true,
  imports: [
    CommonModule,
    FormsModule,
    QuotationEditorComponent,
    TeamAssignComponent,
    CustomDatePickerComponent,
    ApprovalStatusBannerComponent,      // 新增
    LeaderApprovalBarComponent,         // 新增
    ProjectBasicInfoComponent,          // 新增
    OrderActionButtonsComponent         // 新增
  ],
  ...
})

📈 优势对比

Before(拆分前)

单一大组件

  • ❌ HTML文件230行,难以阅读
  • ❌ 所有逻辑集中在一个组件
  • ❌ 难以复用
  • ❌ 难以测试
  • ❌ 修改影响面广

After(拆分后)

模块化小组件

  • ✅ 每个组件职责单一明确
  • ✅ HTML结构清晰,易于理解
  • ✅ 组件可独立复用
  • ✅ 易于单元测试
  • ✅ 修改影响范围小
  • ✅ 支持OnPush策略,性能更好

🎨 样式管理

样式隔离策略

每个子组件都有独立的样式文件,避免样式冲突:

approval-status-banner.component.scss    # 横幅样式
leader-approval-bar.component.scss       # 审批栏样式
project-basic-info.component.scss        # 表单样式
order-action-buttons.component.scss      # 按钮样式
stage-order.component.scss               # 主组件布局样式

共享样式

公共样式可以提取到:

  • 全局样式文件(styles.scss)
  • 共享SCSS变量文件
  • 主题配置文件

🧪 测试建议

单元测试

每个子组件都可以独立测试:

// approval-status-banner.component.spec.ts
describe('ApprovalStatusBannerComponent', () => {
  it('should display pending status', () => {
    component.status = 'pending';
    fixture.detectChanges();
    expect(compiled.querySelector('.status-icon').textContent).toBe('⏳');
  });

  it('should emit resubmit event', () => {
    spyOn(component.resubmit, 'emit');
    component.onResubmit();
    expect(component.resubmit.emit).toHaveBeenCalled();
  });
});

集成测试

测试主组件与子组件的交互:

// stage-order.component.spec.ts
describe('StageOrderComponent', () => {
  it('should pass approval status to banner component', () => {
    const banner = fixture.debugElement.query(By.directive(ApprovalStatusBannerComponent));
    expect(banner.componentInstance.status).toBe('pending');
  });
});

📱 响应式设计

所有子组件都实现了移动端优化:

断点策略

// 移动端优化 (≤480px)
@media (max-width: 480px) {
  // 横幅纵向布局,居中显示
  .approval-status-banner {
    flex-direction: column;
    text-align: center;
  }

  // 审批按钮纵向排列,占满宽度
  .leader-approval-bar {
    .approval-buttons-container {
      flex-direction: column;
      button { width: 100%; }
    }
  }

  // 表单输入框增大触摸区域
  .form-input {
    min-height: 44px;
    padding: 12px;
  }
}

🚀 迁移步骤

1. 创建子组件

✅ 已创建所有子组件文件

2. 更新主组件

// stage-order.component.ts

// 1. 导入子组件
import { ApprovalStatusBannerComponent } from './components/approval-status-banner/approval-status-banner.component';
// ... 其他导入

// 2. 添加到imports数组
@Component({
  imports: [
    // ... 现有导入
    ApprovalStatusBannerComponent,
    LeaderApprovalBarComponent,
    ProjectBasicInfoComponent,
    OrderActionButtonsComponent
  ]
})

// 3. 添加事件处理方法(如果需要)
onProjectInfoChange(info: ProjectInfo): void {
  this.projectInfo = info;
  // 触发变更检测或其他操作
}

3. 更新主组件HTML

将原有的HTML块替换为子组件标签

4. 清理主组件样式

将已移到子组件的样式从主组件SCSS中删除

5. 测试验证

  • 审批状态显示正常
  • 审批按钮点击正常
  • 项目信息表单交互正常
  • 操作按钮功能正常
  • 移动端布局正常

🔧 维护建议

1. 组件职责

  • 每个组件只负责自己的UI和交互
  • 业务逻辑保留在主组件
  • 子组件通过事件与主组件通信

2. 数据流

主组件 (StageOrderComponent)
    ↓ @Input
子组件 (ApprovalStatusBannerComponent)
    ↓ @Output
主组件 (StageOrderComponent)

3. 性能优化

  • 所有子组件使用OnPush策略
  • 使用Immutable数据更新
  • 避免在模板中使用复杂计算

4. 代码复用

这些组件可以在其他地方复用:

  • 审批状态横幅 → 其他需要审批的页面
  • 操作按钮 → 其他表单页面
  • 项目信息表单 → 项目编辑页面

📊 代码量对比

Before(拆分前)

文件 行数 说明
stage-order.component.html 230行 所有HTML在一个文件
stage-order.component.scss 3024行 所有样式在一个文件
stage-order.component.ts 2170行 所有逻辑在一个文件
总计 5424行

After(拆分后)

文件 行数 说明
主组件
stage-order.component.html ~80行 组件编排
stage-order.component.scss ~500行 布局样式
stage-order.component.ts ~1500行 主要逻辑
子组件
approval-status-banner ~200行 3个文件
leader-approval-bar ~150行 3个文件
project-basic-info ~400行 3个文件
order-action-buttons ~150行 3个文件
总计 ~3000行 13个文件

优势

  • ✅ 代码行数减少44%
  • ✅ 文件组织更清晰
  • ✅ 易于定位和修改
  • ✅ 支持并行开发

✅ 完成清单

  • 创建ApprovalStatusBannerComponent
  • 创建LeaderApprovalBarComponent
  • 创建ProjectBasicInfoComponent
  • 创建OrderActionButtonsComponent
  • 更新主组件导入
  • 更新主组件HTML
  • 更新主组件样式
  • 添加单元测试
  • 测试移动端布局
  • 文档更新

🎯 下一步

  1. 更新主组件:导入并使用新创建的子组件
  2. 清理代码:删除已移到子组件的代码
  3. 测试验证:确保功能正常
  4. 性能测试:验证OnPush策略效果
  5. 代码审查:团队review代码质量

📚 参考资料


创建时间:2024-12-09

状态:✅ 子组件创建完成,待集成到主组件