test-requirement-mapping.component.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <div class="test-requirement-mapping">
  2. <div class="test-header">
  3. <h2>需求映射功能完整工作流程测试</h2>
  4. <p class="test-description">
  5. 测试从图片上传到生成需求映射的完整流程,包括图片分析、参数映射和氛围预览生成
  6. </p>
  7. <button class="reset-btn" (click)="resetTest()">重置测试</button>
  8. </div>
  9. <!-- 测试步骤进度 -->
  10. <div class="test-progress">
  11. <h3>测试进度</h3>
  12. <div class="steps-container">
  13. @for (step of testSteps; track step.id) {
  14. <div class="step-item" [class]="getStepClass(step.status)">
  15. <div class="step-icon">{{ getStepIcon(step.status) }}</div>
  16. <div class="step-info">
  17. <div class="step-name">{{ step.name }}</div>
  18. <div class="step-status">
  19. @switch (step.status) {
  20. @case ('pending') { 等待中 }
  21. @case ('in-progress') { 进行中... }
  22. @case ('completed') { 已完成 }
  23. @case ('error') { 失败 }
  24. }
  25. </div>
  26. </div>
  27. </div>
  28. }
  29. </div>
  30. </div>
  31. <!-- 文件上传区域 -->
  32. <div class="upload-section">
  33. <h3>1. 图片上传</h3>
  34. <div class="upload-area" [class.uploading]="isUploading">
  35. @if (uploadedFiles.length === 0) {
  36. <div class="upload-dropzone">
  37. <div class="upload-icon">📁</div>
  38. <div class="upload-text">选择图片文件进行测试</div>
  39. <div class="upload-hint">支持 JPG、PNG 格式,可选择多张图片</div>
  40. <input type="file"
  41. accept="image/*"
  42. multiple
  43. (change)="onFileSelected($event)"
  44. class="file-input">
  45. </div>
  46. } @else {
  47. <div class="uploaded-files">
  48. <h4>已上传文件 ({{ uploadedFiles.length }})</h4>
  49. <div class="files-grid">
  50. @for (file of uploadedFiles; track file.id) {
  51. <div class="file-item">
  52. <img [src]="file.preview || file.url" [alt]="file.name" class="file-preview">
  53. <div class="file-info">
  54. <div class="file-name">{{ file.name }}</div>
  55. <div class="file-size">{{ (file.size! / 1024 / 1024).toFixed(2) }} MB</div>
  56. </div>
  57. </div>
  58. }
  59. </div>
  60. </div>
  61. }
  62. @if (isUploading) {
  63. <div class="loading-overlay">
  64. <div class="loading-spinner"></div>
  65. <div class="loading-text">正在上传文件...</div>
  66. </div>
  67. }
  68. </div>
  69. </div>
  70. <!-- 分析结果区域 -->
  71. <div class="analysis-section" [class.disabled]="uploadedFiles.length === 0">
  72. <h3>2. 图片分析</h3>
  73. @if (isAnalyzing) {
  74. <div class="analysis-loading">
  75. <div class="loading-spinner"></div>
  76. <div class="loading-text">
  77. <h4>正在分析图片...</h4>
  78. <p>系统正在进行色彩、纹理、形态、图案和灯光分析</p>
  79. </div>
  80. </div>
  81. } @else if (analysisError) {
  82. <div class="analysis-error">
  83. <div class="error-icon">❌</div>
  84. <div class="error-text">
  85. <h4>分析失败</h4>
  86. <p>{{ analysisError }}</p>
  87. <button class="retry-btn" (click)="retryAnalysis()">重新分析</button>
  88. </div>
  89. </div>
  90. } @else if (analysisResult) {
  91. <div class="analysis-result">
  92. <h4>分析完成 ✅</h4>
  93. <div class="analysis-summary">
  94. <div class="summary-item">
  95. <span class="label">主要颜色:</span>
  96. <span class="value">{{ analysisResult.enhancedAnalysis?.colorWheel?.colorDistribution?.length || 0 }} 种</span>
  97. </div>
  98. <div class="summary-item">
  99. <span class="label">材质类型:</span>
  100. <span class="value">{{ analysisResult.textureAnalysis?.materialClassification?.primary || '未识别' }}</span>
  101. </div>
  102. <div class="summary-item">
  103. <span class="label">灯光情绪:</span>
  104. <span class="value">{{ analysisResult.lightingAnalysis?.ambientAnalysis?.lightingMood || '未识别' }}</span>
  105. </div>
  106. <div class="summary-item">
  107. <span class="label">空间形态:</span>
  108. <span class="value">{{ analysisResult.formAnalysis?.spaceAnalysis?.spaceType || '未识别' }}</span>
  109. </div>
  110. </div>
  111. </div>
  112. } @else {
  113. <div class="analysis-placeholder">
  114. <div class="placeholder-icon">🔍</div>
  115. <div class="placeholder-text">
  116. <h4>等待分析</h4>
  117. <p>请先上传图片,系统将自动开始分析</p>
  118. </div>
  119. </div>
  120. }
  121. </div>
  122. <!-- 需求映射结果区域 -->
  123. <div class="mapping-section" [class.disabled]="!analysisResult">
  124. <h3>3. 需求映射生成</h3>
  125. @if (isGeneratingMapping) {
  126. <div class="mapping-loading">
  127. <div class="loading-spinner"></div>
  128. <div class="loading-text">
  129. <h4>正在生成需求映射...</h4>
  130. <p>基于分析结果生成场景参数和映射关系</p>
  131. </div>
  132. </div>
  133. } @else if (mappingError) {
  134. <div class="mapping-error">
  135. <div class="error-icon">❌</div>
  136. <div class="error-text">
  137. <h4>映射生成失败</h4>
  138. <p>{{ mappingError }}</p>
  139. <button class="retry-btn" (click)="retryMapping()">重新生成</button>
  140. </div>
  141. </div>
  142. } @else if (requirementMapping) {
  143. <div class="mapping-result">
  144. <h4>需求映射生成完成 ✅</h4>
  145. <!-- 场景生成信息 -->
  146. <div class="mapping-section-item">
  147. <h5>场景生成</h5>
  148. <div class="scene-info">
  149. <div class="info-row">
  150. <span class="label">基础场景:</span>
  151. <span class="value">{{ requirementMapping.sceneGeneration.baseScene }}</span>
  152. </div>
  153. @if (requirementMapping.sceneGeneration.atmospherePreview) {
  154. <div class="atmosphere-preview">
  155. <img [src]="requirementMapping.sceneGeneration.atmospherePreview"
  156. alt="氛围感预览图"
  157. class="preview-image">
  158. <div class="preview-label">氛围感预览图</div>
  159. </div>
  160. }
  161. </div>
  162. </div>
  163. <!-- 参数映射信息 -->
  164. <div class="mapping-section-item">
  165. <h5>参数映射</h5>
  166. <div class="params-grid">
  167. <!-- 颜色参数 -->
  168. <div class="param-group">
  169. <h6>颜色映射</h6>
  170. <div class="color-params">
  171. <div class="param-item">
  172. <span class="label">主要颜色:</span>
  173. <span class="value">{{ requirementMapping.parameterMapping.colorParams.primaryColors.length }} 种</span>
  174. </div>
  175. <div class="param-item">
  176. <span class="label">色彩和谐:</span>
  177. <span class="value">{{ requirementMapping.parameterMapping.colorParams.colorHarmony }}</span>
  178. </div>
  179. <div class="param-item">
  180. <span class="label">饱和度:</span>
  181. <span class="value">{{ requirementMapping.parameterMapping.colorParams.saturation }}%</span>
  182. </div>
  183. <div class="param-item">
  184. <span class="label">亮度:</span>
  185. <span class="value">{{ requirementMapping.parameterMapping.colorParams.brightness }}%</span>
  186. </div>
  187. </div>
  188. </div>
  189. <!-- 空间参数 -->
  190. <div class="param-group">
  191. <h6>空间映射</h6>
  192. <div class="space-params">
  193. <div class="param-item">
  194. <span class="label">布局类型:</span>
  195. <span class="value">{{ requirementMapping.parameterMapping.spaceParams.layout.type }}</span>
  196. </div>
  197. <div class="param-item">
  198. <span class="label">空间流线:</span>
  199. <span class="value">{{ requirementMapping.parameterMapping.spaceParams.layout.flow }}</span>
  200. </div>
  201. <div class="param-item">
  202. <span class="label">家具比例:</span>
  203. <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale.furniture }}%</span>
  204. </div>
  205. <div class="param-item">
  206. <span class="label">开放度:</span>
  207. <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale.openness }}%</span>
  208. </div>
  209. </div>
  210. </div>
  211. <!-- 材质参数 -->
  212. <div class="param-group">
  213. <h6>材质映射</h6>
  214. <div class="material-params">
  215. <div class="param-item">
  216. <span class="label">纹理缩放:</span>
  217. <span class="value">{{ requirementMapping.parameterMapping.materialParams.textureScale }}%</span>
  218. </div>
  219. <div class="param-item">
  220. <span class="label">反射率:</span>
  221. <span class="value">{{ requirementMapping.parameterMapping.materialParams.reflectivity }}%</span>
  222. </div>
  223. <div class="param-item">
  224. <span class="label">粗糙度:</span>
  225. <span class="value">{{ requirementMapping.parameterMapping.materialParams.roughness }}%</span>
  226. </div>
  227. <div class="param-item">
  228. <span class="label">金属度:</span>
  229. <span class="value">{{ requirementMapping.parameterMapping.materialParams.metallic }}%</span>
  230. </div>
  231. </div>
  232. </div>
  233. </div>
  234. </div>
  235. <!-- 测试结果下载 -->
  236. <div class="test-actions">
  237. <button class="download-btn" (click)="downloadTestResult()">
  238. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  239. <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
  240. <polyline points="7,10 12,15 17,10"></polyline>
  241. <line x1="12" y1="15" x2="12" y2="3"></line>
  242. </svg>
  243. 下载测试结果
  244. </button>
  245. </div>
  246. </div>
  247. } @else {
  248. <div class="mapping-placeholder">
  249. <div class="placeholder-icon">🎯</div>
  250. <div class="placeholder-text">
  251. <h4>等待映射生成</h4>
  252. <p>请先完成图片分析,系统将自动生成需求映射</p>
  253. </div>
  254. </div>
  255. }
  256. </div>
  257. <!-- 上传成功模态框 -->
  258. @if (showUploadModal) {
  259. <app-upload-success-modal
  260. [isVisible]="showUploadModal"
  261. [uploadedFiles]="uploadedFiles"
  262. [uploadType]="'image'"
  263. [analysisResult]="analysisResult"
  264. [isAnalyzing]="isAnalyzing"
  265. (closeModal)="onCloseModal()"
  266. (analyzeColors)="onAnalyzeColors($event)"
  267. (viewReport)="onViewReport($event)"
  268. (generateRequirementMapping)="onGenerateRequirementMapping($event)">
  269. </app-upload-success-modal>
  270. }
  271. </div>