test-requirement-mapping.component.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import { Component, OnInit, OnDestroy } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. import { Subscription } from 'rxjs';
  5. import { RequirementMappingService } from '../../services/requirement-mapping.service';
  6. import { ColorAnalysisService, ColorAnalysisResult } from '../../shared/services/color-analysis.service';
  7. import { RequirementMapping, SceneTemplate } from '../../models/requirement-mapping.interface';
  8. import { UploadSuccessModalComponent, UploadedFile } from '../../shared/components/upload-success-modal/upload-success-modal.component';
  9. @Component({
  10. selector: 'app-test-requirement-mapping',
  11. standalone: true,
  12. imports: [CommonModule, FormsModule, UploadSuccessModalComponent],
  13. templateUrl: './test-requirement-mapping.component.html',
  14. styleUrls: ['./test-requirement-mapping.component.scss']
  15. })
  16. export class TestRequirementMappingComponent implements OnInit, OnDestroy {
  17. // 测试状态
  18. isUploading = false;
  19. isAnalyzing = false;
  20. isGeneratingMapping = false;
  21. // 上传的文件
  22. uploadedFiles: UploadedFile[] = [];
  23. // 分析结果
  24. analysisResult: ColorAnalysisResult | undefined = undefined;
  25. analysisError: string | null = null;
  26. // 需求映射结果
  27. requirementMapping: RequirementMapping | null = null;
  28. mappingError: string | null = null;
  29. // 模态框状态
  30. showUploadModal = false;
  31. // 测试步骤状态
  32. testSteps = [
  33. { id: 'upload', name: '图片上传', status: 'pending' as 'pending' | 'in-progress' | 'completed' | 'error' },
  34. { id: 'analysis', name: '图片分析', status: 'pending' as 'pending' | 'in-progress' | 'completed' | 'error' },
  35. { id: 'mapping', name: '需求映射', status: 'pending' as 'pending' | 'in-progress' | 'completed' | 'error' },
  36. { id: 'preview', name: '氛围预览', status: 'pending' as 'pending' | 'in-progress' | 'completed' | 'error' }
  37. ];
  38. private subscriptions: Subscription[] = [];
  39. constructor(
  40. private requirementMappingService: RequirementMappingService,
  41. private colorAnalysisService: ColorAnalysisService
  42. ) {}
  43. ngOnInit(): void {
  44. this.resetTest();
  45. }
  46. ngOnDestroy(): void {
  47. this.subscriptions.forEach(sub => sub.unsubscribe());
  48. // 清理对象URL
  49. this.uploadedFiles.forEach(file => {
  50. if (file.url.startsWith('blob:')) {
  51. URL.revokeObjectURL(file.url);
  52. }
  53. });
  54. }
  55. // 重置测试
  56. resetTest(): void {
  57. this.isUploading = false;
  58. this.isAnalyzing = false;
  59. this.isGeneratingMapping = false;
  60. this.uploadedFiles = [];
  61. this.analysisResult = undefined;
  62. this.analysisError = null;
  63. this.requirementMapping = null;
  64. this.mappingError = null;
  65. this.showUploadModal = false;
  66. this.testSteps.forEach(step => {
  67. step.status = 'pending';
  68. });
  69. }
  70. // 文件上传处理
  71. onFileSelected(event: Event): void {
  72. const input = event.target as HTMLInputElement;
  73. if (!input.files || input.files.length === 0) return;
  74. this.updateStepStatus('upload', 'in-progress');
  75. this.isUploading = true;
  76. try {
  77. const files = Array.from(input.files);
  78. this.uploadedFiles = files.map(file => ({
  79. id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
  80. name: file.name,
  81. url: URL.createObjectURL(file),
  82. size: file.size,
  83. type: 'image' as const,
  84. preview: URL.createObjectURL(file)
  85. }));
  86. // 模拟上传延迟
  87. setTimeout(() => {
  88. this.isUploading = false;
  89. this.updateStepStatus('upload', 'completed');
  90. this.showUploadModal = true;
  91. }, 1000);
  92. } catch (error) {
  93. console.error('文件上传失败:', error);
  94. this.isUploading = false;
  95. this.updateStepStatus('upload', 'error');
  96. }
  97. }
  98. // 开始分析
  99. startAnalysis(): void {
  100. if (this.uploadedFiles.length === 0) return;
  101. this.updateStepStatus('analysis', 'in-progress');
  102. this.isAnalyzing = true;
  103. this.analysisError = null;
  104. // 使用第一个文件进行分析
  105. const firstFile = this.uploadedFiles[0];
  106. // 添加null检查和错误处理
  107. if (!firstFile) {
  108. this.analysisError = '未找到有效的文件';
  109. this.isAnalyzing = false;
  110. this.updateStepStatus('analysis', 'error');
  111. return;
  112. }
  113. try {
  114. const analysisSubscription = this.colorAnalysisService.analyzeImage(firstFile).subscribe({
  115. next: (result: ColorAnalysisResult) => {
  116. if (result) {
  117. this.analysisResult = result;
  118. this.isAnalyzing = false;
  119. this.updateStepStatus('analysis', 'completed');
  120. // 自动开始需求映射
  121. this.startRequirementMapping();
  122. } else {
  123. this.analysisError = '分析结果为空';
  124. this.isAnalyzing = false;
  125. this.updateStepStatus('analysis', 'error');
  126. }
  127. },
  128. error: (error: any) => {
  129. console.error('分析失败:', error);
  130. this.analysisError = '图片分析失败,请重试';
  131. this.isAnalyzing = false;
  132. this.updateStepStatus('analysis', 'error');
  133. }
  134. });
  135. this.subscriptions.push(analysisSubscription);
  136. } catch (error) {
  137. console.error('启动分析失败:', error);
  138. this.analysisError = '启动分析失败,请重试';
  139. this.isAnalyzing = false;
  140. this.updateStepStatus('analysis', 'error');
  141. }
  142. }
  143. // 开始需求映射
  144. startRequirementMapping(): void {
  145. if (!this.analysisResult) {
  146. console.warn('分析结果为空,无法开始需求映射');
  147. return;
  148. }
  149. this.updateStepStatus('mapping', 'in-progress');
  150. this.isGeneratingMapping = true;
  151. this.mappingError = null;
  152. try {
  153. const mappingSubscription = this.requirementMappingService.generateRequirementMapping(
  154. this.analysisResult,
  155. SceneTemplate.LIVING_ROOM_MODERN
  156. ).subscribe({
  157. next: (mapping) => {
  158. if (mapping) {
  159. this.requirementMapping = mapping;
  160. this.isGeneratingMapping = false;
  161. this.updateStepStatus('mapping', 'completed');
  162. this.updateStepStatus('preview', 'completed');
  163. console.log('=== 需求映射测试完成 ===');
  164. console.log('映射结果:', this.requirementMapping);
  165. } else {
  166. this.mappingError = '需求映射结果为空';
  167. this.isGeneratingMapping = false;
  168. this.updateStepStatus('mapping', 'error');
  169. }
  170. },
  171. error: (error) => {
  172. console.error('需求映射生成失败:', error);
  173. this.mappingError = '需求映射生成失败,请重试';
  174. this.isGeneratingMapping = false;
  175. this.updateStepStatus('mapping', 'error');
  176. }
  177. });
  178. this.subscriptions.push(mappingSubscription);
  179. } catch (error) {
  180. console.error('启动需求映射失败:', error);
  181. this.mappingError = '启动需求映射失败,请重试';
  182. this.isGeneratingMapping = false;
  183. this.updateStepStatus('mapping', 'error');
  184. }
  185. }
  186. // 更新步骤状态
  187. private updateStepStatus(stepId: string, status: 'pending' | 'in-progress' | 'completed' | 'error'): void {
  188. const step = this.testSteps.find(s => s.id === stepId);
  189. if (step) {
  190. step.status = status;
  191. } else {
  192. console.warn(`未找到步骤: ${stepId}`);
  193. }
  194. }
  195. // 辅助方法:获取步骤图标
  196. getStepIcon(status: string): string {
  197. switch (status) {
  198. case 'completed': return '✅';
  199. case 'in-progress': return '⏳';
  200. case 'error': return '❌';
  201. default: return '⭕';
  202. }
  203. }
  204. // 辅助方法:从HSL值生成颜色字符串
  205. getColorFromHSL(hue: number, saturation: number, brightness: number): string {
  206. return `hsl(${hue}, ${saturation}%, ${brightness}%)`;
  207. }
  208. // 获取步骤状态类名
  209. getStepClass(status: string): string {
  210. return `step-${status}`;
  211. }
  212. // 模态框事件处理
  213. onCloseModal(): void {
  214. this.showUploadModal = false;
  215. }
  216. onAnalyzeColors(files: UploadedFile[]): void {
  217. this.startAnalysis();
  218. }
  219. onViewReport(result: ColorAnalysisResult): void {
  220. console.log('查看报告:', result);
  221. }
  222. onGenerateRequirementMapping(mapping: RequirementMapping): void {
  223. console.log('需求映射生成完成:', mapping);
  224. }
  225. // 手动重试分析
  226. retryAnalysis(): void {
  227. this.analysisError = null;
  228. this.startAnalysis();
  229. }
  230. // 手动重试映射
  231. retryMapping(): void {
  232. this.mappingError = null;
  233. this.startRequirementMapping();
  234. }
  235. // 下载测试结果
  236. downloadTestResult(): void {
  237. if (!this.requirementMapping) return;
  238. const testResult = {
  239. timestamp: new Date().toISOString(),
  240. uploadedFiles: this.uploadedFiles.map(f => ({
  241. name: f.name,
  242. size: f.size,
  243. type: f.type
  244. })),
  245. analysisResult: this.analysisResult,
  246. requirementMapping: this.requirementMapping,
  247. testSteps: this.testSteps
  248. };
  249. const blob = new Blob([JSON.stringify(testResult, null, 2)], { type: 'application/json' });
  250. const url = URL.createObjectURL(blob);
  251. const link = document.createElement('a');
  252. link.href = url;
  253. link.download = `requirement-mapping-test-${Date.now()}.json`;
  254. link.click();
  255. URL.revokeObjectURL(url);
  256. }
  257. }