|
@@ -0,0 +1,2207 @@
|
|
|
+# 确认需求功能优化方案文档
|
|
|
+
|
|
|
+## 📋 项目背景
|
|
|
+
|
|
|
+### 当前功能概述
|
|
|
+
|
|
|
+本文档针对**需求沟通阶段**的**确认需求**功能模块进行全面分析和优化建议。该模块位于设计师项目详情页面中,是整个项目流程中的关键环节。
|
|
|
+
|
|
|
+### 相关代码文件
|
|
|
+
|
|
|
+- **主组件**:`src/app/shared/components/requirements-confirm-card/requirements-confirm-card.ts`
|
|
|
+- **模板文件**:`src/app/shared/components/requirements-confirm-card/requirements-confirm-card.html`
|
|
|
+- **样式文件**:`src/app/shared/components/requirements-confirm-card/requirements-confirm-card.scss`
|
|
|
+- **父组件集成**:`src/app/pages/designer/project-detail/project-detail.ts`
|
|
|
+
|
|
|
+### 当前功能架构
|
|
|
+
|
|
|
+确认需求模块采用**四标签页**架构:
|
|
|
+1. **素材解析** - 文本、图片、CAD上传及分析
|
|
|
+2. **需求映射** - 基于固定场景的参数映射和氛围预览
|
|
|
+3. **协作验证** - 需求列表、评论、优先级管理
|
|
|
+4. **进度管理** - 整体进度跟踪和历史状态管理
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 📊 功能对比矩阵
|
|
|
+
|
|
|
+### 需求文档要求 vs 当前实现对比
|
|
|
+
|
|
|
+| 功能模块 | 需求文档要求 | 当前实现状态 | 优先级 | 完成度 |
|
|
|
+|---------|------------|------------|-------|-------|
|
|
|
+| **素材分析** |
|
|
|
+| 色彩分析 | 补充色轮图标,完整展示色彩信息 | 有基础数据,缺少可视化 | 🔴 高 | 60% |
|
|
|
+| 形体分析 | 整体形体+软装形体选择 | 只有分析数据,无选择功能 | 🔴 高 | 40% |
|
|
|
+| 质感分析 | 质感、材质、亮光/哑光对比 | 有材质分类,展示不直观 | 🟡 中 | 50% |
|
|
|
+| 纹理分析 | 图案可视化展示 | 只有文字标签 | 🟡 中 | 40% |
|
|
|
+| 灯光分析 | 光色、光比、光质、光占比 | 有光色和光质,缺光比和光占比 | 🔴 高 | 50% |
|
|
|
+| 完全展示所有信息 | 核心要求 | 信息折叠,展示简化 | 🔴 高 | 50% |
|
|
|
+| **需求映射** |
|
|
|
+| 功能定位 | 隐藏功能,后台自动生成 | 独立标签页,用户可见 | 🟡 中 | 需调整 |
|
|
|
+| **协作验证** |
|
|
|
+| 五维度验证 | 色彩、形体、质感、纹理、灯光分类 | 通用需求列表 | 🔴 高 | 20% |
|
|
|
+| 素材关联 | 关联参考图、CAD、文字说明 | 无关联机制 | 🔴 高 | 0% |
|
|
|
+| 选择和标注 | 交互式标注工具 | 无标注功能 | 🔴 高 | 0% |
|
|
|
+| 区域细分 | 支持不同区域(客厅、婴儿房等) | 只有整体方案 | 🔴 高 | 0% |
|
|
|
+| 分工人员 | 不同区域分配不同人员 | 只有角色标识 | 🟡 中 | 10% |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 🎯 详细优化点清单
|
|
|
+
|
|
|
+## A. 素材分析标签页优化
|
|
|
+
|
|
|
+### A1. 色彩分析增强 🔴 高优先级
|
|
|
+
|
|
|
+#### 当前问题
|
|
|
+- 只显示简单的SVG图标
|
|
|
+- 色彩数据展示不够直观
|
|
|
+- 缺少交互式色轮可视化
|
|
|
+
|
|
|
+#### 优化方案
|
|
|
+**1. 交互式色轮可视化组件**
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 新建组件:src/app/shared/components/color-wheel-visualizer/
|
|
|
+interface ColorWheelConfig {
|
|
|
+ colors: Array<{
|
|
|
+ hue: number; // 色调角度 0-360
|
|
|
+ saturation: number; // 饱和度 0-100
|
|
|
+ brightness: number; // 亮度 0-100
|
|
|
+ percentage: number; // 占比
|
|
|
+ hex: string;
|
|
|
+ }>;
|
|
|
+ interactive: boolean; // 是否可交互
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**功能特性:**
|
|
|
+- SVG绘制的色轮,显示360度色相环
|
|
|
+- 在色轮上标注主要颜色的位置
|
|
|
+- 鼠标悬停显示详细信息(RGB、HEX、HSB值)
|
|
|
+- 显示色彩分布饼图
|
|
|
+- 色彩和谐度可视化(单色、类似色、互补色等)
|
|
|
+
|
|
|
+**2. 色彩详情卡片**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="color-analysis-detail">
|
|
|
+ <div class="color-wheel-container">
|
|
|
+ <app-color-wheel-visualizer
|
|
|
+ [colors]="material.analysis.enhancedColorAnalysis.colorWheel.colorDistribution"
|
|
|
+ [interactive]="true">
|
|
|
+ </app-color-wheel-visualizer>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="color-info-grid">
|
|
|
+ <!-- 主色调信息 -->
|
|
|
+ <div class="color-metric">
|
|
|
+ <span class="label">主色调</span>
|
|
|
+ <div class="hue-indicator" [style.background]="getHueColor()"></div>
|
|
|
+ <span class="value">{{ primaryHue }}°</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 饱和度 -->
|
|
|
+ <div class="color-metric">
|
|
|
+ <span class="label">饱和度</span>
|
|
|
+ <div class="saturation-bar">
|
|
|
+ <div class="fill" [style.width.%]="saturation"></div>
|
|
|
+ </div>
|
|
|
+ <span class="value">{{ saturation }}%</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 色温 -->
|
|
|
+ <div class="color-metric">
|
|
|
+ <span class="label">色温</span>
|
|
|
+ <div class="temperature-indicator" [class]="temperatureClass"></div>
|
|
|
+ <span class="value">{{ temperature }}K ({{ temperatureDesc }})</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 色彩心理学 -->
|
|
|
+ <div class="color-psychology">
|
|
|
+ <h6>色彩心理学</h6>
|
|
|
+ <div class="mood-tags">
|
|
|
+ <span class="mood-tag primary">{{ primaryMood }}</span>
|
|
|
+ <span class="atmosphere-tag">{{ atmosphere }}</span>
|
|
|
+ </div>
|
|
|
+ <ul class="effects-list">
|
|
|
+ <li *ngFor="let effect of psychologicalEffects">{{ effect }}</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**3. 技术实施细节**
|
|
|
+- 使用D3.js或纯SVG实现色轮
|
|
|
+- 响应式设计,适配不同屏幕尺寸
|
|
|
+- 动画过渡效果
|
|
|
+- 支持导出色板
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### A2. 形体分析补充 🔴 高优先级
|
|
|
+
|
|
|
+#### 当前问题
|
|
|
+- 只展示形体复杂度和视觉冲击的数值
|
|
|
+- 缺少软装形体的分类和选择功能
|
|
|
+- 无法针对特定形体进行标注
|
|
|
+
|
|
|
+#### 优化方案
|
|
|
+
|
|
|
+**1. 形体分类展示**
|
|
|
+
|
|
|
+```typescript
|
|
|
+interface FormCategory {
|
|
|
+ overall: {
|
|
|
+ lineType: 'straight' | 'curved' | 'mixed'; // 直线/曲线/混合
|
|
|
+ complexity: number; // 复杂度 0-100
|
|
|
+ symmetry: 'symmetric' | 'asymmetric'; // 对称/不对称
|
|
|
+ };
|
|
|
+ furniture: {
|
|
|
+ dominant: string[]; // 主要家具形体
|
|
|
+ style: string; // 风格(简约、古典、现代等)
|
|
|
+ proportion: number; // 占比
|
|
|
+ };
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**2. 软装形体选择器**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="form-analysis-section">
|
|
|
+ <h6>形体分析</h6>
|
|
|
+
|
|
|
+ <!-- 整体形体 -->
|
|
|
+ <div class="overall-form">
|
|
|
+ <div class="form-type-selector">
|
|
|
+ <label>线条类型:</label>
|
|
|
+ <div class="type-options">
|
|
|
+ <button class="type-btn"
|
|
|
+ [class.active]="formType === 'straight'"
|
|
|
+ (click)="selectFormType('straight')">
|
|
|
+ <svg><!-- 直线图标 --></svg>
|
|
|
+ 直线条
|
|
|
+ </button>
|
|
|
+ <button class="type-btn"
|
|
|
+ [class.active]="formType === 'curved'"
|
|
|
+ (click)="selectFormType('curved')">
|
|
|
+ <svg><!-- 曲线图标 --></svg>
|
|
|
+ 曲线条
|
|
|
+ </button>
|
|
|
+ <button class="type-btn"
|
|
|
+ [class.active]="formType === 'mixed'"
|
|
|
+ (click)="selectFormType('mixed')">
|
|
|
+ <svg><!-- 混合图标 --></svg>
|
|
|
+ 混合型
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 对称性 -->
|
|
|
+ <div class="symmetry-indicator">
|
|
|
+ <span class="label">对称性:</span>
|
|
|
+ <div class="symmetry-visual">
|
|
|
+ <svg><!-- 对称性可视化图标 --></svg>
|
|
|
+ </div>
|
|
|
+ <span class="value">{{ symmetryType }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 软装形体选择 -->
|
|
|
+ <div class="furniture-form-selector">
|
|
|
+ <h6>软装形体选择</h6>
|
|
|
+ <div class="furniture-grid">
|
|
|
+ <div class="furniture-item"
|
|
|
+ *ngFor="let item of furnitureItems"
|
|
|
+ [class.selected]="item.selected"
|
|
|
+ (click)="toggleFurniture(item)">
|
|
|
+ <img [src]="item.preview" [alt]="item.name">
|
|
|
+ <span class="name">{{ item.name }}</span>
|
|
|
+ <span class="form-tag">{{ item.formType }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 形体比例分析 -->
|
|
|
+ <div class="proportion-analysis">
|
|
|
+ <h6>形体比例分析</h6>
|
|
|
+ <div class="proportion-chart">
|
|
|
+ <!-- 使用ECharts绘制比例图 -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**3. 数据模型扩展**
|
|
|
+
|
|
|
+```typescript
|
|
|
+interface FurnitureFormItem {
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ category: 'sofa' | 'table' | 'chair' | 'cabinet' | 'bed' | 'decoration';
|
|
|
+ formType: 'geometric' | 'organic' | 'linear' | 'curved';
|
|
|
+ preview: string;
|
|
|
+ selected: boolean;
|
|
|
+ dominance: number; // 视觉主导性
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### A3. 质感分析优化 🟡 中优先级
|
|
|
+
|
|
|
+#### 当前问题
|
|
|
+- 只显示材质类别和粗糙度等级文字
|
|
|
+- 亮光/哑光对比不明显
|
|
|
+- 缺少质感的视觉化展示
|
|
|
+
|
|
|
+#### 优化方案
|
|
|
+
|
|
|
+**1. 亮光/哑光对比卡片**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="texture-analysis-section">
|
|
|
+ <h6>质感分析</h6>
|
|
|
+
|
|
|
+ <!-- 光泽度对比 -->
|
|
|
+ <div class="glossiness-comparison">
|
|
|
+ <div class="gloss-card matte" [class.active]="glossType === 'matte'">
|
|
|
+ <div class="gloss-preview matte-texture">
|
|
|
+ <span class="label">哑光</span>
|
|
|
+ <div class="gloss-level">{{ matteLevel }}/10</div>
|
|
|
+ </div>
|
|
|
+ <div class="gloss-properties">
|
|
|
+ <span>柔和 · 低反射 · 温暖</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="gloss-card glossy" [class.active]="glossType === 'glossy'">
|
|
|
+ <div class="gloss-preview glossy-texture">
|
|
|
+ <span class="label">亮光</span>
|
|
|
+ <div class="gloss-level">{{ glossyLevel }}/10</div>
|
|
|
+ </div>
|
|
|
+ <div class="gloss-properties">
|
|
|
+ <span>明亮 · 高反射 · 现代</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 材质属性详情 -->
|
|
|
+ <div class="material-properties">
|
|
|
+ <div class="property-row">
|
|
|
+ <span class="label">粗糙度</span>
|
|
|
+ <div class="property-bar">
|
|
|
+ <div class="fill roughness" [style.width.%]="roughness"></div>
|
|
|
+ <span class="value">{{ roughnessLevel }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="property-row">
|
|
|
+ <span class="label">反射率</span>
|
|
|
+ <div class="property-bar">
|
|
|
+ <div class="fill reflectivity" [style.width.%]="reflectivity"></div>
|
|
|
+ <span class="value">{{ reflectivity }}%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="property-row">
|
|
|
+ <span class="label">质感层次</span>
|
|
|
+ <div class="texture-layers">
|
|
|
+ <span class="layer" *ngFor="let layer of textureLayers">{{ layer }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 材质分类 -->
|
|
|
+ <div class="material-classification">
|
|
|
+ <h6>材质类型</h6>
|
|
|
+ <div class="material-tags">
|
|
|
+ <span class="material-tag primary">{{ primaryMaterial }}</span>
|
|
|
+ <span class="material-tag" *ngFor="let mat of secondaryMaterials">
|
|
|
+ {{ mat.name }} ({{ mat.percentage }}%)
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**2. 质感预览组件**
|
|
|
+
|
|
|
+使用3D渲染或高质量图片展示不同质感效果:
|
|
|
+- 木质(哑光/亮光)
|
|
|
+- 金属(拉丝/抛光)
|
|
|
+- 织物(粗糙/光滑)
|
|
|
+- 石材(天然/磨光)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### A4. 纹理分析增强 🟡 中优先级
|
|
|
+
|
|
|
+#### 当前问题
|
|
|
+- 只有纹理类型的文字标签
|
|
|
+- 缺少图案的可视化展示
|
|
|
+- 无法直观了解纹理重复规律
|
|
|
+
|
|
|
+#### 优化方案
|
|
|
+
|
|
|
+**1. 纹理图案可视化**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="pattern-analysis-section">
|
|
|
+ <h6>纹理分析</h6>
|
|
|
+
|
|
|
+ <!-- 图案预览 -->
|
|
|
+ <div class="pattern-preview-grid">
|
|
|
+ <div class="pattern-item" *ngFor="let pattern of recognizedPatterns">
|
|
|
+ <div class="pattern-visual">
|
|
|
+ <!-- 截取参考图中的纹理区域显示 -->
|
|
|
+ <img [src]="pattern.sampleImage" [alt]="pattern.type">
|
|
|
+ <div class="pattern-overlay">
|
|
|
+ <span class="pattern-type">{{ pattern.type }}</span>
|
|
|
+ <span class="coverage">{{ pattern.coverage }}%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="pattern-info">
|
|
|
+ <span class="complexity">复杂度: {{ pattern.complexity }}</span>
|
|
|
+ <span class="scale">尺度: {{ pattern.scale }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 重复规律分析 -->
|
|
|
+ <div class="repetition-analysis">
|
|
|
+ <h6>重复规律</h6>
|
|
|
+ <div class="repetition-visual">
|
|
|
+ <svg>
|
|
|
+ <!-- 绘制网格或重复单元 -->
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <div class="repetition-info">
|
|
|
+ <div class="info-row">
|
|
|
+ <span class="label">重复类型:</span>
|
|
|
+ <span class="value">{{ repetitionType }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-row">
|
|
|
+ <span class="label">间距:</span>
|
|
|
+ <span class="value">横向 {{ spacingH }}mm · 纵向 {{ spacingV }}mm</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-row">
|
|
|
+ <span class="label">对称性:</span>
|
|
|
+ <span class="value">{{ symmetryType }} (强度 {{ symmetryStrength }}%)</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 纹理族 -->
|
|
|
+ <div class="texture-family">
|
|
|
+ <h6>纹理族系</h6>
|
|
|
+ <div class="family-tags">
|
|
|
+ <span class="family-tag primary">{{ primaryFamily }}</span>
|
|
|
+ <span class="family-tag">{{ subcategory }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="suggested-materials">
|
|
|
+ <span class="label">建议材质:</span>
|
|
|
+ <span class="material" *ngFor="let mat of suggestedMaterials">{{ mat }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**2. 技术实施**
|
|
|
+- 使用Canvas API提取纹理样本
|
|
|
+- 傅里叶变换分析重复模式
|
|
|
+- 提供纹理缩放预览
|
|
|
+- 支持纹理导出
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### A5. 灯光分析补充 🔴 高优先级
|
|
|
+
|
|
|
+#### 当前问题
|
|
|
+- 只有光源类型、亮度、情绪
|
|
|
+- **缺少光比数据**
|
|
|
+- **缺少光占比数据**
|
|
|
+- 光质展示不够直观
|
|
|
+
|
|
|
+#### 优化方案
|
|
|
+
|
|
|
+**1. 完整的灯光分析面板**
|
|
|
+
|
|
|
+```typescript
|
|
|
+interface CompleteLightingAnalysis {
|
|
|
+ // 现有数据
|
|
|
+ lightSource: {
|
|
|
+ type: string;
|
|
|
+ intensity: number;
|
|
|
+ mood: string;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 新增:光比
|
|
|
+ lightingRatio: {
|
|
|
+ keyToFill: number; // 主光与补光比例 (如 3:1)
|
|
|
+ contrast: number; // 明暗对比度
|
|
|
+ description: string; // 高反差/低反差/中等
|
|
|
+ };
|
|
|
+
|
|
|
+ // 新增:光占比
|
|
|
+ lightDistribution: {
|
|
|
+ natural: number; // 自然光占比 %
|
|
|
+ artificial: number; // 人工光占比 %
|
|
|
+ ambient: number; // 环境光占比 %
|
|
|
+ direct: number; // 直射光占比 %
|
|
|
+ indirect: number; // 间接光占比 %
|
|
|
+ };
|
|
|
+
|
|
|
+ // 光质
|
|
|
+ lightQuality: {
|
|
|
+ softness: number; // 柔和度
|
|
|
+ diffusion: number; // 漫射度
|
|
|
+ directionality: number; // 方向性
|
|
|
+ };
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**2. 灯光分析UI**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="lighting-analysis-section">
|
|
|
+ <h6>灯光分析</h6>
|
|
|
+
|
|
|
+ <!-- 光源识别 -->
|
|
|
+ <div class="light-sources">
|
|
|
+ <div class="source-item" *ngFor="let source of lightSources">
|
|
|
+ <div class="source-icon" [class]="source.type">
|
|
|
+ <svg><!-- 光源图标 --></svg>
|
|
|
+ </div>
|
|
|
+ <div class="source-info">
|
|
|
+ <span class="type">{{ source.subtype }}</span>
|
|
|
+ <span class="intensity">强度 {{ source.intensity }}%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 光比分析 NEW -->
|
|
|
+ <div class="lighting-ratio">
|
|
|
+ <h6>光比分析</h6>
|
|
|
+ <div class="ratio-visual">
|
|
|
+ <div class="ratio-bars">
|
|
|
+ <div class="key-light-bar" [style.height.%]="keyLightRatio">
|
|
|
+ <span>主光</span>
|
|
|
+ <span class="value">{{ keyLightRatio }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="fill-light-bar" [style.height.%]="fillLightRatio">
|
|
|
+ <span>补光</span>
|
|
|
+ <span class="value">{{ fillLightRatio }}%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="ratio-description">
|
|
|
+ <span class="ratio-value">{{ keyToFillRatio }}</span>
|
|
|
+ <span class="contrast-level">{{ contrastDescription }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 光占比饼图 NEW -->
|
|
|
+ <div class="light-distribution">
|
|
|
+ <h6>光占比</h6>
|
|
|
+ <div class="distribution-chart">
|
|
|
+ <!-- 使用ECharts绘制饼图 -->
|
|
|
+ <canvas id="lightDistributionChart"></canvas>
|
|
|
+ </div>
|
|
|
+ <div class="distribution-legend">
|
|
|
+ <div class="legend-item">
|
|
|
+ <span class="color natural"></span>
|
|
|
+ <span class="label">自然光</span>
|
|
|
+ <span class="value">{{ naturalLight }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="legend-item">
|
|
|
+ <span class="color artificial"></span>
|
|
|
+ <span class="label">人工光</span>
|
|
|
+ <span class="value">{{ artificialLight }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="legend-item">
|
|
|
+ <span class="color ambient"></span>
|
|
|
+ <span class="label">环境光</span>
|
|
|
+ <span class="value">{{ ambientLight }}%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 光质分析 -->
|
|
|
+ <div class="light-quality">
|
|
|
+ <h6>光质</h6>
|
|
|
+ <div class="quality-metrics">
|
|
|
+ <div class="metric-item">
|
|
|
+ <span class="label">柔和度</span>
|
|
|
+ <div class="metric-bar">
|
|
|
+ <div class="fill" [style.width.%]="softness"></div>
|
|
|
+ </div>
|
|
|
+ <span class="value">{{ softness }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="metric-item">
|
|
|
+ <span class="label">漫射度</span>
|
|
|
+ <div class="metric-bar">
|
|
|
+ <div class="fill" [style.width.%]="diffusion"></div>
|
|
|
+ </div>
|
|
|
+ <span class="value">{{ diffusion }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="metric-item">
|
|
|
+ <span class="label">方向性</span>
|
|
|
+ <div class="metric-bar">
|
|
|
+ <div class="fill" [style.width.%]="directionality"></div>
|
|
|
+ </div>
|
|
|
+ <span class="value">{{ directionality }}%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 色温与情绪 -->
|
|
|
+ <div class="color-temperature-mood">
|
|
|
+ <div class="temperature">
|
|
|
+ <span class="label">色温</span>
|
|
|
+ <div class="temp-bar" [style.background]="tempGradient"></div>
|
|
|
+ <span class="value">{{ colorTemp }}K</span>
|
|
|
+ </div>
|
|
|
+ <div class="mood">
|
|
|
+ <span class="label">灯光情绪</span>
|
|
|
+ <span class="mood-tag">{{ lightingMood }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**3. 服务层扩展**
|
|
|
+
|
|
|
+修改 `src/app/shared/services/lighting-analysis.service.ts`:
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 新增光比计算方法
|
|
|
+calculateLightingRatio(image: File): Observable<LightingRatioResult> {
|
|
|
+ // 分析图像的亮度分布
|
|
|
+ // 识别主光源和补光源
|
|
|
+ // 计算光比
|
|
|
+}
|
|
|
+
|
|
|
+// 新增光占比计算方法
|
|
|
+calculateLightDistribution(lightingData: any): LightDistribution {
|
|
|
+ // 根据光源识别结果计算各类光源占比
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### A6. 全展示模式 🔴 高优先级
|
|
|
+
|
|
|
+#### 当前问题
|
|
|
+- 分析结果折叠显示,用户需要点击展开
|
|
|
+- 无法一次性查看所有素材的完整分析
|
|
|
+- 核心要求是"完全展示所有采集的参考信息"
|
|
|
+
|
|
|
+#### 优化方案
|
|
|
+
|
|
|
+**1. 展开/折叠控制**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="materials-list">
|
|
|
+ <div class="list-header">
|
|
|
+ <h5>已上传素材</h5>
|
|
|
+ <div class="view-controls">
|
|
|
+ <button class="btn-ghost"
|
|
|
+ [class.active]="viewMode === 'compact'"
|
|
|
+ (click)="setViewMode('compact')">
|
|
|
+ 精简视图
|
|
|
+ </button>
|
|
|
+ <button class="btn-ghost"
|
|
|
+ [class.active]="viewMode === 'expanded'"
|
|
|
+ (click)="setViewMode('expanded')">
|
|
|
+ 完整展示
|
|
|
+ </button>
|
|
|
+ <button class="btn-ghost" (click)="expandAll()">全部展开</button>
|
|
|
+ <button class="btn-ghost" (click)="collapseAll()">全部折叠</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="material-cards" [class]="viewMode">
|
|
|
+ <!-- 素材卡片 -->
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**2. 完整展示模式布局**
|
|
|
+
|
|
|
+```scss
|
|
|
+.material-cards.expanded {
|
|
|
+ .material-card {
|
|
|
+ .analysis-section {
|
|
|
+ display: block !important; // 强制展开所有分析部分
|
|
|
+
|
|
|
+ .color-wheel-visualizer,
|
|
|
+ .form-analysis-section,
|
|
|
+ .texture-analysis-section,
|
|
|
+ .pattern-analysis-section,
|
|
|
+ .lighting-analysis-section {
|
|
|
+ max-height: none;
|
|
|
+ overflow: visible;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**3. 打印/导出功能**
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 新增导出分析报告功能
|
|
|
+exportAnalysisReport(materialId: string, format: 'pdf' | 'excel' | 'json') {
|
|
|
+ const material = this.materials.find(m => m.id === materialId);
|
|
|
+ // 生成完整的分析报告
|
|
|
+}
|
|
|
+
|
|
|
+// 批量导出所有素材
|
|
|
+exportAllMaterials() {
|
|
|
+ // 导出所有素材的完整分析数据
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## B. 需求映射调整 🟡 中优先级
|
|
|
+
|
|
|
+### 当前问题
|
|
|
+- 需求映射作为独立标签页展示给用户
|
|
|
+- 文档要求:应该是隐藏功能,在后台自动生成
|
|
|
+
|
|
|
+### 优化方案
|
|
|
+
|
|
|
+**1. 移除"需求映射"标签页**
|
|
|
+
|
|
|
+修改 `requirements-confirm-card.html`:
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- 移除需求映射标签按钮 -->
|
|
|
+<div class="tab-navigation">
|
|
|
+ <button class="tab-button" [class.active]="activeTab === 'materials'">素材解析</button>
|
|
|
+ <!-- <button class="tab-button" [class.active]="activeTab === 'mapping'">需求映射</button> -->
|
|
|
+ <!-- 移除此标签 -->
|
|
|
+ <button class="tab-button" [class.active]="activeTab === 'collaboration'">协作验证</button>
|
|
|
+ <button class="tab-button" [class.active]="activeTab === 'progress'">进度管理</button>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**2. 自动触发映射机制**
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 在素材分析完成后自动触发
|
|
|
+private onMaterialAnalysisComplete(material: MaterialFile): void {
|
|
|
+ // 检查是否所有素材都已分析完成
|
|
|
+ const allAnalyzed = this.materials.every(m => m.analysis);
|
|
|
+
|
|
|
+ if (allAnalyzed) {
|
|
|
+ console.log('所有素材分析完成,自动触发需求映射');
|
|
|
+ this.triggerBackgroundMapping();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 后台映射生成
|
|
|
+private triggerBackgroundMapping(): void {
|
|
|
+ this.isGeneratingMapping = true;
|
|
|
+
|
|
|
+ // 收集所有分析结果
|
|
|
+ const analysisResults = this.materials
|
|
|
+ .filter(m => m.type === 'image')
|
|
|
+ .map(m => this.convertToColorAnalysisResult(m));
|
|
|
+
|
|
|
+ // 自动生成需求映射(不显示给用户)
|
|
|
+ this.requirementMappingService.generateRequirementMapping(
|
|
|
+ this.mergeAnalysisResults(analysisResults),
|
|
|
+ SceneTemplate.LIVING_ROOM_MODERN
|
|
|
+ ).subscribe({
|
|
|
+ next: (mapping) => {
|
|
|
+ this.requirementMapping = mapping;
|
|
|
+ this.isGeneratingMapping = false;
|
|
|
+
|
|
|
+ // 静默保存,不弹窗提示
|
|
|
+ console.log('需求映射已自动生成:', mapping);
|
|
|
+
|
|
|
+ // 发送映射数据到父组件(用于方案确认阶段展示)
|
|
|
+ this.emitMappingDataUpdate();
|
|
|
+ },
|
|
|
+ error: (error) => {
|
|
|
+ console.error('自动映射生成失败:', error);
|
|
|
+ this.isGeneratingMapping = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**3. 保留数据接口**
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 保留映射数据的访问接口,供其他模块使用
|
|
|
+public getMappingData(): RequirementMapping | null {
|
|
|
+ return this.requirementMapping;
|
|
|
+}
|
|
|
+
|
|
|
+// 提供重新生成映射的方法(调试用)
|
|
|
+public regenerateMapping(): void {
|
|
|
+ this.triggerBackgroundMapping();
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**4. 在开发模式下保留可见性(可选)**
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 环境配置
|
|
|
+showMappingTab = environment.production ? false : true;
|
|
|
+```
|
|
|
+
|
|
|
+```html
|
|
|
+<button class="tab-button"
|
|
|
+ *ngIf="showMappingTab"
|
|
|
+ [class.active]="activeTab === 'mapping'">
|
|
|
+ 需求映射 (调试)
|
|
|
+</button>
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## C. 协作验证重构 🔴 高优先级(核心)
|
|
|
+
|
|
|
+这是最重要的优化模块,需要完全重构当前的协作验证功能。
|
|
|
+
|
|
|
+### C1. 五维验证模块设计
|
|
|
+
|
|
|
+#### 当前问题
|
|
|
+- 只有通用的需求列表(色彩氛围、空间布局、材质选择)
|
|
|
+- 没有按照五个维度(色彩、形体、质感、纹理、灯光)分类
|
|
|
+- 无法针对特定维度进行深度验证
|
|
|
+
|
|
|
+#### 优化方案
|
|
|
+
|
|
|
+**1. 数据模型重构**
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 新建接口:src/app/models/collaboration-verification.interface.ts
|
|
|
+
|
|
|
+export interface DimensionVerification {
|
|
|
+ id: string;
|
|
|
+ dimension: 'color' | 'form' | 'texture' | 'pattern' | 'lighting';
|
|
|
+ dimensionName: string;
|
|
|
+ status: 'pending' | 'in-review' | 'confirmed' | 'rejected';
|
|
|
+
|
|
|
+ // 关联的素材
|
|
|
+ linkedMaterials: LinkedMaterial[];
|
|
|
+
|
|
|
+ // 验证项
|
|
|
+ verificationItems: VerificationItem[];
|
|
|
+
|
|
|
+ // 标注
|
|
|
+ annotations: Annotation[];
|
|
|
+
|
|
|
+ // 协作信息
|
|
|
+ assignedTo?: string;
|
|
|
+ comments: Comment[];
|
|
|
+
|
|
|
+ // 区域(如果是区域细分)
|
|
|
+ areaId?: string;
|
|
|
+
|
|
|
+ lastUpdated: Date;
|
|
|
+}
|
|
|
+
|
|
|
+export interface LinkedMaterial {
|
|
|
+ materialId: string;
|
|
|
+ materialType: 'image' | 'cad' | 'text';
|
|
|
+ materialName: string;
|
|
|
+ relevance: number; // 相关度 0-100
|
|
|
+ highlightAreas?: Array<{x: number; y: number; width: number; height: number}>;
|
|
|
+}
|
|
|
+
|
|
|
+export interface VerificationItem {
|
|
|
+ id: string;
|
|
|
+ title: string;
|
|
|
+ description: string;
|
|
|
+ status: 'pending' | 'confirmed' | 'rejected';
|
|
|
+ priority: 'high' | 'medium' | 'low';
|
|
|
+ tags: string[];
|
|
|
+}
|
|
|
+
|
|
|
+export interface Annotation {
|
|
|
+ id: string;
|
|
|
+ type: 'text' | 'arrow' | 'highlight' | 'circle' | 'rectangle';
|
|
|
+ content?: string;
|
|
|
+ position: {x: number; y: number};
|
|
|
+ size?: {width: number; height: number};
|
|
|
+ color: string;
|
|
|
+ author: string;
|
|
|
+ timestamp: Date;
|
|
|
+ linkedMaterialId: string;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**2. 五维验证UI布局**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="collaboration-section-redesign">
|
|
|
+ <div class="section-header">
|
|
|
+ <h5>协作验证</h5>
|
|
|
+ <div class="dimension-selector">
|
|
|
+ <button class="dimension-tab"
|
|
|
+ *ngFor="let dim of dimensions"
|
|
|
+ [class.active]="activeDimension === dim.key"
|
|
|
+ (click)="selectDimension(dim.key)">
|
|
|
+ <svg [innerHTML]="dim.icon"></svg>
|
|
|
+ <span>{{ dim.name }}</span>
|
|
|
+ <span class="status-badge" [class]="getDimensionStatus(dim.key)">
|
|
|
+ {{ getDimensionStatusText(dim.key) }}
|
|
|
+ </span>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 当前维度的验证内容 -->
|
|
|
+ <div class="dimension-content" [ngSwitch]="activeDimension">
|
|
|
+
|
|
|
+ <!-- 色彩验证 -->
|
|
|
+ <div *ngSwitchCase="'color'" class="color-verification">
|
|
|
+ <app-dimension-verification
|
|
|
+ [dimension]="'color'"
|
|
|
+ [materials]="getColorRelatedMaterials()"
|
|
|
+ [verificationData]="colorVerificationData"
|
|
|
+ (materialLinked)="onMaterialLinked($event)"
|
|
|
+ (annotationAdded)="onAnnotationAdded($event)"
|
|
|
+ (statusChanged)="onVerificationStatusChanged($event)">
|
|
|
+ </app-dimension-verification>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 形体验证 -->
|
|
|
+ <div *ngSwitchCase="'form'" class="form-verification">
|
|
|
+ <app-dimension-verification
|
|
|
+ [dimension]="'form'"
|
|
|
+ [materials]="getFormRelatedMaterials()"
|
|
|
+ [verificationData]="formVerificationData">
|
|
|
+ </app-dimension-verification>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 质感验证 -->
|
|
|
+ <div *ngSwitchCase="'texture'" class="texture-verification">
|
|
|
+ <app-dimension-verification
|
|
|
+ [dimension]="'texture'"
|
|
|
+ [materials]="getTextureRelatedMaterials()"
|
|
|
+ [verificationData]="textureVerificationData">
|
|
|
+ </app-dimension-verification>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 纹理验证 -->
|
|
|
+ <div *ngSwitchCase="'pattern'" class="pattern-verification">
|
|
|
+ <app-dimension-verification
|
|
|
+ [dimension]="'pattern'"
|
|
|
+ [materials]="getPatternRelatedMaterials()"
|
|
|
+ [verificationData]="patternVerificationData">
|
|
|
+ </app-dimension-verification>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 灯光验证 -->
|
|
|
+ <div *ngSwitchCase="'lighting'" class="lighting-verification">
|
|
|
+ <app-dimension-verification
|
|
|
+ [dimension]="'lighting'"
|
|
|
+ [materials]="getLightingRelatedMaterials()"
|
|
|
+ [verificationData]="lightingVerificationData">
|
|
|
+ </app-dimension-verification>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**3. 新建维度验证组件**
|
|
|
+
|
|
|
+创建 `src/app/shared/components/dimension-verification/`
|
|
|
+
|
|
|
+```typescript
|
|
|
+@Component({
|
|
|
+ selector: 'app-dimension-verification',
|
|
|
+ templateUrl: './dimension-verification.component.html',
|
|
|
+ styleUrls: ['./dimension-verification.component.scss']
|
|
|
+})
|
|
|
+export class DimensionVerificationComponent implements OnInit {
|
|
|
+ @Input() dimension!: 'color' | 'form' | 'texture' | 'pattern' | 'lighting';
|
|
|
+ @Input() materials: MaterialFile[] = [];
|
|
|
+ @Input() verificationData?: DimensionVerification;
|
|
|
+
|
|
|
+ @Output() materialLinked = new EventEmitter<LinkedMaterial>();
|
|
|
+ @Output() annotationAdded = new EventEmitter<Annotation>();
|
|
|
+ @Output() statusChanged = new EventEmitter<{dimension: string; status: string}>();
|
|
|
+
|
|
|
+ // 组件逻辑...
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### C2. 素材关联系统
|
|
|
+
|
|
|
+#### 功能设计
|
|
|
+
|
|
|
+**1. 拖拽关联**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="material-linking-panel">
|
|
|
+ <div class="source-materials">
|
|
|
+ <h6>素材库</h6>
|
|
|
+ <div class="material-list"
|
|
|
+ cdkDropList
|
|
|
+ #materialList="cdkDropList"
|
|
|
+ [cdkDropListData]="availableMaterials"
|
|
|
+ [cdkDropListConnectedTo]="[linkedList]">
|
|
|
+
|
|
|
+ <div class="material-item"
|
|
|
+ *ngFor="let material of availableMaterials"
|
|
|
+ cdkDrag>
|
|
|
+ <img [src]="material.url" [alt]="material.name">
|
|
|
+ <span>{{ material.name }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="linked-materials">
|
|
|
+ <h6>已关联素材 - {{ dimensionName }}</h6>
|
|
|
+ <div class="linked-list"
|
|
|
+ cdkDropList
|
|
|
+ #linkedList="cdkDropList"
|
|
|
+ [cdkDropListData]="linkedMaterials"
|
|
|
+ [cdkDropListConnectedTo]="[materialList]"
|
|
|
+ (cdkDropListDropped)="onMaterialDropped($event)">
|
|
|
+
|
|
|
+ <div class="linked-item"
|
|
|
+ *ngFor="let linked of linkedMaterials"
|
|
|
+ cdkDrag>
|
|
|
+ <img [src]="linked.materialUrl" [alt]="linked.materialName">
|
|
|
+ <div class="linked-info">
|
|
|
+ <span class="name">{{ linked.materialName }}</span>
|
|
|
+ <span class="relevance">相关度: {{ linked.relevance }}%</span>
|
|
|
+ </div>
|
|
|
+ <button class="btn-remove" (click)="unlinkMaterial(linked.id)">
|
|
|
+ <svg><!-- 删除图标 --></svg>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**2. 下拉选择关联**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="material-selector">
|
|
|
+ <label>关联参考图:</label>
|
|
|
+ <select (change)="linkMaterial($event.target.value)">
|
|
|
+ <option value="">-- 选择素材 --</option>
|
|
|
+ <option *ngFor="let material of availableMaterials"
|
|
|
+ [value]="material.id">
|
|
|
+ {{ material.name }}
|
|
|
+ </option>
|
|
|
+ </select>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**3. 关联逻辑实现**
|
|
|
+
|
|
|
+```typescript
|
|
|
+onMaterialDropped(event: CdkDragDrop<MaterialFile[]>) {
|
|
|
+ if (event.previousContainer === event.container) {
|
|
|
+ moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
|
|
|
+ } else {
|
|
|
+ transferArrayItem(
|
|
|
+ event.previousContainer.data,
|
|
|
+ event.container.data,
|
|
|
+ event.previousIndex,
|
|
|
+ event.currentIndex
|
|
|
+ );
|
|
|
+
|
|
|
+ // 创建关联
|
|
|
+ const material = event.container.data[event.currentIndex];
|
|
|
+ this.createMaterialLink(material);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private createMaterialLink(material: MaterialFile): void {
|
|
|
+ const linked: LinkedMaterial = {
|
|
|
+ materialId: material.id,
|
|
|
+ materialType: material.type,
|
|
|
+ materialName: material.name,
|
|
|
+ relevance: this.calculateRelevance(material), // 自动计算相关度
|
|
|
+ highlightAreas: []
|
|
|
+ };
|
|
|
+
|
|
|
+ this.materialLinked.emit(linked);
|
|
|
+}
|
|
|
+
|
|
|
+// 计算素材与当前维度的相关度
|
|
|
+private calculateRelevance(material: MaterialFile): number {
|
|
|
+ // 根据维度和素材分析结果计算相关度
|
|
|
+ switch(this.dimension) {
|
|
|
+ case 'color':
|
|
|
+ return material.analysis?.enhancedColorAnalysis ? 90 : 50;
|
|
|
+ case 'form':
|
|
|
+ return material.analysis?.formAnalysis ? 85 : 40;
|
|
|
+ case 'texture':
|
|
|
+ return material.analysis?.textureAnalysis ? 88 : 45;
|
|
|
+ case 'pattern':
|
|
|
+ return material.analysis?.patternAnalysis ? 82 : 35;
|
|
|
+ case 'lighting':
|
|
|
+ return material.analysis?.lightingAnalysis ? 92 : 50;
|
|
|
+ default:
|
|
|
+ return 50;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### C3. 标注工具集成
|
|
|
+
|
|
|
+#### 功能设计
|
|
|
+
|
|
|
+**1. 标注工具栏**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="annotation-toolbar">
|
|
|
+ <button class="tool-btn"
|
|
|
+ [class.active]="activeTool === 'text'"
|
|
|
+ (click)="selectTool('text')">
|
|
|
+ <svg><!-- 文本图标 --></svg>
|
|
|
+ 文字
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button class="tool-btn"
|
|
|
+ [class.active]="activeTool === 'arrow'"
|
|
|
+ (click)="selectTool('arrow')">
|
|
|
+ <svg><!-- 箭头图标 --></svg>
|
|
|
+ 箭头
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button class="tool-btn"
|
|
|
+ [class.active]="activeTool === 'highlight'"
|
|
|
+ (click)="selectTool('highlight')">
|
|
|
+ <svg><!-- 高亮图标 --></svg>
|
|
|
+ 高亮
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button class="tool-btn"
|
|
|
+ [class.active]="activeTool === 'circle'"
|
|
|
+ (click)="selectTool('circle')">
|
|
|
+ <svg><!-- 圆圈图标 --></svg>
|
|
|
+ 圆圈
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button class="tool-btn"
|
|
|
+ [class.active]="activeTool === 'rectangle'"
|
|
|
+ (click)="selectTool('rectangle')">
|
|
|
+ <svg><!-- 矩形图标 --></svg>
|
|
|
+ 矩形
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <div class="color-picker">
|
|
|
+ <input type="color"
|
|
|
+ [(ngModel)]="annotationColor"
|
|
|
+ title="标注颜色">
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <button class="tool-btn" (click)="clearAnnotations()">
|
|
|
+ <svg><!-- 清除图标 --></svg>
|
|
|
+ 清除全部
|
|
|
+ </button>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**2. 标注画布**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="annotation-canvas-container">
|
|
|
+ <div class="canvas-wrapper">
|
|
|
+ <!-- 底层:原始图片 -->
|
|
|
+ <img [src]="selectedMaterial.url"
|
|
|
+ class="base-image"
|
|
|
+ #baseImage>
|
|
|
+
|
|
|
+ <!-- 中层:SVG标注层 -->
|
|
|
+ <svg class="annotation-layer"
|
|
|
+ [attr.width]="canvasWidth"
|
|
|
+ [attr.height]="canvasHeight"
|
|
|
+ (mousedown)="startAnnotation($event)"
|
|
|
+ (mousemove)="continueAnnotation($event)"
|
|
|
+ (mouseup)="finishAnnotation($event)">
|
|
|
+
|
|
|
+ <!-- 已有标注 -->
|
|
|
+ <g *ngFor="let annotation of annotations"
|
|
|
+ [attr.data-id]="annotation.id"
|
|
|
+ class="annotation-item"
|
|
|
+ (click)="selectAnnotation(annotation)">
|
|
|
+
|
|
|
+ <!-- 文字标注 -->
|
|
|
+ <text *ngIf="annotation.type === 'text'"
|
|
|
+ [attr.x]="annotation.position.x"
|
|
|
+ [attr.y]="annotation.position.y"
|
|
|
+ [attr.fill]="annotation.color">
|
|
|
+ {{ annotation.content }}
|
|
|
+ </text>
|
|
|
+
|
|
|
+ <!-- 箭头标注 -->
|
|
|
+ <line *ngIf="annotation.type === 'arrow'"
|
|
|
+ [attr.x1]="annotation.start.x"
|
|
|
+ [attr.y1]="annotation.start.y"
|
|
|
+ [attr.x2]="annotation.end.x"
|
|
|
+ [attr.y2]="annotation.end.y"
|
|
|
+ [attr.stroke]="annotation.color"
|
|
|
+ stroke-width="2"
|
|
|
+ marker-end="url(#arrowhead)">
|
|
|
+ </line>
|
|
|
+
|
|
|
+ <!-- 高亮标注 -->
|
|
|
+ <rect *ngIf="annotation.type === 'highlight'"
|
|
|
+ [attr.x]="annotation.position.x"
|
|
|
+ [attr.y]="annotation.position.y"
|
|
|
+ [attr.width]="annotation.size.width"
|
|
|
+ [attr.height]="annotation.size.height"
|
|
|
+ [attr.fill]="annotation.color"
|
|
|
+ opacity="0.3">
|
|
|
+ </rect>
|
|
|
+
|
|
|
+ <!-- 圆圈标注 -->
|
|
|
+ <circle *ngIf="annotation.type === 'circle'"
|
|
|
+ [attr.cx]="annotation.position.x"
|
|
|
+ [attr.cy]="annotation.position.y"
|
|
|
+ [attr.r]="annotation.radius"
|
|
|
+ [attr.stroke]="annotation.color"
|
|
|
+ stroke-width="2"
|
|
|
+ fill="none">
|
|
|
+ </circle>
|
|
|
+
|
|
|
+ <!-- 矩形标注 -->
|
|
|
+ <rect *ngIf="annotation.type === 'rectangle'"
|
|
|
+ [attr.x]="annotation.position.x"
|
|
|
+ [attr.y]="annotation.position.y"
|
|
|
+ [attr.width]="annotation.size.width"
|
|
|
+ [attr.height]="annotation.size.height"
|
|
|
+ [attr.stroke]="annotation.color"
|
|
|
+ stroke-width="2"
|
|
|
+ fill="none">
|
|
|
+ </rect>
|
|
|
+ </g>
|
|
|
+
|
|
|
+ <!-- 箭头标记定义 -->
|
|
|
+ <defs>
|
|
|
+ <marker id="arrowhead" markerWidth="10" markerHeight="10"
|
|
|
+ refX="5" refY="5" orient="auto">
|
|
|
+ <polygon points="0 0, 10 5, 0 10" [attr.fill]="annotationColor" />
|
|
|
+ </marker>
|
|
|
+ </defs>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 标注列表 -->
|
|
|
+ <div class="annotation-list">
|
|
|
+ <h6>标注列表</h6>
|
|
|
+ <div class="annotation-item-list">
|
|
|
+ <div class="list-item"
|
|
|
+ *ngFor="let annotation of annotations"
|
|
|
+ [class.selected]="selectedAnnotation?.id === annotation.id"
|
|
|
+ (click)="selectAnnotation(annotation)">
|
|
|
+ <span class="type-icon">{{ getAnnotationIcon(annotation.type) }}</span>
|
|
|
+ <span class="content">{{ annotation.content || annotation.type }}</span>
|
|
|
+ <span class="author">{{ annotation.author }}</span>
|
|
|
+ <button class="btn-delete" (click)="deleteAnnotation(annotation.id)">
|
|
|
+ <svg><!-- 删除图标 --></svg>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**3. 标注逻辑实现**
|
|
|
+
|
|
|
+```typescript
|
|
|
+export class AnnotationToolComponent {
|
|
|
+ activeTool: 'text' | 'arrow' | 'highlight' | 'circle' | 'rectangle' | null = null;
|
|
|
+ annotationColor = '#FF0000';
|
|
|
+ annotations: Annotation[] = [];
|
|
|
+ isDrawing = false;
|
|
|
+ currentAnnotation: Partial<Annotation> | null = null;
|
|
|
+
|
|
|
+ startAnnotation(event: MouseEvent): void {
|
|
|
+ if (!this.activeTool) return;
|
|
|
+
|
|
|
+ this.isDrawing = true;
|
|
|
+ const rect = (event.target as HTMLElement).getBoundingClientRect();
|
|
|
+ const x = event.clientX - rect.left;
|
|
|
+ const y = event.clientY - rect.top;
|
|
|
+
|
|
|
+ this.currentAnnotation = {
|
|
|
+ id: this.generateId(),
|
|
|
+ type: this.activeTool,
|
|
|
+ position: { x, y },
|
|
|
+ color: this.annotationColor,
|
|
|
+ author: this.getCurrentUser(),
|
|
|
+ timestamp: new Date()
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ continueAnnotation(event: MouseEvent): void {
|
|
|
+ if (!this.isDrawing || !this.currentAnnotation) return;
|
|
|
+
|
|
|
+ const rect = (event.target as HTMLElement).getBoundingClientRect();
|
|
|
+ const x = event.clientX - rect.left;
|
|
|
+ const y = event.clientY - rect.top;
|
|
|
+
|
|
|
+ // 根据工具类型更新标注
|
|
|
+ if (this.activeTool === 'arrow') {
|
|
|
+ this.currentAnnotation.end = { x, y };
|
|
|
+ } else if (['highlight', 'rectangle'].includes(this.activeTool!)) {
|
|
|
+ this.currentAnnotation.size = {
|
|
|
+ width: x - this.currentAnnotation.position!.x,
|
|
|
+ height: y - this.currentAnnotation.position!.y
|
|
|
+ };
|
|
|
+ } else if (this.activeTool === 'circle') {
|
|
|
+ const dx = x - this.currentAnnotation.position!.x;
|
|
|
+ const dy = y - this.currentAnnotation.position!.y;
|
|
|
+ this.currentAnnotation.radius = Math.sqrt(dx * dx + dy * dy);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ finishAnnotation(event: MouseEvent): void {
|
|
|
+ if (!this.isDrawing || !this.currentAnnotation) return;
|
|
|
+
|
|
|
+ this.isDrawing = false;
|
|
|
+
|
|
|
+ // 如果是文字工具,弹出输入框
|
|
|
+ if (this.activeTool === 'text') {
|
|
|
+ const text = prompt('请输入标注内容:');
|
|
|
+ if (text) {
|
|
|
+ this.currentAnnotation.content = text;
|
|
|
+ this.annotations.push(this.currentAnnotation as Annotation);
|
|
|
+ this.annotationAdded.emit(this.currentAnnotation as Annotation);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.annotations.push(this.currentAnnotation as Annotation);
|
|
|
+ this.annotationAdded.emit(this.currentAnnotation as Annotation);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.currentAnnotation = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ deleteAnnotation(id: string): void {
|
|
|
+ const index = this.annotations.findIndex(a => a.id === id);
|
|
|
+ if (index !== -1) {
|
|
|
+ this.annotations.splice(index, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ clearAnnotations(): void {
|
|
|
+ if (confirm('确定清除所有标注吗?')) {
|
|
|
+ this.annotations = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### C4. 区域细分管理
|
|
|
+
|
|
|
+#### 功能设计
|
|
|
+
|
|
|
+**1. 区域管理面板**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="area-management-panel">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h5>区域细分管理</h5>
|
|
|
+ <button class="btn-primary" (click)="addNewArea()">
|
|
|
+ <svg><!-- 添加图标 --></svg>
|
|
|
+ 新增区域
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 整体方案 -->
|
|
|
+ <div class="overall-plan"
|
|
|
+ [class.active]="selectedArea === null"
|
|
|
+ (click)="selectArea(null)">
|
|
|
+ <div class="area-header">
|
|
|
+ <svg><!-- 整体图标 --></svg>
|
|
|
+ <h6>整体方案</h6>
|
|
|
+ <span class="area-status" [class]="overallStatus">{{ getStatusText(overallStatus) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="area-summary">
|
|
|
+ <span>默认统一风格设计</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 细分区域列表 -->
|
|
|
+ <div class="area-list">
|
|
|
+ <div class="area-item"
|
|
|
+ *ngFor="let area of areas"
|
|
|
+ [class.active]="selectedArea?.id === area.id"
|
|
|
+ (click)="selectArea(area)">
|
|
|
+
|
|
|
+ <div class="area-header">
|
|
|
+ <div class="area-icon" [style.background]="area.color">
|
|
|
+ {{ area.icon }}
|
|
|
+ </div>
|
|
|
+ <div class="area-info">
|
|
|
+ <h6>{{ area.name }}</h6>
|
|
|
+ <span class="area-style">{{ area.styleDescription }}</span>
|
|
|
+ </div>
|
|
|
+ <span class="area-status" [class]="area.status">
|
|
|
+ {{ getStatusText(area.status) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="area-details">
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">责任人:</span>
|
|
|
+ <span class="value">{{ area.assignedTo || '未分配' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">五维验证:</span>
|
|
|
+ <span class="value">{{ getCompletedDimensions(area) }}/5</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="area-actions">
|
|
|
+ <button class="btn-ghost btn-xs" (click)="editArea(area); $event.stopPropagation()">
|
|
|
+ 编辑
|
|
|
+ </button>
|
|
|
+ <button class="btn-ghost btn-xs" (click)="deleteArea(area.id); $event.stopPropagation()">
|
|
|
+ 删除
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**2. 区域数据模型**
|
|
|
+
|
|
|
+```typescript
|
|
|
+export interface Area {
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ icon: string; // emoji或图标
|
|
|
+ color: string; // 区域标识颜色
|
|
|
+ styleDescription: string; // 风格描述
|
|
|
+ status: 'pending' | 'in-progress' | 'completed';
|
|
|
+
|
|
|
+ // 分工
|
|
|
+ assignedTo?: string; // 责任人ID
|
|
|
+ assignedRole?: string; // 角色
|
|
|
+
|
|
|
+ // 五维验证数据
|
|
|
+ dimensionVerifications: {
|
|
|
+ color?: DimensionVerification;
|
|
|
+ form?: DimensionVerification;
|
|
|
+ texture?: DimensionVerification;
|
|
|
+ pattern?: DimensionVerification;
|
|
|
+ lighting?: DimensionVerification;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 特殊需求
|
|
|
+ specialRequirements?: string[];
|
|
|
+
|
|
|
+ // 参考素材(区域专属)
|
|
|
+ dedicatedMaterials?: string[]; // 素材ID列表
|
|
|
+
|
|
|
+ createdAt: Date;
|
|
|
+ updatedAt: Date;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**3. 区域添加/编辑对话框**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="area-dialog-overlay" *ngIf="showAreaDialog">
|
|
|
+ <div class="area-dialog">
|
|
|
+ <div class="dialog-header">
|
|
|
+ <h5>{{ editingArea ? '编辑区域' : '新增区域' }}</h5>
|
|
|
+ <button class="btn-close" (click)="closeAreaDialog()">×</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="dialog-body">
|
|
|
+ <form [formGroup]="areaForm">
|
|
|
+ <!-- 区域名称 -->
|
|
|
+ <div class="form-group">
|
|
|
+ <label>区域名称</label>
|
|
|
+ <input type="text"
|
|
|
+ formControlName="name"
|
|
|
+ placeholder="如:客厅、婴儿房、主卧等">
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 预设选择 -->
|
|
|
+ <div class="form-group">
|
|
|
+ <label>选择预设</label>
|
|
|
+ <div class="preset-grid">
|
|
|
+ <button type="button"
|
|
|
+ class="preset-btn"
|
|
|
+ *ngFor="let preset of areaPresets"
|
|
|
+ [class.selected]="selectedPreset?.id === preset.id"
|
|
|
+ (click)="applyPreset(preset)">
|
|
|
+ <span class="preset-icon">{{ preset.icon }}</span>
|
|
|
+ <span class="preset-name">{{ preset.name }}</span>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 风格描述 -->
|
|
|
+ <div class="form-group">
|
|
|
+ <label>风格描述</label>
|
|
|
+ <textarea formControlName="styleDescription"
|
|
|
+ rows="3"
|
|
|
+ placeholder="如:黑白灰很酷的现代风格 或 色彩更儿童化的温馨空间"></textarea>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 标识颜色 -->
|
|
|
+ <div class="form-group">
|
|
|
+ <label>标识颜色</label>
|
|
|
+ <input type="color" formControlName="color">
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 责任人分配 -->
|
|
|
+ <div class="form-group">
|
|
|
+ <label>责任人</label>
|
|
|
+ <select formControlName="assignedTo">
|
|
|
+ <option value="">-- 未分配 --</option>
|
|
|
+ <option *ngFor="let user of availableUsers" [value]="user.id">
|
|
|
+ {{ user.name }} ({{ user.role }})
|
|
|
+ </option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 特殊需求 -->
|
|
|
+ <div class="form-group">
|
|
|
+ <label>特殊需求</label>
|
|
|
+ <div class="requirement-tags">
|
|
|
+ <input type="text"
|
|
|
+ #requirementInput
|
|
|
+ placeholder="按Enter添加需求标签"
|
|
|
+ (keyup.enter)="addRequirement(requirementInput.value); requirementInput.value = ''">
|
|
|
+ <div class="tags-list">
|
|
|
+ <span class="tag" *ngFor="let req of specialRequirements; let i = index">
|
|
|
+ {{ req }}
|
|
|
+ <button class="remove" (click)="removeRequirement(i)">×</button>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <button class="btn-secondary" (click)="closeAreaDialog()">取消</button>
|
|
|
+ <button class="btn-primary" (click)="saveArea()">保存</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**4. 区域预设配置**
|
|
|
+
|
|
|
+```typescript
|
|
|
+const areaPresets: AreaPreset[] = [
|
|
|
+ {
|
|
|
+ id: 'living-room',
|
|
|
+ name: '客厅',
|
|
|
+ icon: '🛋️',
|
|
|
+ color: '#4A90E2',
|
|
|
+ styleDescription: '现代简约,明亮开阔',
|
|
|
+ defaultRequirements: ['开放式布局', '充足采光', '舒适氛围']
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'baby-room',
|
|
|
+ name: '婴儿房',
|
|
|
+ icon: '👶',
|
|
|
+ color: '#FFB6C1',
|
|
|
+ styleDescription: '色彩丰富,童趣温馨',
|
|
|
+ defaultRequirements: ['安全环保', '柔和色彩', '储物充足', '易于清洁']
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'master-bedroom',
|
|
|
+ name: '主卧',
|
|
|
+ icon: '🛏️',
|
|
|
+ color: '#9B59B6',
|
|
|
+ styleDescription: '宁静优雅,私密舒适',
|
|
|
+ defaultRequirements: ['隔音效果', '柔和灯光', '收纳空间']
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'kitchen',
|
|
|
+ name: '厨房',
|
|
|
+ icon: '🍳',
|
|
|
+ color: '#E67E22',
|
|
|
+ styleDescription: '实用高效,易于清洁',
|
|
|
+ defaultRequirements: ['动线合理', '台面充足', '通风良好', '防油防水']
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'bathroom',
|
|
|
+ name: '卫生间',
|
|
|
+ icon: '🚿',
|
|
|
+ color: '#3498DB',
|
|
|
+ styleDescription: '清新明亮,干湿分离',
|
|
|
+ defaultRequirements: ['防水防滑', '通风除湿', '储物空间']
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'study',
|
|
|
+ name: '书房',
|
|
|
+ icon: '📚',
|
|
|
+ color: '#27AE60',
|
|
|
+ styleDescription: '安静专注,书香氛围',
|
|
|
+ defaultRequirements: ['采光充足', '隔音效果', '书柜充足']
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'dining',
|
|
|
+ name: '餐厅',
|
|
|
+ icon: '🍽️',
|
|
|
+ color: '#F39C12',
|
|
|
+ styleDescription: '温馨舒适,聚餐氛围',
|
|
|
+ defaultRequirements: ['空间适中', '照明充足', '易于清洁']
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'custom',
|
|
|
+ name: '自定义',
|
|
|
+ icon: '✏️',
|
|
|
+ color: '#95A5A6',
|
|
|
+ styleDescription: '',
|
|
|
+ defaultRequirements: []
|
|
|
+ }
|
|
|
+];
|
|
|
+```
|
|
|
+
|
|
|
+**5. 区域切换与对比视图**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="area-comparison-view">
|
|
|
+ <div class="comparison-header">
|
|
|
+ <h5>区域参数对比</h5>
|
|
|
+ <button class="btn-ghost" (click)="exportComparison()">导出对比表</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <table class="comparison-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>维度/区域</th>
|
|
|
+ <th *ngFor="let area of areas">{{ area.name }}</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr>
|
|
|
+ <td>色彩</td>
|
|
|
+ <td *ngFor="let area of areas">
|
|
|
+ <span class="status-badge" [class]="area.dimensionVerifications.color?.status">
|
|
|
+ {{ getStatusText(area.dimensionVerifications.color?.status) }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td>形体</td>
|
|
|
+ <td *ngFor="let area of areas">
|
|
|
+ <span class="status-badge" [class]="area.dimensionVerifications.form?.status">
|
|
|
+ {{ getStatusText(area.dimensionVerifications.form?.status) }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td>质感</td>
|
|
|
+ <td *ngFor="let area of areas">
|
|
|
+ <span class="status-badge" [class]="area.dimensionVerifications.texture?.status">
|
|
|
+ {{ getStatusText(area.dimensionVerifications.texture?.status) }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td>纹理</td>
|
|
|
+ <td *ngFor="let area of areas">
|
|
|
+ <span class="status-badge" [class]="area.dimensionVerifications.pattern?.status">
|
|
|
+ {{ getStatusText(area.dimensionVerifications.pattern?.status) }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td>灯光</td>
|
|
|
+ <td *ngFor="let area of areas">
|
|
|
+ <span class="status-badge" [class]="area.dimensionVerifications.lighting?.status">
|
|
|
+ {{ getStatusText(area.dimensionVerifications.lighting?.status) }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### C5. 分工和人员系统
|
|
|
+
|
|
|
+#### 功能设计
|
|
|
+
|
|
|
+**1. 人员角色定义**
|
|
|
+
|
|
|
+```typescript
|
|
|
+export interface CollaborationUser {
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ role: 'designer' | 'customer-service' | 'team-leader' | 'client';
|
|
|
+ avatar?: string;
|
|
|
+ permissions: {
|
|
|
+ canEditColor: boolean;
|
|
|
+ canEditForm: boolean;
|
|
|
+ canEditTexture: boolean;
|
|
|
+ canEditPattern: boolean;
|
|
|
+ canEditLighting: boolean;
|
|
|
+ canApprove: boolean;
|
|
|
+ };
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**2. 分工面板**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="collaboration-assignment-panel">
|
|
|
+ <h6>分工管理</h6>
|
|
|
+
|
|
|
+ <!-- 按维度分工 -->
|
|
|
+ <div class="dimension-assignments">
|
|
|
+ <div class="assignment-row" *ngFor="let dimension of dimensions">
|
|
|
+ <span class="dimension-name">{{ dimension.name }}</span>
|
|
|
+ <select [(ngModel)]="dimensionAssignments[dimension.key]"
|
|
|
+ (change)="updateAssignment(dimension.key, $event.target.value)">
|
|
|
+ <option value="">-- 未分配 --</option>
|
|
|
+ <option *ngFor="let user of eligibleUsers[dimension.key]"
|
|
|
+ [value]="user.id">
|
|
|
+ {{ user.name }} ({{ user.role }})
|
|
|
+ </option>
|
|
|
+ </select>
|
|
|
+ <span class="assignment-status">
|
|
|
+ {{ getAssignmentStatus(dimension.key) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 按区域分工 -->
|
|
|
+ <div class="area-assignments" *ngIf="areas.length > 0">
|
|
|
+ <h6>区域责任人</h6>
|
|
|
+ <div class="assignment-row" *ngFor="let area of areas">
|
|
|
+ <span class="area-name">{{ area.name }}</span>
|
|
|
+ <select [(ngModel)]="area.assignedTo"
|
|
|
+ (change)="updateAreaAssignment(area.id, $event.target.value)">
|
|
|
+ <option value="">-- 未分配 --</option>
|
|
|
+ <option *ngFor="let user of availableUsers"
|
|
|
+ [value]="user.id">
|
|
|
+ {{ user.name }} ({{ user.role }})
|
|
|
+ </option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 协作状态跟踪 -->
|
|
|
+ <div class="collaboration-tracking">
|
|
|
+ <h6>协作状态</h6>
|
|
|
+ <div class="tracking-grid">
|
|
|
+ <div class="tracking-item" *ngFor="let user of activeUsers">
|
|
|
+ <div class="user-avatar">
|
|
|
+ <img [src]="user.avatar || defaultAvatar" [alt]="user.name">
|
|
|
+ <span class="online-indicator" *ngIf="user.isOnline"></span>
|
|
|
+ </div>
|
|
|
+ <div class="user-info">
|
|
|
+ <span class="name">{{ user.name }}</span>
|
|
|
+ <span class="role">{{ getRoleName(user.role) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="user-tasks">
|
|
|
+ <span class="task-count">{{ getUserTaskCount(user.id) }} 项任务</span>
|
|
|
+ <span class="completion">{{ getUserCompletion(user.id) }}% 完成</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**3. 审批流程**
|
|
|
+
|
|
|
+```html
|
|
|
+<div class="approval-workflow">
|
|
|
+ <h6>审批流程</h6>
|
|
|
+
|
|
|
+ <div class="workflow-steps">
|
|
|
+ <div class="step"
|
|
|
+ *ngFor="let step of approvalSteps; let i = index"
|
|
|
+ [class.completed]="step.status === 'completed'"
|
|
|
+ [class.active]="step.status === 'in-progress'">
|
|
|
+
|
|
|
+ <div class="step-number">{{ i + 1 }}</div>
|
|
|
+ <div class="step-content">
|
|
|
+ <h6>{{ step.title }}</h6>
|
|
|
+ <p>{{ step.description }}</p>
|
|
|
+
|
|
|
+ <div class="step-assignee">
|
|
|
+ <span class="label">审批人:</span>
|
|
|
+ <span class="value">{{ step.assignee?.name || '待分配' }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="step-actions" *ngIf="step.status === 'in-progress' && canApprove(step)">
|
|
|
+ <button class="btn-success btn-sm" (click)="approveStep(step.id)">
|
|
|
+ 通过
|
|
|
+ </button>
|
|
|
+ <button class="btn-danger btn-sm" (click)="rejectStep(step.id)">
|
|
|
+ 驳回
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="step-result" *ngIf="step.status === 'completed'">
|
|
|
+ <span class="result-badge" [class]="step.result">
|
|
|
+ {{ step.result === 'approved' ? '已通过' : '已驳回' }}
|
|
|
+ </span>
|
|
|
+ <span class="result-time">{{ step.completedAt | date:'MM-dd HH:mm' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+```
|
|
|
+
|
|
|
+**4. 审批逻辑**
|
|
|
+
|
|
|
+```typescript
|
|
|
+interface ApprovalStep {
|
|
|
+ id: string;
|
|
|
+ title: string;
|
|
|
+ description: string;
|
|
|
+ assignee?: CollaborationUser;
|
|
|
+ status: 'pending' | 'in-progress' | 'completed';
|
|
|
+ result?: 'approved' | 'rejected';
|
|
|
+ completedAt?: Date;
|
|
|
+ comments?: string;
|
|
|
+}
|
|
|
+
|
|
|
+const approvalSteps: ApprovalStep[] = [
|
|
|
+ {
|
|
|
+ id: 'dimension-verification',
|
|
|
+ title: '五维验证审核',
|
|
|
+ description: '设计师完成五个维度的验证',
|
|
|
+ status: 'pending'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'customer-service-review',
|
|
|
+ title: '客服审核',
|
|
|
+ description: '客服确认需求理解正确',
|
|
|
+ status: 'pending'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'team-leader-approval',
|
|
|
+ title: '组长审批',
|
|
|
+ description: '组长审批整体方案',
|
|
|
+ status: 'pending'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'client-confirmation',
|
|
|
+ title: '客户确认',
|
|
|
+ description: '客户最终确认',
|
|
|
+ status: 'pending'
|
|
|
+ }
|
|
|
+];
|
|
|
+
|
|
|
+approveStep(stepId: string): void {
|
|
|
+ const step = this.approvalSteps.find(s => s.id === stepId);
|
|
|
+ if (step) {
|
|
|
+ step.status = 'completed';
|
|
|
+ step.result = 'approved';
|
|
|
+ step.completedAt = new Date();
|
|
|
+
|
|
|
+ // 自动启动下一步
|
|
|
+ const nextStepIndex = this.approvalSteps.indexOf(step) + 1;
|
|
|
+ if (nextStepIndex < this.approvalSteps.length) {
|
|
|
+ this.approvalSteps[nextStepIndex].status = 'in-progress';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否所有步骤都完成
|
|
|
+ const allApproved = this.approvalSteps.every(s => s.result === 'approved');
|
|
|
+ if (allApproved) {
|
|
|
+ this.onAllStepsApproved();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+rejectStep(stepId: string): void {
|
|
|
+ const step = this.approvalSteps.find(s => s.id === stepId);
|
|
|
+ if (step) {
|
|
|
+ const reason = prompt('请输入驳回原因:');
|
|
|
+ if (reason) {
|
|
|
+ step.status = 'completed';
|
|
|
+ step.result = 'rejected';
|
|
|
+ step.comments = reason;
|
|
|
+ step.completedAt = new Date();
|
|
|
+
|
|
|
+ // 通知相关人员
|
|
|
+ this.notifyRejection(step);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. 技术实施建议
|
|
|
+
|
|
|
+### 需要新增的组件
|
|
|
+
|
|
|
+```
|
|
|
+src/app/shared/components/
|
|
|
+├── color-wheel-visualizer/ # 色轮可视化组件
|
|
|
+│ ├── color-wheel-visualizer.component.ts
|
|
|
+│ ├── color-wheel-visualizer.component.html
|
|
|
+│ └── color-wheel-visualizer.component.scss
|
|
|
+│
|
|
|
+├── dimension-verification/ # 维度验证组件
|
|
|
+│ ├── dimension-verification.component.ts
|
|
|
+│ ├── dimension-verification.component.html
|
|
|
+│ └── dimension-verification.component.scss
|
|
|
+│
|
|
|
+├── annotation-tool/ # 标注工具组件
|
|
|
+│ ├── annotation-tool.component.ts
|
|
|
+│ ├── annotation-tool.component.html
|
|
|
+│ └── annotation-tool.component.scss
|
|
|
+│
|
|
|
+├── area-management/ # 区域管理组件
|
|
|
+│ ├── area-management.component.ts
|
|
|
+│ ├── area-management.component.html
|
|
|
+│ └── area-management.component.scss
|
|
|
+│
|
|
|
+├── furniture-form-selector/ # 软装形体选择器
|
|
|
+│ ├── furniture-form-selector.component.ts
|
|
|
+│ ├── furniture-form-selector.component.html
|
|
|
+│ └── furniture-form-selector.component.scss
|
|
|
+│
|
|
|
+├── texture-comparison/ # 质感对比组件
|
|
|
+│ ├── texture-comparison.component.ts
|
|
|
+│ ├── texture-comparison.component.html
|
|
|
+│ └── texture-comparison.component.scss
|
|
|
+│
|
|
|
+├── pattern-visualizer/ # 纹理可视化组件
|
|
|
+│ ├── pattern-visualizer.component.ts
|
|
|
+│ ├── pattern-visualizer.component.html
|
|
|
+│ └── pattern-visualizer.component.scss
|
|
|
+│
|
|
|
+└── lighting-distribution-chart/ # 光占比图表组件
|
|
|
+ ├── lighting-distribution-chart.component.ts
|
|
|
+ ├── lighting-distribution-chart.component.html
|
|
|
+ └── lighting-distribution-chart.component.scss
|
|
|
+```
|
|
|
+
|
|
|
+### 需要修改的现有文件
|
|
|
+
|
|
|
+**1. requirements-confirm-card.ts**
|
|
|
+- 移除需求映射标签页相关代码
|
|
|
+- 添加五维验证数据结构
|
|
|
+- 添加区域管理逻辑
|
|
|
+- 集成标注工具
|
|
|
+- 实现分工和审批流程
|
|
|
+
|
|
|
+**2. requirements-confirm-card.html**
|
|
|
+- 重构协作验证标签页
|
|
|
+- 增强素材分析展示
|
|
|
+- 添加区域管理UI
|
|
|
+- 集成新组件
|
|
|
+
|
|
|
+**3. requirements-confirm-card.scss**
|
|
|
+- 新增五维验证样式
|
|
|
+- 标注工具样式
|
|
|
+- 区域管理样式
|
|
|
+- 响应式布局优化
|
|
|
+
|
|
|
+### 数据模型调整
|
|
|
+
|
|
|
+**新建接口文件:**
|
|
|
+
|
|
|
+```
|
|
|
+src/app/models/
|
|
|
+├── collaboration-verification.interface.ts # 协作验证相关接口
|
|
|
+├── annotation.interface.ts # 标注相关接口
|
|
|
+├── area-management.interface.ts # 区域管理相关接口
|
|
|
+└── approval-workflow.interface.ts # 审批流程相关接口
|
|
|
+```
|
|
|
+
|
|
|
+### API接口需求
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 需要后端提供的API接口
|
|
|
+
|
|
|
+// 1. 灯光分析增强
|
|
|
+POST /api/analysis/lighting/ratio // 计算光比
|
|
|
+POST /api/analysis/lighting/distribution // 计算光占比
|
|
|
+
|
|
|
+// 2. 标注管理
|
|
|
+POST /api/annotations // 创建标注
|
|
|
+GET /api/annotations/:projectId // 获取项目标注
|
|
|
+PUT /api/annotations/:id // 更新标注
|
|
|
+DELETE /api/annotations/:id // 删除标注
|
|
|
+
|
|
|
+// 3. 区域管理
|
|
|
+POST /api/areas // 创建区域
|
|
|
+GET /api/areas/:projectId // 获取项目区域
|
|
|
+PUT /api/areas/:id // 更新区域
|
|
|
+DELETE /api/areas/:id // 删除区域
|
|
|
+
|
|
|
+// 4. 分工管理
|
|
|
+POST /api/assignments // 分配任务
|
|
|
+GET /api/assignments/:projectId // 获取项目分工
|
|
|
+PUT /api/assignments/:id // 更新分工
|
|
|
+
|
|
|
+// 5. 审批流程
|
|
|
+POST /api/approvals/:stepId/approve // 审批通过
|
|
|
+POST /api/approvals/:stepId/reject // 审批驳回
|
|
|
+GET /api/approvals/:projectId/status // 获取审批状态
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. 实施分阶段建议
|
|
|
+
|
|
|
+### 阶段1:素材分析增强(预计5-7天)
|
|
|
+
|
|
|
+**优先级:高**
|
|
|
+
|
|
|
+**任务清单:**
|
|
|
+- [ ] 创建色轮可视化组件
|
|
|
+- [ ] 实现软装形体选择器
|
|
|
+- [ ] 优化质感对比展示
|
|
|
+- [ ] 增强纹理图案可视化
|
|
|
+- [ ] 补充灯光分析(光比、光占比)
|
|
|
+- [ ] 实现全展示模式切换
|
|
|
+- [ ] 添加分析报告导出功能
|
|
|
+
|
|
|
+**验收标准:**
|
|
|
+- 色轮能正确显示色彩分布
|
|
|
+- 软装形体可选择并保存
|
|
|
+- 质感亮光/哑光对比清晰
|
|
|
+- 灯光分析包含光比和光占比数据
|
|
|
+- 全展示模式能展开所有分析细节
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 阶段2:需求映射隐藏(预计1-2天)
|
|
|
+
|
|
|
+**优先级:中**
|
|
|
+
|
|
|
+**任务清单:**
|
|
|
+- [ ] 移除需求映射标签页UI
|
|
|
+- [ ] 实现后台自动触发机制
|
|
|
+- [ ] 保留映射数据接口
|
|
|
+- [ ] 测试自动映射功能
|
|
|
+- [ ] 添加开发模式可见性(可选)
|
|
|
+
|
|
|
+**验收标准:**
|
|
|
+- 需求映射标签页不可见
|
|
|
+- 素材分析完成后自动生成映射
|
|
|
+- 映射数据正确传递给父组件
|
|
|
+- 不影响方案确认阶段的显示
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 阶段3:协作验证重构(预计10-14天)
|
|
|
+
|
|
|
+**优先级:高(核心)**
|
|
|
+
|
|
|
+**子阶段3.1:五维验证模块(3-4天)**
|
|
|
+- [ ] 创建维度验证数据模型
|
|
|
+- [ ] 实现五维验证UI布局
|
|
|
+- [ ] 创建dimension-verification组件
|
|
|
+- [ ] 实现维度切换逻辑
|
|
|
+- [ ] 添加验证状态管理
|
|
|
+
|
|
|
+**子阶段3.2:素材关联系统(2-3天)**
|
|
|
+- [ ] 实现拖拽关联功能(使用CDK Drag&Drop)
|
|
|
+- [ ] 实现下拉选择关联
|
|
|
+- [ ] 添加相关度计算
|
|
|
+- [ ] 实现素材高亮显示
|
|
|
+
|
|
|
+**子阶段3.3:标注工具集成(3-4天)**
|
|
|
+- [ ] 创建annotation-tool组件
|
|
|
+- [ ] 实现SVG标注层
|
|
|
+- [ ] 支持文字、箭头、高亮、圆圈、矩形标注
|
|
|
+- [ ] 实现标注保存和加载
|
|
|
+- [ ] 添加标注列表管理
|
|
|
+
|
|
|
+**子阶段3.4:区域细分管理(2-3天)**
|
|
|
+- [ ] 创建area-management组件
|
|
|
+- [ ] 实现区域添加/编辑对话框
|
|
|
+- [ ] 配置区域预设
|
|
|
+- [ ] 实现区域切换逻辑
|
|
|
+- [ ] 添加区域对比视图
|
|
|
+
|
|
|
+**子阶段3.5:分工和人员系统(2-3天)**
|
|
|
+- [ ] 定义用户角色和权限
|
|
|
+- [ ] 实现分工管理面板
|
|
|
+- [ ] 实现协作状态跟踪
|
|
|
+- [ ] 实现审批流程
|
|
|
+- [ ] 添加通知机制
|
|
|
+
|
|
|
+**验收标准:**
|
|
|
+- 五个维度可独立验证
|
|
|
+- 素材能正确关联到各维度
|
|
|
+- 标注工具功能完整且流畅
|
|
|
+- 区域可添加、编辑、删除
|
|
|
+- 分工和审批流程正常运行
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. 验收标准
|
|
|
+
|
|
|
+### 功能完整性验收
|
|
|
+
|
|
|
+**素材分析模块:**
|
|
|
+- ✅ 所有五个维度(色彩、形体、质感、纹理、灯光)都有完整的分析数据
|
|
|
+- ✅ 色轮可视化正确展示色彩分布
|
|
|
+- ✅ 灯光分析包含光比和光占比
|
|
|
+- ✅ 可切换精简视图和完整展示模式
|
|
|
+- ✅ 可导出分析报告(PDF/Excel/JSON)
|
|
|
+
|
|
|
+**协作验证模块:**
|
|
|
+- ✅ 五维验证模块独立可操作
|
|
|
+- ✅ 素材能通过拖拽或选择关联到维度
|
|
|
+- ✅ 标注工具支持5种标注类型
|
|
|
+- ✅ 区域可自由添加和管理
|
|
|
+- ✅ 分工分配正常,审批流程完整
|
|
|
+
|
|
|
+**系统集成:**
|
|
|
+- ✅ 需求映射在后台自动生成
|
|
|
+- ✅ 数据正确传递给父组件
|
|
|
+- ✅ 不影响其他模块功能
|
|
|
+
|
|
|
+### 用户体验标准
|
|
|
+
|
|
|
+**交互体验:**
|
|
|
+- 响应时间 < 200ms(标注工具除外)
|
|
|
+- 拖拽操作流畅无卡顿
|
|
|
+- 标注绘制实时反馈
|
|
|
+- 表单验证及时提示
|
|
|
+
|
|
|
+**视觉设计:**
|
|
|
+- UI风格与整体系统一致
|
|
|
+- 色彩搭配和谐
|
|
|
+- 图标清晰易识别
|
|
|
+- 布局合理不拥挤
|
|
|
+
|
|
|
+**易用性:**
|
|
|
+- 操作步骤明确,不超过3步
|
|
|
+- 提供操作提示和帮助文档
|
|
|
+- 支持键盘快捷键(标注工具)
|
|
|
+- 错误提示友好明确
|
|
|
+
|
|
|
+**性能要求:**
|
|
|
+- 素材列表加载 < 1s
|
|
|
+- 色轮渲染 < 500ms
|
|
|
+- 标注保存 < 300ms
|
|
|
+- 区域切换 < 200ms
|
|
|
+
|
|
|
+### 数据准确性
|
|
|
+
|
|
|
+- ✅ 分析数据与实际图片内容相符
|
|
|
+- ✅ 光比计算误差 < 10%
|
|
|
+- ✅ 色彩提取准确率 > 90%
|
|
|
+- ✅ 关联关系正确保存和加载
|
|
|
+
|
|
|
+### 兼容性
|
|
|
+
|
|
|
+- ✅ 支持Chrome、Edge、Firefox最新版本
|
|
|
+- ✅ 响应式设计适配1366px及以上分辨率
|
|
|
+- ✅ 支持触摸屏操作(标注工具)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. 风险评估与建议
|
|
|
+
|
|
|
+### 潜在风险
|
|
|
+
|
|
|
+**技术风险:**
|
|
|
+1. **色轮可视化性能** - 大量颜色数据可能影响渲染性能
|
|
|
+ - 建议:使用Canvas替代SVG,实现虚拟滚动
|
|
|
+
|
|
|
+2. **标注工具复杂度** - SVG操作和状态管理复杂
|
|
|
+ - 建议:使用成熟的库如Fabric.js或Konva.js
|
|
|
+
|
|
|
+3. **拖拽关联稳定性** - CDK Drag&Drop可能有兼容性问题
|
|
|
+ - 建议:充分测试,准备降级方案
|
|
|
+
|
|
|
+**业务风险:**
|
|
|
+1. **区域细分粒度** - 过细可能导致操作复杂
|
|
|
+ - 建议:限制最大区域数量(如10个)
|
|
|
+
|
|
|
+2. **审批流程僵化** - 固定流程可能不适合所有项目
|
|
|
+ - 建议:支持自定义审批流程
|
|
|
+
|
|
|
+### 优化建议
|
|
|
+
|
|
|
+**性能优化:**
|
|
|
+- 使用虚拟滚动加载大量素材
|
|
|
+- 图片懒加载和预加载策略
|
|
|
+- 标注数据分页加载
|
|
|
+- 实施增量更新策略
|
|
|
+
|
|
|
+**用户体验优化:**
|
|
|
+- 添加操作引导(首次使用)
|
|
|
+- 提供快捷键支持
|
|
|
+- 实现撤销/重做功能
|
|
|
+- 添加实时保存提示
|
|
|
+
|
|
|
+**扩展性建议:**
|
|
|
+- 标注工具支持插件扩展
|
|
|
+- 区域类型可配置
|
|
|
+- 审批流程可自定义
|
|
|
+- 支持第三方集成(如AI分析)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. 总结
|
|
|
+
|
|
|
+本优化方案围绕三个核心目标:
|
|
|
+
|
|
|
+1. **素材分析完全展示** - 通过增强色彩、形体、质感、纹理、灯光五个维度的可视化和数据完整性,确保所有采集的参考信息都能充分展示。
|
|
|
+
|
|
|
+2. **需求映射后台化** - 将用户不需要直接操作的需求映射功能隐藏,改为自动触发,简化用户流程。
|
|
|
+
|
|
|
+3. **协作验证深度重构** - 这是最核心的改进,通过五维验证、素材关联、标注工具、区域细分、分工管理等功能,实现真正的协作式需求验证。
|
|
|
+
|
|
|
+实施建议采用分阶段推进,优先完成素材分析增强和协作验证重构,这两个是高优先级且用户价值最大的功能。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 附录
|
|
|
+
|
|
|
+### A. 相关技术栈
|
|
|
+
|
|
|
+- **Angular**: v16+
|
|
|
+- **Angular CDK**: Drag&Drop模块
|
|
|
+- **D3.js**: 色轮可视化
|
|
|
+- **ECharts**: 图表绘制
|
|
|
+- **Fabric.js/Konva.js**: 标注工具(可选)
|
|
|
+- **RxJS**: 响应式数据流
|
|
|
+
|
|
|
+### B. 参考资源
|
|
|
+
|
|
|
+- [Angular CDK Drag and Drop](https://material.angular.io/cdk/drag-drop/overview)
|
|
|
+- [D3.js Color Wheel Tutorial](https://observablehq.com/@d3/color-wheel)
|
|
|
+- [ECharts Pie Chart](https://echarts.apache.org/examples/en/editor.html?c=pie-simple)
|
|
|
+- [Fabric.js Documentation](http://fabricjs.com/docs/)
|
|
|
+
|
|
|
+### C. 联系与支持
|
|
|
+
|
|
|
+如有疑问或需要进一步讨论,请联系:
|
|
|
+- 项目负责人:[待填写]
|
|
|
+- 技术负责人:[待填写]
|
|
|
+- 设计负责人:[待填写]
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+**文档版本**: v1.0
|
|
|
+**创建日期**: {{ currentDate }}
|
|
|
+**最后更新**: {{ currentDate }}
|
|
|
+**状态**: 待审核
|
|
|
+
|