QUOTATION-DUPLICATE-REMOVAL-FIX.md 7.3 KB

报价空间重复问题修复方案

问题描述

在报价页面中发现存在重复的空间报价条目,例如:

  • 儿童房出现2次
  • 卫生间出现2次
  • 厨房出现2次
  • 客厅出现2次
  • 次卧出现2次
  • 阳台出现2次
  • 餐厅出现2次

这导致页面显示混乱,报价计算不准确。

问题原因

  1. 数据库层面:在Product表中可能存在同名空间的重复记录
  2. 前端展示:虽然之前的代码已经有去重逻辑,但未能检测和提醒用户清理数据库中的重复数据

解决方案

1. 增强报价生成的去重逻辑

位置: quotation-editor.component.ts - generateQuotationFromProducts() 方法

改进内容:

  • 使用 Map 数据结构确保同名空间只保留第一个
  • 记录所有重复的产品ID
  • 如果检测到重复,提示用户是否自动清理
  • 在控制台输出详细的去重日志

    // 使用 Map 去重,key 为空间名称,value 为空间数据
    const spaceMap = new Map<string, any>();
    const duplicateProductIds: string[] = [];
    
    for (const product of this.products) {
    const productName = product.get('productName');
      
    // 如果该空间名称已存在,记录重复的产品ID
    if (spaceMap.has(productName)) {
    console.log(`⚠️ 检测到重复空间: ${productName} (产品ID: ${product.id})`);
    duplicateProductIds.push(product.id);
    continue;
    }
    // ... 处理唯一空间
    }
    
    // 如果检测到重复产品,提示用户清理
    if (duplicateProductIds.length > 0) {
    console.warn(`⚠️ 检测到 ${duplicateProductIds.length} 个重复产品,建议清理`);
    if (this.canEdit && await window?.fmode?.confirm(`检测到 ${duplicateProductIds.length} 个重复空间产品,是否自动清理?`)) {
    await this.removeDuplicateProducts(duplicateProductIds);
    return; // 清理后重新生成报价
    }
    }
    

2. 新增批量删除重复产品方法

方法: removeDuplicateProducts(productIds: string[])

功能:

  • 批量删除指定的重复产品记录
  • 删除后自动重新加载产品列表
  • 重新生成报价确保数据一致性
  • 提供详细的操作日志

    async removeDuplicateProducts(productIds: string[]): Promise<void> {
    console.log(`🗑️ 开始清理 ${productIds.length} 个重复产品...`);
      
    // 批量删除重复产品
    for (const productId of productIds) {
    const product = this.products.find(p => p.id === productId);
    if (product) {
      await product.destroy();
      console.log(`  ✓ 已删除: ${product.get('productName')} (${productId})`);
    }
    }
    
    // 重新加载产品列表并生成报价
    await this.loadProjectProducts();
    await this.generateQuotationFromProducts();
      
    console.log('✅ 重复产品清理完成');
    }
    

3. 新增手动清理工具方法

方法: cleanupDuplicateProducts()

功能:

  • 扫描所有产品,检测重复的空间名称
  • 显示详细的重复信息(空间名称和数量)
  • 保留第一个产品,删除后续重复的
  • 用户确认后执行清理操作

    async cleanupDuplicateProducts(): Promise<void> {
    // 使用 Map 检测重复
    const productNameMap = new Map<string, any[]>();
      
    for (const product of this.products) {
    const productName = product.get('productName');
    if (!productNameMap.has(productName)) {
      productNameMap.set(productName, []);
    }
    productNameMap.get(productName)!.push(product);
    }
    
    // 找出所有重复的产品
    const duplicateProductIds: string[] = [];
    const duplicateNames: string[] = [];
      
    for (const [name, products] of productNameMap.entries()) {
    if (products.length > 1) {
      duplicateNames.push(name);
      // 保留第一个,删除其余的
      for (let i = 1; i < products.length; i++) {
        duplicateProductIds.push(products[i].id);
      }
    }
    }
    
    if (duplicateProductIds.length === 0) {
    window?.fmode?.alert('没有检测到重复产品');
    return;
    }
    
    const message = `检测到以下空间存在重复:\n${duplicateNames.join('、')}\n\n共 ${duplicateProductIds.length} 个重复产品,是否清理?`;
      
    if (await window?.fmode?.confirm(message)) {
    await this.removeDuplicateProducts(duplicateProductIds);
    }
    }
    

4. UI 界面改进

位置: quotation-editor.component.html

在产品管理工具栏添加"清理重复"按钮:

<button class="btn-outline" (click)="cleanupDuplicateProducts()" title="清理重复产品">
  <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
    <path fill="currentColor" d="..."/>
  </svg>
  清理重复
</button>

使用方法

方式一:生成报价时自动检测(推荐)

  1. 打开项目的订单页面
  2. 点击"生成报价"按钮
  3. 如果检测到重复空间,系统会弹出确认对话框
  4. 点击"确定"自动清理重复产品
  5. 清理完成后自动重新生成报价

方式二:手动清理重复

  1. 打开项目的订单页面
  2. 点击工具栏的"清理重复"按钮
  3. 系统显示重复空间的详细信息
  4. 确认后自动清理并重新生成报价

技术细节

去重策略

  • 保留原则: 保留第一个创建的产品,删除后续重复的
  • 识别标准: 使用 productName 字段作为唯一标识
  • 数据安全: 删除操作前会弹出确认对话框

数据流程

加载产品列表 
  → 检测重复 
  → 提示用户 
  → 删除重复记录 
  → 重新加载 
  → 生成唯一报价

日志输出

系统会在控制台输出详细日志,方便调试:

  • ⚠️ 检测到重复空间: 发现重复时
  • 🗑️ 开始清理: 开始删除操作
  • ✓ 已删除: 每个产品删除成功
  • ✅ 报价空间生成完成: 显示最终的唯一空间数量

预期效果

修复前

  • 报价列表显示16个空间(包含8个重复)
  • 多个空间名称相同
  • 报价总额可能重复计算

修复后

  • 报价列表显示8个唯一空间
  • 每个空间类型只显示一次
  • 报价总额准确反映实际空间

样式保留

本次修复完全保留了原有的UI样式:

  • ✅ 蓝色产品卡片标识
  • ✅ 价格显示样式
  • ✅ 状态徽章(未开始、进行中等)
  • ✅ 百分比显示
  • ✅ 展开/折叠功能
  • ✅ 编辑和删除按钮

测试建议

  1. 功能测试:

    • 创建多个同名空间产品
    • 点击"生成报价"验证自动检测
    • 点击"清理重复"手动清理
  2. 边界测试:

    • 无重复产品时的提示
    • 全部产品都重复的情况
    • 删除操作取消的情况
  3. 数据验证:

    • 确认只删除重复的产品
    • 确认保留第一个产品
    • 确认报价总额正确

注意事项

  1. 权限控制: 只有具有编辑权限(canEdit = true)的用户才能看到清理按钮
  2. 数据备份: 删除操作不可逆,建议在测试环境先验证
  3. 并发问题: 如果多人同时编辑,可能需要刷新页面
  4. 性能考虑: 批量删除时会逐个调用API,大量重复时可能需要一定时间

相关文件

  • src/modules/project/components/quotation-editor.component.ts - 核心逻辑
  • src/modules/project/components/quotation-editor.component.html - UI界面
  • src/modules/project/components/quotation-editor.component.scss - 样式(无改动)

版本信息

  • 修复日期: 2025-10-31
  • 影响范围: 报价编辑器组件
  • 向下兼容: 是
  • 破坏性变更: 否