# 映三色报价系统PRD - 报价自动分配与智能计算

## 一、功能概述

### 1.1 功能定位
映三色视觉设计表现公司的智能报价系统，支持三级报价体系（一级老客户、二级中端组、三级高端组），实现以空间为单位的独立设计产品报价管理，自动计算内部执行分配。

### 1.2 核心价值
- **空间独立报价**: 每个空间都是独立的设计产品服务，有独立的报价和分配
- **智能预设场景**: 根据项目类型和场景提供预设选项，提高报价效率
- **自动分配计算**: 基于报价总额自动计算内部执行三阶段分配（10%-40%-50%）
- **精准价格控制**: 所有价格均为整数，无小数点，便于财务核算
- **规则驱动**: 严格遵循docs/data/quotation.md中的定价标准

---

## 二、报价规则体系

### 2.1 三级报价体系

| 报价等级 | 适用对象 | 价格水平 | 服务定位 |
|---------|---------|---------|---------|
| **一级** | 老客户 | 最低 | 标准服务，维护老客户关系 |
| **二级** | 中端客户 | 中等 | 中端市场，平衡性价比 |
| **三级** | 高端客户 | 最高 | 高端市场，优质服务 |

### 2.2 家装区报价规则

#### 2.2.1 风格等级分类

| 风格等级 | 风格类型 |
|---------|---------|
| **基础风格组** | 现代、北欧、简约、后现代、工业风 |
| **中级风格组** | 轻奢、日式、新中式 |
| **高级风格组** | 简欧、古典中式、欧式、美式、简美、轻法 |
| **顶级风格组** | 古典欧式、古典美式、纯法式 |

#### 2.2.2 空间类型与基础定价（一级报价-静态单张示例）

| 空间类型 | 基础风格组 | 中级风格组 | 高级风格组 | 顶级风格组 |
|---------|-----------|-----------|-----------|-----------|
| **平层客餐厅** | 300元 | 350元 | 400元 | 500元 |
| **跃层客餐厅** | 400元 | 450元 | 500元 | 600元 |
| **挑空客餐厅** | 600元 | 700元 | 800元 | 900元 |
| **卧室** | 300元 | 300元 | 300元 | 300元 |

#### 2.2.3 家装浮动加价规则

1. **功能区加价**
   - 客餐厅空间只出一张：+100元
   - 每增加一个功能区：+100元
   - 卧室每增加一个功能区：+100元

2. **造型复杂度加价**
   - 立面和吊顶根据参考图制作，考虑立面造型复杂：+100至200元

3. **设计服务加价**
   - 需要我们设计（非提供参考图）：原价×2

4. **卧室特殊规则**
   - 卧室每增加一个功能区：+100元
   - 根据参考图建模：+100元

### 2.3 工装区报价规则

#### 2.3.1 场景分类与定价（一级报价-静态单张）

| 空间类型 | 门厅空间 | 封闭空间 |
|---------|---------|---------|
| 办公空间 | 600元 | 400元 |
| 商业空间 | 600元 | 500元 |
| 娱乐空间 | 600元 | 500元 |
| 酒店餐厅 | 600元 | 500元 |
| 公共空间 | 600元 | 600元 |

#### 2.3.2 工装浮动加价规则

1. **功能区加价**: 每增加一个功能区：+400元
2. **造型复杂度加价**: 立面和吊顶根据参考图制作，考虑立面造型复杂：+100至200元
3. **设计服务加价**: 需要我们设计：原价×2
4. **全景输出**: 需要全景渲染：原价×2

### 2.4 建筑类报价规则

#### 2.4.1 场景分类与定价（一级报价）

| 场景类型 | 价格 |
|---------|------|
| 门头 | 700元 |
| 小型单体 | 1000元 |
| 大型单体 | 1500元 |
| 鸟瞰 | 2000元 |

---

## 三、核心功能设计

### 3.1 空间独立设计产品管理

**设计原则**: 每个空间都是一个独立的设计产品服务，有独立的：
- 产品名称（空间名称）
- 产品类型（客厅、卧室、厨房等）
- 报价配置（基础价格 + 加价项）
- 工序明细（建模、软装、渲染、后期）
- 内部分配（建模阶段、软装渲染、公司分配）

### 3.2 智能添加产品功能

#### 3.2.1 添加产品流程

1. 点击"添加产品"按钮
2. 弹出产品配置对话框/模态框
3. 选择配置项：
   - **项目类型**: 家装 / 工装 / 建筑类
   - **渲染类型**: 静态单张 / 360全景（家装/工装）
   - **空间场景**: 根据项目类型显示预设场景
   - **风格等级**: （家装专属）基础/中级/高级/顶级风格组
   - **业态类型**: （工装专属）办公/商业/娱乐/酒店餐厅/公共
   - **空间类型**: （家装）平层/跃层/挑空/卧室，（工装）门厅/封闭
   - **自定义名称**: 可修改默认名称
4. 系统自动计算基础价格并显示
5. 确认后创建产品

#### 3.2.2 预设场景列表

**家装预设场景**:
- 客厅、餐厅、主卧、次卧、儿童房、书房、厨房、卫生间、阳台

**工装预设场景**:
- 大堂、接待区、会议室、办公区、休息区、展示区、洽谈区

**建筑类预设场景**:
- 门头、小型单体、大型单体、鸟瞰

**场景选择逻辑**:
- 如果用户选择的场景在预设列表中，直接使用预设名称和报价规则
- 如果用户选择"自定义"，则需要手动填写名称，系统匹配最接近的报价规则

### 3.3 基础价格计算（calculateBasePrice）

#### 3.3.1 计算逻辑

```typescript
function calculateBasePrice(config: {
  priceLevel: '一级' | '二级' | '三级';
  projectType: '家装' | '工装' | '建筑类';
  renderType: '静态单张' | '360全景';
  spaceType: string; // 平层/跃层/挑空/卧室 或 门厅空间/封闭空间
  styleLevel?: string; // 家装：基础风格组/中级风格组等
  businessType?: string; // 工装：办公空间/商业空间等
  architectureType?: string; // 建筑类：门头/小型单体等
}): number {
  // 根据报价规则表精确查找价格
  // 每个空间都是独立的设计产品服务，有独立的基础价格
}
```

#### 3.3.2 示例计算

**示例1: 家装平层客厅（一级-现代风格-静态单张）**
```
基础价格 = 300元（基础风格组-平层）
```

**示例2: 家装跃层客厅（三级-古典欧式-360全景）**
```
基础价格 = 1700元（顶级风格组-跃层-360全景）
```

**示例3: 工装办公门厅（二级-静态单张）**
```
基础价格 = 700元（二级-办公空间-门厅空间-静态单张）
```

### 3.4 内部执行分配规则

#### 3.4.1 分配比例

基于报价总价，自动按固定比例拆分为内部执行三个阶段：

| 分配类型 | 占比 | 说明 | 用途 |
|---------|------|------|------|
| **建模阶段** | 10% | 3D模型构建 | 支付给建模设计师的费用 |
| **软装渲染** | 40% | 软装搭配+效果图渲染 | 支付给软装和渲染设计师的费用 |
| **公司分配** | 50% | 公司运营与利润 | 公司管理成本、平台服务费、利润等 |

#### 3.4.2 分配计算公式

```typescript
建模阶段金额 = Math.round(报价总价 × 10%)
软装渲染金额 = Math.round(报价总价 × 40%)
公司分配金额 = Math.round(报价总价 × 50%)
```

**注意**: 所有金额使用 `Math.round()` 四舍五入到整数，确保无小数。

#### 3.4.3 分配示例

**示例1: 厨房空间**
```
报价总价: ¥900
建模阶段: ¥900 × 10% = ¥90
软装渲染: ¥900 × 40% = ¥360
公司分配: ¥900 × 50% = ¥450
```

**示例2: 客厅空间**
```
报价总价: ¥1,500
建模阶段: ¥1,500 × 10% = ¥150
软装渲染: ¥1,500 × 40% = ¥600
公司分配: ¥1,500 × 50% = ¥750
```

---

## 四、数据结构设计

### 4.1 产品（Product）数据结构

```typescript
interface Product {
  id: string;
  project: Pointer<Project>;
  productName: string; // 产品名称（空间名称）
  productType: string; // 产品类型（living_room, bedroom等）

  // 空间信息
  space: {
    spaceName: string;
    area: number;
    dimensions: { length: number; width: number; height: number };
    spaceType: string; // 平层/跃层/挑空等
    styleLevel?: string; // 风格等级（家装）
    businessType?: string; // 业态类型（工装）
    architectureType?: string; // 建筑类型
    complexity: 'low' | 'medium' | 'high';
  };

  // 报价信息
  quotation: {
    priceLevel: '一级' | '二级' | '三级';
    basePrice: number; // 基础价格（整数）
    adjustments: {
      extraFunction?: number; // 额外功能区数量
      complexity?: number; // 造型复杂度加价
      design?: boolean; // 是否需要设计服务
      panoramic?: boolean; // 是否全景渲染
    };
    finalPrice: number; // 最终价格（整数）

    // 工序明细
    processes: {
      modeling: { enabled: boolean; price: number; quantity: number; };
      softDecor: { enabled: boolean; price: number; quantity: number; };
      rendering: { enabled: boolean; price: number; quantity: number; };
      postProcess: { enabled: boolean; price: number; quantity: number; };
    };

    // 内部分配（整数）
    allocation: {
      modeling: number;
      decoration: number;
      company: number;
    };

    status: 'draft' | 'confirmed' | 'modified';
    validUntil: Date;
  };

  // 需求信息
  requirements: {
    colorRequirement: any;
    materialRequirement: any;
    lightingRequirement: any;
    specificRequirements: string[];
  };

  status: 'not_started' | 'in_progress' | 'completed';
  profile?: Pointer<Profile>; // 负责的设计师
}
```

### 4.2 项目（Project）报价汇总结构

```typescript
interface ProjectQuotation {
  spaces: Array<{
    name: string;
    productId: string;
    processes: ProcessConfig;
    subtotal: number; // 该空间小计（整数）
  }>;

  total: number; // 项目总价（整数）

  // 产品占比明细
  spaceBreakdown: Array<{
    spaceName: string;
    spaceId: string;
    amount: number; // 整数
    percentage: number; // 百分比
  }>;

  // 内部执行分配（整数）
  allocation: {
    modeling: {
      amount: number;
      percentage: 10;
      description: '3D模型构建';
    };
    decoration: {
      amount: number;
      percentage: 40;
      description: '软装搭配+效果图渲染';
    };
    company: {
      amount: number;
      percentage: 50;
      description: '公司运营与利润';
    };
    updatedAt: Date;
  };

  generatedAt: Date;
  validUntil: Date;
}
```

---

## 五、UI/UX设计

### 5.1 添加产品组件

#### 5.1.1 组件结构

```html
<!-- 添加产品模态框 -->
<div class="add-product-modal">
  <div class="modal-header">
    <h3>添加设计产品</h3>
    <button class="close-btn" (click)="closeModal()">×</button>
  </div>

  <div class="modal-body">
    <!-- 项目类型选择 -->
    <div class="form-group">
      <label>项目类型</label>
      <div class="radio-group">
        <label><input type="radio" name="projectType" value="家装"> 家装</label>
        <label><input type="radio" name="projectType" value="工装"> 工装</label>
        <label><input type="radio" name="projectType" value="建筑类"> 建筑类</label>
      </div>
    </div>

    <!-- 渲染类型选择（家装/工装） -->
    @if (projectType === '家装' || projectType === '工装') {
      <div class="form-group">
        <label>渲染类型</label>
        <div class="radio-group">
          <label><input type="radio" name="renderType" value="静态单张"> 静态单张</label>
          <label><input type="radio" name="renderType" value="360全景"> 360全景</label>
        </div>
      </div>
    }

    <!-- 预设场景选择 -->
    <div class="form-group">
      <label>选择场景</label>
      <div class="scene-grid">
        @for (scene of getPresetScenes(); track scene) {
          <button
            class="scene-card"
            [class.selected]="selectedScene === scene"
            (click)="selectScene(scene)">
            <i class="scene-icon icon-{{ scene.icon }}"></i>
            <span>{{ scene.name }}</span>
          </button>
        }
        <button
          class="scene-card custom"
          [class.selected]="selectedScene === 'custom'"
          (click)="selectCustomScene()">
          <i class="scene-icon icon-add"></i>
          <span>自定义</span>
        </button>
      </div>
    </div>

    <!-- 风格选择（家装专属） -->
    @if (projectType === '家装') {
      <div class="form-group">
        <label>风格等级</label>
        <select [(ngModel)]="styleLevel">
          <option value="基础风格组">基础风格组</option>
          <option value="中级风格组">中级风格组</option>
          <option value="高级风格组">高级风格组</option>
          <option value="顶级风格组">顶级风格组</option>
        </select>
      </div>

      <div class="form-group">
        <label>空间类型</label>
        <select [(ngModel)]="spaceType">
          <option value="平层">平层</option>
          <option value="跃层">跃层</option>
          <option value="挑空">挑空</option>
          <option value="卧室">卧室</option>
        </select>
      </div>
    }

    <!-- 业态类型（工装专属） -->
    @if (projectType === '工装') {
      <div class="form-group">
        <label>业态类型</label>
        <select [(ngModel)]="businessType">
          <option value="办公空间">办公空间</option>
          <option value="商业空间">商业空间</option>
          <option value="娱乐空间">娱乐空间</option>
          <option value="酒店餐厅">酒店餐厅</option>
          <option value="公共空间">公共空间</option>
        </select>
      </div>

      <div class="form-group">
        <label>空间类型</label>
        <select [(ngModel)]="spaceType">
          <option value="门厅空间">门厅空间</option>
          <option value="封闭空间">封闭空间</option>
        </select>
      </div>
    }

    <!-- 建筑类型（建筑类专属） -->
    @if (projectType === '建筑类') {
      <div class="form-group">
        <label>建筑类型</label>
        <select [(ngModel)]="architectureType">
          <option value="门头">门头</option>
          <option value="小型单体">小型单体</option>
          <option value="大型单体">大型单体</option>
          <option value="鸟瞰">鸟瞰</option>
        </select>
      </div>
    }

    <!-- 自定义名称 -->
    <div class="form-group">
      <label>产品名称</label>
      <input
        type="text"
        [(ngModel)]="productName"
        placeholder="例如：客厅、主卧、大堂等"
        class="form-input">
    </div>

    <!-- 价格预览 -->
    <div class="price-preview">
      <span class="label">基础价格:</span>
      <span class="price">¥{{ calculatePreviewPrice() }}</span>
    </div>
  </div>

  <div class="modal-footer">
    <button class="btn-secondary" (click)="closeModal()">取消</button>
    <button class="btn-primary" (click)="confirmAddProduct()">确认添加</button>
  </div>
</div>
```

### 5.2 报价编辑器主界面

#### 5.2.1 产品卡片展示

```html
<!-- 产品卡片 -->
<div class="product-card">
  <!-- 卡片头部 -->
  <div class="product-header" (click)="toggleExpand(product)">
    <div class="product-info">
      <div class="product-icon-box">
        <i class="icon-{{ product.productType }}"></i>
      </div>
      <div class="product-details">
        <h4>{{ product.productName }}</h4>
        <div class="product-tags">
          <span class="tag">{{ product.space.spaceType }}</span>
          @if (product.space.styleLevel) {
            <span class="tag">{{ product.space.styleLevel }}</span>
          }
          <span class="tag status-{{ product.status }}">{{ getStatusText(product.status) }}</span>
        </div>
      </div>
    </div>

    <div class="product-pricing">
      <div class="price-main">¥{{ getProductFinalPrice(product) }}</div>
      <div class="price-base">基础 ¥{{ product.quotation.basePrice }}</div>
    </div>

    <div class="expand-icon">
      <i class="icon-chevron-down"></i>
    </div>
  </div>

  <!-- 卡片内容（展开时显示） -->
  @if (isExpanded(product)) {
    <div class="product-content">
      <!-- 空间信息 -->
      <div class="info-section">
        <h5>空间信息</h5>
        <div class="info-grid">
          <div class="info-item">
            <span class="label">面积</span>
            <span class="value">{{ product.space.area || 0 }}㎡</span>
          </div>
          <div class="info-item">
            <span class="label">复杂度</span>
            <span class="value">{{ product.space.complexity }}</span>
          </div>
        </div>
      </div>

      <!-- 工序明细 -->
      <div class="process-section">
        <h5>工序明细</h5>
        <div class="process-grid">
          @for (processType of processTypes; track processType.key) {
            <div class="process-card" [class.enabled]="isProcessEnabled(product, processType.key)">
              <div class="process-header">
                <label class="checkbox">
                  <input
                    type="checkbox"
                    [checked]="isProcessEnabled(product, processType.key)"
                    (change)="toggleProcess(product, processType.key)">
                  <span>{{ processType.name }}</span>
                </label>
              </div>

              @if (isProcessEnabled(product, processType.key)) {
                <div class="process-inputs">
                  <div class="input-group">
                    <label>单价</label>
                    <input
                      type="number"
                      [(ngModel)]="product.quotation.processes[processType.key].price"
                      (ngModelChange)="onProcessChange(product)"
                      class="input-field">
                  </div>
                  <div class="input-group">
                    <label>数量</label>
                    <input
                      type="number"
                      [(ngModel)]="product.quotation.processes[processType.key].quantity"
                      (ngModelChange)="onProcessChange(product)"
                      class="input-field">
                  </div>
                  <div class="process-subtotal">
                    小计: ¥{{ calculateProcessSubtotal(product, processType.key) }}
                  </div>
                </div>
              }
            </div>
          }
        </div>
      </div>

      <!-- 内部分配明细（该产品） -->
      <div class="allocation-mini">
        <h5>内部分配</h5>
        <div class="allocation-row">
          <span class="label">建模阶段 (10%)</span>
          <span class="value">¥{{ product.quotation.allocation.modeling }}</span>
        </div>
        <div class="allocation-row">
          <span class="label">软装渲染 (40%)</span>
          <span class="value">¥{{ product.quotation.allocation.decoration }}</span>
        </div>
        <div class="allocation-row">
          <span class="label">公司分配 (50%)</span>
          <span class="value">¥{{ product.quotation.allocation.company }}</span>
        </div>
      </div>
    </div>
  }
</div>
```

### 5.3 报价汇总区域

```html
<div class="quotation-summary">
  <div class="summary-header">
    <h4>报价汇总</h4>
    <button class="btn-toggle" (click)="showBreakdown = !showBreakdown">
      {{ showBreakdown ? '隐藏' : '显示' }}明细
    </button>
  </div>

  <!-- 产品占比明细 -->
  @if (showBreakdown) {
    <div class="breakdown-list">
      @for (item of quotation.spaceBreakdown; track item.spaceId) {
        <div class="breakdown-item">
          <span class="name">{{ item.spaceName }}</span>
          <span class="amount">¥{{ item.amount }}</span>
          <span class="percentage">{{ item.percentage }}%</span>
        </div>
      }
    </div>
  }

  <!-- 内部执行分配 -->
  <div class="allocation-section">
    <div class="allocation-header">
      <h4>内部执行分配</h4>
      <button class="btn-toggle" (click)="showAllocation = !showAllocation">
        {{ showAllocation ? '隐藏' : '显示' }}
      </button>
    </div>

    @if (showAllocation && quotation.allocation) {
      <div class="allocation-list">
        <!-- 建模阶段 -->
        <div class="allocation-card modeling">
          <div class="allocation-icon">
            <svg>...</svg>
          </div>
          <div class="allocation-info">
            <h5>建模阶段</h5>
            <p>3D模型构建</p>
          </div>
          <div class="allocation-values">
            <span class="percentage">10%</span>
            <span class="amount">¥{{ quotation.allocation.modeling.amount }}</span>
          </div>
        </div>

        <!-- 软装渲染 -->
        <div class="allocation-card decoration">
          <div class="allocation-icon">
            <svg>...</svg>
          </div>
          <div class="allocation-info">
            <h5>软装渲染</h5>
            <p>软装搭配+效果图渲染</p>
          </div>
          <div class="allocation-values">
            <span class="percentage">40%</span>
            <span class="amount">¥{{ quotation.allocation.decoration.amount }}</span>
          </div>
        </div>

        <!-- 公司分配 -->
        <div class="allocation-card company">
          <div class="allocation-icon">
            <svg>...</svg>
          </div>
          <div class="allocation-info">
            <h5>公司分配</h5>
            <p>公司运营与利润</p>
          </div>
          <div class="allocation-values">
            <span class="percentage">50%</span>
            <span class="amount">¥{{ quotation.allocation.company.amount }}</span>
          </div>
        </div>
      </div>

      <div class="allocation-note">
        <i class="icon-info"></i>
        <span>内部执行分配为系统自动计算，基于报价总额按固定比例拆分，所有金额均为整数</span>
      </div>
    }
  </div>

  <!-- 总价 -->
  <div class="total-section">
    <div class="total-row">
      <span class="label">报价总额</span>
      <span class="amount">¥{{ quotation.total }}</span>
    </div>
    <div class="total-meta">
      <span>生成于 {{ quotation.generatedAt | date:'yyyy-MM-dd HH:mm' }}</span>
      <span>有效期至 {{ quotation.validUntil | date:'yyyy-MM-dd' }}</span>
    </div>
  </div>
</div>
```

---

## 六、移动端适配

### 6.1 响应式设计要点

1. **卡片布局**: 移动端单列显示，宽度100%
2. **工序网格**: 移动端改为单列，避免横向滚动
3. **表单元素**: 增大触摸区域，最小44×44px
4. **字体大小**: 移动端基础字号14px，标题16-18px
5. **间距优化**: 移动端适当增加卡片间距，提升点击体验

### 6.2 移动端专属优化

```scss
@media (max-width: 768px) {
  // 产品卡片
  .product-card {
    margin-bottom: 16px;

    .product-header {
      flex-direction: column;
      align-items: flex-start;
      padding: 16px;

      .product-pricing {
        width: 100%;
        margin-top: 12px;
        display: flex;
        justify-content: space-between;
      }
    }
  }

  // 工序网格
  .process-grid {
    grid-template-columns: 1fr;
  }

  // 分配卡片
  .allocation-card {
    flex-direction: column;
    align-items: flex-start;

    .allocation-values {
      width: 100%;
      flex-direction: row;
      justify-content: space-between;
      margin-top: 12px;
      padding-top: 12px;
      border-top: 1px solid rgba(0,0,0,0.05);
    }
  }

  // 添加产品模态框
  .add-product-modal {
    width: 100%;
    max-height: 90vh;
    border-radius: 16px 16px 0 0;

    .scene-grid {
      grid-template-columns: repeat(3, 1fr);
    }
  }
}
```

---

## 七、技术实现要点

### 7.1 关键方法实现

#### 7.1.1 calculateBasePrice（重构版）

```typescript
/**
 * 计算空间基础价格
 * 严格按照 docs/data/quotation.md 规则
 */
private calculateBasePrice(config: ProductConfig): number {
  const {
    priceLevel,
    projectType,
    renderType,
    spaceType,
    styleLevel,
    businessType,
    architectureType
  } = config;

  // 使用quotation-rules.ts中的getBasePrice函数
  const basePrice = getBasePrice(
    priceLevel,
    projectType,
    renderType,
    spaceType,
    styleLevel,
    businessType,
    architectureType
  );

  // 确保返回整数
  return Math.round(basePrice);
}
```

#### 7.1.2 整数价格确保

```typescript
/**
 * 确保所有价格计算结果都是整数
 */
private ensureIntegerPrices(quotation: any) {
  // 工序价格整数化
  for (const space of quotation.spaces) {
    for (const processKey of Object.keys(space.processes)) {
      const process = space.processes[processKey];
      process.price = Math.round(process.price);
      process.quantity = Math.round(process.quantity);
    }
  }

  // 总价整数化
  quotation.total = Math.round(quotation.total);

  // 分配金额整数化
  if (quotation.allocation) {
    quotation.allocation.modeling.amount = Math.round(quotation.allocation.modeling.amount);
    quotation.allocation.decoration.amount = Math.round(quotation.allocation.decoration.amount);
    quotation.allocation.company.amount = Math.round(quotation.allocation.company.amount);
  }
}
```

---

## 八、测试用例

### 8.1 基础价格计算测试

```typescript
test('家装平层基础风格组一级报价应为300元', () => {
  const price = calculateBasePrice({
    priceLevel: '一级',
    projectType: '家装',
    renderType: '静态单张',
    spaceType: '平层',
    styleLevel: '基础风格组'
  });
  expect(price).toBe(300);
});

test('工装办公门厅三级报价应为900元', () => {
  const price = calculateBasePrice({
    priceLevel: '三级',
    projectType: '工装',
    renderType: '静态单张',
    spaceType: '门厅空间',
    businessType: '办公空间'
  });
  expect(price).toBe(900);
});
```

### 8.2 分配计算测试

```typescript
test('900元报价应分配为90-360-450', () => {
  const allocation = calculateAllocation(900);
  expect(allocation.modeling.amount).toBe(90);
  expect(allocation.decoration.amount).toBe(360);
  expect(allocation.company.amount).toBe(450);
});

test('分配金额应全部为整数', () => {
  const allocation = calculateAllocation(12345);
  expect(allocation.modeling.amount % 1).toBe(0);
  expect(allocation.decoration.amount % 1).toBe(0);
  expect(allocation.company.amount % 1).toBe(0);
});
```

---

## 九、FAQ

### Q1: 为什么每个空间都是独立的设计产品?
**A:** 按照业务规则，每个空间（客厅、卧室、厨房等）都是一个独立的设计图服务，有独立的报价、工序和分配。这样可以精准控制每个空间的成本和利润。

### Q2: 如何确保价格都是整数?
**A:** 所有涉及价格计算的地方都使用 `Math.round()` 进行四舍五入，包括基础价格、工序价格、总价、分配金额等。

### Q3: 预设场景列表可以自定义吗?
**A:** 可以。预设场景列表定义在组件配置中，管理员可以根据实际业务需求调整。如果用户选择"自定义"，可以手动输入空间名称。

### Q4: 报价规则如何更新?
**A:** 报价规则定义在 `quotation-rules.ts` 配置文件和 `docs/data/quotation.md` 文档中。更新时需同步修改两处，确保代码和文档一致。

---

**文档版本**: v2.0
**创建日期**: 2025-10-25
**更新日期**: 2025-10-25
**作者**: Claude Code AI
**审核状态**: 待审核
