upload-success-modal.component.ts 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414
  1. import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, SimpleChanges, ChangeDetectionStrategy, HostListener } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { ColorAnalysisService, AnalysisProgress, ColorAnalysisResult } from '../../services/color-analysis.service';
  4. import { FormAnalysisService } from '../../services/form-analysis.service';
  5. import { TextureAnalysisService } from '../../services/texture-analysis.service';
  6. import { PatternAnalysisService } from '../../services/pattern-analysis.service';
  7. import { LightingAnalysisService } from '../../services/lighting-analysis.service';
  8. import { RequirementMappingService } from '../../../services/requirement-mapping.service';
  9. import { AtmospherePreviewService } from '../../../services/atmosphere-preview.service';
  10. import { RequirementMapping, SceneTemplate } from '../../../models/requirement-mapping.interface';
  11. import { Subscription } from 'rxjs';
  12. import { modalAnimations } from './upload-success-modal.animations';
  13. export interface UploadedFile {
  14. id: string;
  15. name: string;
  16. url: string;
  17. size?: number;
  18. type?: 'image' | 'cad' | 'text';
  19. preview?: string;
  20. }
  21. export interface ColorInfo {
  22. hex: string;
  23. rgb: { r: number; g: number; b: number };
  24. percentage: number;
  25. name?: string; // 颜色名称,如"深蓝色"、"暖白色"等
  26. }
  27. @Component({
  28. selector: 'app-upload-success-modal',
  29. standalone: true,
  30. imports: [CommonModule],
  31. templateUrl: './upload-success-modal.component.html',
  32. styleUrls: ['./upload-success-modal.component.scss'],
  33. // changeDetection: ChangeDetectionStrategy.OnPush, // 暂时禁用OnPush以解决交互问题
  34. animations: modalAnimations
  35. })
  36. export class UploadSuccessModalComponent implements OnInit, OnDestroy, OnChanges {
  37. @Input() isVisible: boolean = false;
  38. @Input() uploadedFiles: UploadedFile[] = [];
  39. @Input() uploadType: 'image' | 'document' | 'mixed' = 'image';
  40. @Input() analysisResult?: ColorAnalysisResult;
  41. @Input() isAnalyzing: boolean = false; // 新增:从父组件接收分析状态
  42. @Output() closeModal = new EventEmitter<void>();
  43. @Output() analyzeColors = new EventEmitter<UploadedFile[]>();
  44. @Output() viewReport = new EventEmitter<ColorAnalysisResult>();
  45. @Output() generateRequirementMapping = new EventEmitter<RequirementMapping>(); // 新增:需求映射生成事件
  46. // 移除本地的isAnalyzing属性,使用@Input()的isAnalyzing
  47. // isAnalyzing = false; // 已移除,现在从父组件接收
  48. analysisProgress: AnalysisProgress | null = null;
  49. analysisError: string | null = null;
  50. // 需求映射相关状态
  51. requirementMapping: RequirementMapping | null = null;
  52. isGeneratingMapping = false;
  53. mappingError: string | null = null;
  54. // 增强分析标签页状态
  55. activeTab: 'color' | 'form' | 'texture' | 'pattern' | 'lighting' | 'mapping' = 'color';
  56. // 响应式状态
  57. isMobile = false;
  58. isTablet = false;
  59. // 动画状态
  60. animationState = 'idle';
  61. buttonHoverState = 'normal';
  62. copySuccess = false; // 复制成功状态
  63. // 暴露Math对象给模板使用
  64. Math = Math;
  65. private progressSubscription?: Subscription;
  66. private resizeSubscription?: Subscription;
  67. constructor(
  68. private colorAnalysisService: ColorAnalysisService,
  69. private formAnalysisService: FormAnalysisService,
  70. private textureAnalysisService: TextureAnalysisService,
  71. private patternAnalysisService: PatternAnalysisService,
  72. private lightingAnalysisService: LightingAnalysisService,
  73. private requirementMappingService: RequirementMappingService,
  74. private atmospherePreviewService: AtmospherePreviewService
  75. ) {}
  76. ngOnInit() {
  77. this.checkScreenSize();
  78. this.setupResizeListener();
  79. }
  80. ngOnChanges(changes: SimpleChanges) {
  81. if (changes['isVisible']) {
  82. console.log('📢 弹窗 isVisible 变化:', {
  83. previous: changes['isVisible'].previousValue,
  84. current: changes['isVisible'].currentValue,
  85. firstChange: changes['isVisible'].firstChange
  86. });
  87. }
  88. }
  89. ngOnDestroy() {
  90. this.progressSubscription?.unsubscribe();
  91. this.resizeSubscription?.unsubscribe();
  92. }
  93. // 响应式布局检测
  94. @HostListener('window:resize', ['$event'])
  95. onResize(event: any) {
  96. this.checkScreenSize();
  97. }
  98. @HostListener('document:keydown', ['$event'])
  99. onKeyDown(event: KeyboardEvent) {
  100. if (event.key === 'Escape' && this.isVisible) {
  101. this.onClose();
  102. }
  103. }
  104. // 开始颜色分析
  105. async startColorAnalysis() {
  106. if (this.uploadedFiles.length === 0 || this.uploadType !== 'image') {
  107. return;
  108. }
  109. this.analysisError = null;
  110. try {
  111. // 发射分析事件给父组件处理
  112. this.analyzeColors.emit(this.uploadedFiles);
  113. // 注意:不再调用本地的simulateAnalysis,而是等待父组件传递分析结果
  114. // 父组件会通过@Input() analysisResult传递分析结果
  115. // 父组件也会通过@Input() isAnalyzing控制分析状态
  116. } catch (error) {
  117. this.analysisError = '颜色分析失败,请重试';
  118. console.error('Color analysis error:', error);
  119. }
  120. }
  121. // 模拟分析过程
  122. private async simulateAnalysis(): Promise<void> {
  123. return new Promise((resolve) => {
  124. setTimeout(() => {
  125. // 模拟分析结果
  126. this.analysisResult = {
  127. colors: [
  128. { hex: '#8B4513', rgb: { r: 139, g: 69, b: 19 }, percentage: 35.2 },
  129. { hex: '#A0522D', rgb: { r: 160, g: 82, b: 45 }, percentage: 27.1 },
  130. { hex: '#D2B48C', rgb: { r: 210, g: 180, b: 140 }, percentage: 22.4 },
  131. { hex: '#DEB887', rgb: { r: 222, g: 184, b: 135 }, percentage: 12.5 },
  132. { hex: '#F5F5DC', rgb: { r: 245, g: 245, b: 220 }, percentage: 2.8 }
  133. ],
  134. originalImage: this.uploadedFiles[0]?.url || '',
  135. mosaicImage: '/assets/images/mock-mosaic.jpg',
  136. reportPath: '/reports/color-analysis-' + Date.now() + '.html',
  137. enhancedAnalysis: {
  138. colorWheel: {
  139. dominantHue: 45,
  140. saturationRange: { min: 20, max: 80 },
  141. brightnessRange: { min: 40, max: 90 },
  142. colorDistribution: [
  143. { hue: 45, saturation: 65, brightness: 75, percentage: 62.3 },
  144. { hue: 30, saturation: 45, brightness: 85, percentage: 22.4 },
  145. { hue: 40, saturation: 55, brightness: 70, percentage: 12.5 }
  146. ]
  147. },
  148. colorHarmony: {
  149. harmonyType: 'analogous',
  150. harmonyScore: 78,
  151. suggestions: ['保持温暖色调的统一性', '可适当增加对比色作为点缀'],
  152. relationships: [
  153. { color1: '#8B4513', color2: '#D2B48C', relationship: '相似色', strength: 85 }
  154. ]
  155. },
  156. colorTemperature: {
  157. averageTemperature: 3200,
  158. temperatureRange: { min: 2800, max: 3600 },
  159. warmCoolBalance: 65,
  160. temperatureDescription: '温暖色调,适合营造舒适氛围',
  161. lightingRecommendations: ['适合使用暖白光照明(2700K-3000K)', '营造温馨舒适的氛围']
  162. },
  163. colorPsychology: {
  164. mood: '温馨',
  165. atmosphere: '舒适',
  166. suitableSpaces: ['客厅', '卧室', '餐厅'],
  167. psychologicalEffects: ['放松身心', '增强温暖感', '促进交流'],
  168. emotionalImpact: {
  169. energy: 45,
  170. warmth: 85,
  171. sophistication: 60,
  172. comfort: 90
  173. }
  174. }
  175. },
  176. // 模拟其他分析结果
  177. formAnalysis: {
  178. lineAnalysis: {
  179. dominantLines: ['直线', '曲线'],
  180. dominantLineType: '直线',
  181. visualFlow: '流畅'
  182. },
  183. overallAssessment: {
  184. complexity: 65,
  185. visualImpact: 78
  186. }
  187. },
  188. textureAnalysis: {
  189. surfaceProperties: {
  190. roughness: { level: 'moderate', value: 50, description: '中等粗糙度' },
  191. glossiness: { level: 'satin', value: 40, reflectivity: 35 },
  192. transparency: { level: 'opaque', value: 10, description: '基本不透明' },
  193. reflectivity: { level: 'low', value: 25, description: '低反射' }
  194. },
  195. materialClassification: {
  196. primaryMaterials: ['木材', '织物'],
  197. secondaryMaterials: ['金属', '玻璃']
  198. }
  199. },
  200. patternAnalysis: {
  201. patternRecognition: {
  202. primaryPatterns: [{ type: 'geometric', confidence: 80, coverage: 60, characteristics: ['规则', '对称'] }],
  203. patternComplexity: { level: 'moderate', value: 60, description: '中等复杂度' }
  204. },
  205. visualRhythm: {
  206. rhythmType: { primary: 'regular', secondary: 'flowing' },
  207. movement: { direction: 'horizontal', intensity: 65 }
  208. }
  209. },
  210. lightingAnalysis: {
  211. lightSourceIdentification: {
  212. primarySources: ['自然光', '人工光'],
  213. lightingSetup: '混合照明'
  214. },
  215. ambientAnalysis: {
  216. ambientLight: '柔和',
  217. lightingMood: '温馨'
  218. }
  219. }
  220. };
  221. // 添加调试输出
  222. console.log('=== 分析结果数据结构调试 ===');
  223. console.log('完整分析结果:', this.analysisResult);
  224. console.log('形体分析:', this.analysisResult.formAnalysis);
  225. console.log('质感分析:', this.analysisResult.textureAnalysis);
  226. console.log('纹理分析:', this.analysisResult.patternAnalysis);
  227. console.log('灯光分析:', this.analysisResult.lightingAnalysis);
  228. console.log('色彩分析:', this.analysisResult.enhancedAnalysis);
  229. console.log('主要光源:', this.analysisResult.lightingAnalysis.lightSourceIdentification.primarySources);
  230. console.log('材质分类:', this.analysisResult.textureAnalysis.materialClassification);
  231. console.log('视觉节奏:', this.analysisResult.patternAnalysis.visualRhythm);
  232. // 生成需求映射
  233. this.generateRequirementMappingFromAnalysis();
  234. resolve();
  235. }, 2000);
  236. });
  237. }
  238. // 根据分析结果生成需求映射
  239. private generateRequirementMappingFromAnalysis(): void {
  240. if (!this.analysisResult) {
  241. return;
  242. }
  243. try {
  244. this.isGeneratingMapping = true;
  245. this.mappingError = null;
  246. // 使用需求映射服务生成映射
  247. this.requirementMappingService.generateRequirementMapping(this.analysisResult).subscribe({
  248. next: (mapping) => {
  249. this.requirementMapping = mapping;
  250. console.log('=== 需求映射生成成功 ===');
  251. console.log('需求映射结果:', this.requirementMapping);
  252. // 发出需求映射生成事件
  253. this.generateRequirementMapping.emit(this.requirementMapping);
  254. },
  255. error: (error) => {
  256. console.error('需求映射生成失败:', error);
  257. this.mappingError = '需求映射生成失败,请稍后重试';
  258. },
  259. complete: () => {
  260. this.isGeneratingMapping = false;
  261. }
  262. });
  263. } catch (error) {
  264. console.error('需求映射初始化失败:', error);
  265. this.mappingError = '需求映射初始化失败,请稍后重试';
  266. this.isGeneratingMapping = false;
  267. }
  268. }
  269. // 手动重新生成需求映射
  270. regenerateRequirementMapping(): void {
  271. if (!this.analysisResult) {
  272. return;
  273. }
  274. this.generateRequirementMappingFromAnalysis();
  275. }
  276. // 事件处理方法
  277. onClose() {
  278. console.log('🔴 弹窗组件 onClose 被调用');
  279. this.closeModal.emit();
  280. console.log('✅ closeModal 事件已发出');
  281. }
  282. onBackdropClick(event: Event) {
  283. // 点击背景遮罩关闭弹窗
  284. this.onClose();
  285. }
  286. onAnalyzeColorsClick() {
  287. if (this.isAnalyzing || this.uploadedFiles.length === 0) {
  288. return;
  289. }
  290. this.animationState = 'loading';
  291. this.analyzeColors.emit(this.uploadedFiles);
  292. }
  293. onViewReportClick() {
  294. if (this.analysisResult) {
  295. // 创建一个新的窗口来显示完整报告
  296. const reportWindow = window.open('', '_blank', 'width=1200,height=800,scrollbars=yes,resizable=yes');
  297. if (reportWindow) {
  298. // 生成完整的HTML报告内容
  299. const reportHtml = this.generateFullReport();
  300. reportWindow.document.write(reportHtml);
  301. reportWindow.document.close();
  302. reportWindow.focus();
  303. } else {
  304. // 如果弹窗被阻止,则下载报告文件
  305. this.downloadReport();
  306. }
  307. // 触发事件给父组件
  308. this.viewReport.emit(this.analysisResult);
  309. }
  310. }
  311. // 工具方法
  312. shouldShowColorAnalysis(): boolean {
  313. return this.uploadType === 'image' || this.hasImageFiles();
  314. }
  315. formatFileSize(bytes: number): string {
  316. if (bytes === 0) return '0 Bytes';
  317. const k = 1024;
  318. const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  319. const i = Math.floor(Math.log(bytes) / Math.log(k));
  320. return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  321. }
  322. getFileTypeIcon(file: UploadedFile): string {
  323. if (file.type?.startsWith('image/')) {
  324. return 'image';
  325. } else if (file.type?.includes('pdf')) {
  326. return 'pdf';
  327. } else if (file.type?.includes('word') || file.type?.includes('doc')) {
  328. return 'document';
  329. } else {
  330. return 'file';
  331. }
  332. }
  333. hasImageFiles(): boolean {
  334. return this.uploadedFiles.some(file => file.type?.startsWith('image/'));
  335. }
  336. // 生成颜色描述文字
  337. generateColorDescription(): string {
  338. if (!this.analysisResult || !this.analysisResult.colors.length) {
  339. return '';
  340. }
  341. const colorDescriptions = this.analysisResult.colors.map(color => {
  342. const colorName = color.name || this.getColorName(color.hex);
  343. return `${colorName}(${color.hex}) ${color.percentage}%`;
  344. });
  345. return `主要色彩:${colorDescriptions.join('、')}`;
  346. }
  347. // 根据色值获取颜色名称
  348. private getColorName(hex: string): string {
  349. const colorMap: { [key: string]: string } = {
  350. '#FFFFFF': '纯白色',
  351. '#F5F5F5': '白烟',
  352. '#E5E5E5': '浅灰色',
  353. '#CCCCCC': '中灰色',
  354. '#999999': '深灰色',
  355. '#666666': '暗灰色',
  356. '#333333': '深暗灰',
  357. '#000000': '纯黑色',
  358. '#FF0000': '红色',
  359. '#00FF00': '酸橙色',
  360. '#0000FF': '蓝色',
  361. '#FFFF00': '黄色',
  362. '#FF00FF': '品红色',
  363. '#00FFFF': '青色',
  364. '#FFA500': '橙色',
  365. '#800080': '紫色',
  366. '#008000': '绿色',
  367. '#000080': '海军蓝',
  368. '#800000': '栗色',
  369. '#808000': '橄榄色',
  370. '#008080': '水鸭色',
  371. '#C0C0C0': '银色',
  372. '#808080': '灰色',
  373. '#FFE4E1': '雾玫瑰',
  374. '#F0F8FF': '爱丽丝蓝',
  375. '#FAEBD7': '古董白',
  376. '#F5F5DC': '米色',
  377. '#DEB887': '硬木色',
  378. '#A52A2A': '棕色',
  379. '#D2691E': '巧克力色',
  380. '#FF7F50': '珊瑚色',
  381. '#6495ED': '矢车菊蓝',
  382. '#DC143C': '深红色',
  383. '#00008B': '深蓝色',
  384. '#B8860B': '深金色',
  385. '#A9A9A9': '深灰色',
  386. '#006400': '深绿色',
  387. '#BDB76B': '深卡其色',
  388. '#8B008B': '深品红',
  389. '#556B2F': '深橄榄绿',
  390. '#FF8C00': '深橙色',
  391. '#9932CC': '深兰花紫',
  392. '#8B0000': '深红色2',
  393. '#E9967A': '深鲑鱼色',
  394. '#8FBC8F': '深海绿',
  395. '#483D8B': '深石板蓝',
  396. '#2F4F4F': '深石板灰',
  397. '#00CED1': '深绿松石',
  398. '#9400D3': '深紫罗兰',
  399. '#FF1493': '深粉红',
  400. '#00BFFF': '深天蓝',
  401. '#696969': '暗灰色2',
  402. '#1E90FF': '道奇蓝',
  403. '#B22222': '火砖色',
  404. '#FFFAF0': '花白色',
  405. '#228B22': '森林绿',
  406. '#DCDCDC': '淡灰色',
  407. '#F8F8FF': '幽灵白',
  408. '#FFD700': '金色',
  409. '#DAA520': '金麒麟色',
  410. '#ADFF2F': '绿黄色',
  411. '#F0FFF0': '蜜瓜色',
  412. '#FF69B4': '热粉红',
  413. '#CD5C5C': '印度红',
  414. '#4B0082': '靛青色',
  415. '#FFFFF0': '象牙色',
  416. '#F0E68C': '卡其色',
  417. '#E6E6FA': '薰衣草色',
  418. '#FFF0F5': '薰衣草红',
  419. '#7CFC00': '草坪绿',
  420. '#FFFACD': '柠檬绸',
  421. '#ADD8E6': '浅蓝色',
  422. '#F08080': '浅珊瑚色',
  423. '#E0FFFF': '浅青色',
  424. '#FAFAD2': '浅金菊黄',
  425. '#D3D3D3': '浅灰色2',
  426. '#90EE90': '浅绿色',
  427. '#FFB6C1': '浅粉红',
  428. '#FFA07A': '浅鲑鱼色',
  429. '#20B2AA': '浅海绿',
  430. '#87CEFA': '浅天蓝',
  431. '#778899': '浅石板灰',
  432. '#B0C4DE': '浅钢蓝',
  433. '#FFFFE0': '浅黄色',
  434. '#32CD32': '酸橙绿',
  435. '#FAF0E6': '亚麻色',
  436. '#66CDAA': '中海绿',
  437. '#0000CD': '中蓝色',
  438. '#BA55D3': '中兰花紫',
  439. '#9370DB': '中紫色',
  440. '#3CB371': '中海春绿',
  441. '#7B68EE': '中石板蓝',
  442. '#00FA9A': '中春绿',
  443. '#48D1CC': '中绿松石',
  444. '#C71585': '中紫罗兰红',
  445. '#191970': '午夜蓝',
  446. '#F5FFFA': '薄荷奶油',
  447. '#FFDEAD': '那瓦霍白',
  448. '#FDF5E6': '老花边',
  449. '#6B8E23': '橄榄褐色',
  450. '#FF4500': '橙红色',
  451. '#DA70D6': '兰花紫',
  452. '#EEE8AA': '灰秋麒麟',
  453. '#98FB98': '灰绿色',
  454. '#AFEEEE': '灰绿松石',
  455. '#DB7093': '灰紫罗兰红',
  456. '#FFEFD5': '番木瓜鞭',
  457. '#FFDAB9': '桃扑',
  458. '#CD853F': '秘鲁色',
  459. '#FFC0CB': '粉红色',
  460. '#DDA0DD': '洋李色',
  461. '#B0E0E6': '粉蓝色',
  462. '#BC8F8F': '玫瑰棕色',
  463. '#4169E1': '皇家蓝',
  464. '#8B4513': '马鞍棕色',
  465. '#FA8072': '鲑鱼色',
  466. '#F4A460': '沙棕色',
  467. '#2E8B57': '海绿色',
  468. '#FFF5EE': '海贝色',
  469. '#A0522D': '赭色',
  470. '#87CEEB': '天蓝色',
  471. '#6A5ACD': '石板蓝',
  472. '#708090': '石板灰',
  473. '#FFFAFA': '雪色',
  474. '#00FF7F': '春绿色',
  475. '#4682B4': '钢蓝色',
  476. '#D2B48C': '棕褐色',
  477. '#D8BFD8': '蓟色',
  478. '#FF6347': '番茄色',
  479. '#40E0D0': '绿松石',
  480. '#EE82EE': '紫罗兰',
  481. '#F5DEB3': '小麦色',
  482. '#9ACD32': '黄绿色'
  483. };
  484. // 如果找到精确匹配,返回对应名称
  485. if (colorMap[hex.toUpperCase()]) {
  486. return colorMap[hex.toUpperCase()];
  487. }
  488. // 否则根据RGB值判断颜色类型
  489. const rgb = this.hexToRgb(hex);
  490. if (!rgb) return '未知颜色';
  491. const { r, g, b } = rgb;
  492. const brightness = (r * 299 + g * 587 + b * 114) / 1000;
  493. // 判断是否为灰色系
  494. const isGray = Math.abs(r - g) < 30 && Math.abs(g - b) < 30 && Math.abs(r - b) < 30;
  495. if (isGray) {
  496. if (brightness > 240) return '浅灰白';
  497. if (brightness > 200) return '浅灰色';
  498. if (brightness > 160) return '中灰色';
  499. if (brightness > 120) return '深灰色';
  500. if (brightness > 80) return '暗灰色';
  501. return '深暗灰';
  502. }
  503. // 判断主要颜色倾向
  504. const max = Math.max(r, g, b);
  505. const min = Math.min(r, g, b);
  506. const saturation = max === 0 ? 0 : (max - min) / max;
  507. if (saturation < 0.2) {
  508. // 低饱和度,偏向灰色
  509. if (brightness > 200) return '浅灰色';
  510. if (brightness > 100) return '中灰色';
  511. return '深灰色';
  512. }
  513. // 高饱和度,判断色相
  514. let colorName = '';
  515. if (r >= g && r >= b) {
  516. if (g > b) {
  517. colorName = brightness > 150 ? '浅橙色' : '橙色';
  518. } else {
  519. colorName = brightness > 150 ? '浅红色' : '红色';
  520. }
  521. } else if (g >= r && g >= b) {
  522. if (r > b) {
  523. colorName = brightness > 150 ? '浅黄绿' : '黄绿色';
  524. } else {
  525. colorName = brightness > 150 ? '浅绿色' : '绿色';
  526. }
  527. } else {
  528. if (r > g) {
  529. colorName = brightness > 150 ? '浅紫色' : '紫色';
  530. } else {
  531. colorName = brightness > 150 ? '浅蓝色' : '蓝色';
  532. }
  533. }
  534. return colorName;
  535. }
  536. // 将十六进制颜色转换为RGB
  537. private hexToRgb(hex: string): { r: number; g: number; b: number } | null {
  538. const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  539. return result ? {
  540. r: parseInt(result[1], 16),
  541. g: parseInt(result[2], 16),
  542. b: parseInt(result[3], 16)
  543. } : null;
  544. }
  545. // 复制颜色描述到剪贴板
  546. async copyColorDescription(): Promise<void> {
  547. const description = this.generateColorDescription();
  548. if (!description) return;
  549. try {
  550. await navigator.clipboard.writeText(description);
  551. this.copySuccess = true;
  552. console.log('颜色描述已复制到剪贴板');
  553. // 2秒后重置复制状态
  554. setTimeout(() => {
  555. this.copySuccess = false;
  556. }, 2000);
  557. } catch (err) {
  558. console.error('复制失败:', err);
  559. // 降级方案:使用传统方法
  560. this.fallbackCopyTextToClipboard(description);
  561. }
  562. }
  563. // 降级复制方案
  564. private fallbackCopyTextToClipboard(text: string): void {
  565. const textArea = document.createElement('textarea');
  566. textArea.value = text;
  567. textArea.style.top = '0';
  568. textArea.style.left = '0';
  569. textArea.style.position = 'fixed';
  570. textArea.style.opacity = '0';
  571. document.body.appendChild(textArea);
  572. textArea.focus();
  573. textArea.select();
  574. try {
  575. const successful = document.execCommand('copy');
  576. if (successful) {
  577. this.copySuccess = true;
  578. console.log('颜色描述已复制到剪贴板(降级方案)');
  579. // 2秒后重置复制状态
  580. setTimeout(() => {
  581. this.copySuccess = false;
  582. }, 2000);
  583. }
  584. } catch (err) {
  585. console.error('降级复制方案也失败了:', err);
  586. }
  587. document.body.removeChild(textArea);
  588. }
  589. // 生成完整的HTML报告
  590. private generateFullReport(): string {
  591. if (!this.analysisResult) return '';
  592. const colors = this.analysisResult.colors;
  593. const colorDescription = this.generateColorDescription();
  594. return `
  595. <!DOCTYPE html>
  596. <html lang="zh-CN">
  597. <head>
  598. <meta charset="UTF-8">
  599. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  600. <title>图片颜色分析完整报告</title>
  601. <style>
  602. body {
  603. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  604. line-height: 1.6;
  605. color: #333;
  606. max-width: 1200px;
  607. margin: 0 auto;
  608. padding: 40px 20px;
  609. background: #f8f9fa;
  610. }
  611. .report-container {
  612. background: white;
  613. border-radius: 16px;
  614. padding: 40px;
  615. box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
  616. }
  617. h1 {
  618. color: #1d1d1f;
  619. font-size: 32px;
  620. font-weight: 700;
  621. margin-bottom: 8px;
  622. text-align: center;
  623. }
  624. .subtitle {
  625. color: #666;
  626. font-size: 16px;
  627. text-align: center;
  628. margin-bottom: 40px;
  629. }
  630. .section {
  631. margin-bottom: 40px;
  632. }
  633. .section-title {
  634. color: #1d1d1f;
  635. font-size: 24px;
  636. font-weight: 600;
  637. margin-bottom: 20px;
  638. border-bottom: 2px solid #007AFF;
  639. padding-bottom: 8px;
  640. }
  641. .color-grid {
  642. display: grid;
  643. grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  644. gap: 20px;
  645. margin-bottom: 30px;
  646. }
  647. .color-item {
  648. background: #f9f9f9;
  649. border-radius: 12px;
  650. padding: 20px;
  651. text-align: center;
  652. border: 1px solid #e5e5ea;
  653. transition: transform 0.2s ease;
  654. }
  655. .color-item:hover {
  656. transform: translateY(-2px);
  657. box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
  658. }
  659. .color-swatch {
  660. width: 80px;
  661. height: 80px;
  662. border-radius: 50%;
  663. margin: 0 auto 16px;
  664. border: 3px solid white;
  665. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  666. }
  667. .color-name {
  668. font-weight: 600;
  669. font-size: 16px;
  670. color: #1d1d1f;
  671. margin-bottom: 4px;
  672. }
  673. .color-hex {
  674. font-family: 'Monaco', 'Menlo', monospace;
  675. font-size: 14px;
  676. color: #666;
  677. margin-bottom: 4px;
  678. }
  679. .color-percentage {
  680. font-size: 18px;
  681. font-weight: 700;
  682. color: #007AFF;
  683. }
  684. .description-section {
  685. background: #f9f9f9;
  686. border-radius: 12px;
  687. padding: 24px;
  688. border-left: 4px solid #007AFF;
  689. }
  690. .description-text {
  691. font-size: 16px;
  692. line-height: 1.8;
  693. color: #333;
  694. white-space: pre-wrap;
  695. }
  696. .stats-grid {
  697. display: grid;
  698. grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  699. gap: 16px;
  700. margin-top: 30px;
  701. }
  702. .stat-item {
  703. text-align: center;
  704. padding: 16px;
  705. background: #f0f8ff;
  706. border-radius: 8px;
  707. }
  708. .stat-value {
  709. font-size: 24px;
  710. font-weight: 700;
  711. color: #007AFF;
  712. }
  713. .stat-label {
  714. font-size: 14px;
  715. color: #666;
  716. margin-top: 4px;
  717. }
  718. .enhanced-section {
  719. background: #f8f9fa;
  720. border-radius: 12px;
  721. padding: 24px;
  722. margin-bottom: 30px;
  723. }
  724. .enhanced-title {
  725. font-size: 20px;
  726. font-weight: 600;
  727. color: #333;
  728. margin-bottom: 20px;
  729. display: flex;
  730. align-items: center;
  731. gap: 8px;
  732. }
  733. .enhanced-title::before {
  734. content: '';
  735. width: 4px;
  736. height: 20px;
  737. background: #007AFF;
  738. border-radius: 2px;
  739. }
  740. .analysis-grid {
  741. display: grid;
  742. grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  743. gap: 20px;
  744. }
  745. .analysis-card {
  746. background: white;
  747. border-radius: 8px;
  748. padding: 20px;
  749. border: 1px solid #e5e5ea;
  750. }
  751. .analysis-card h4 {
  752. font-size: 16px;
  753. font-weight: 600;
  754. color: #333;
  755. margin-bottom: 12px;
  756. }
  757. .info-row {
  758. display: flex;
  759. justify-content: space-between;
  760. align-items: center;
  761. padding: 8px 0;
  762. border-bottom: 1px solid #f0f0f0;
  763. }
  764. .info-row:last-child {
  765. border-bottom: none;
  766. }
  767. .info-label {
  768. font-weight: 500;
  769. color: #666;
  770. font-size: 14px;
  771. }
  772. .info-value {
  773. font-weight: 600;
  774. color: #333;
  775. font-size: 14px;
  776. }
  777. .tag-list {
  778. display: flex;
  779. flex-wrap: wrap;
  780. gap: 8px;
  781. margin-top: 8px;
  782. }
  783. .tag {
  784. padding: 4px 10px;
  785. background: #e3f2fd;
  786. color: #1976d2;
  787. border-radius: 12px;
  788. font-size: 12px;
  789. font-weight: 500;
  790. }
  791. .progress-bar {
  792. width: 100%;
  793. height: 8px;
  794. background: #f0f0f0;
  795. border-radius: 4px;
  796. overflow: hidden;
  797. margin: 8px 0;
  798. }
  799. .progress-fill {
  800. height: 100%;
  801. background: linear-gradient(90deg, #007AFF, #34C759);
  802. border-radius: 4px;
  803. }
  804. .footer {
  805. text-align: center;
  806. margin-top: 40px;
  807. padding-top: 20px;
  808. border-top: 1px solid #e5e5ea;
  809. color: #666;
  810. font-size: 14px;
  811. }
  812. @media print {
  813. body { background: white; }
  814. .report-container { box-shadow: none; }
  815. }
  816. </style>
  817. </head>
  818. <body>
  819. <div class="report-container">
  820. <h1>图片颜色分析报告</h1>
  821. <p class="subtitle">基于AI智能分析生成的详细颜色报告</p>
  822. <div class="section">
  823. <h2 class="section-title">颜色分析结果</h2>
  824. <div class="color-grid">
  825. ${colors.map(color => `
  826. <div class="color-item">
  827. <div class="color-swatch" style="background-color: ${color.hex}"></div>
  828. <div class="color-name">${this.getColorName(color.hex)}</div>
  829. <div class="color-hex">${color.hex}</div>
  830. <div class="color-percentage">${color.percentage}%</div>
  831. </div>
  832. `).join('')}
  833. </div>
  834. <div class="stats-grid">
  835. <div class="stat-item">
  836. <div class="stat-value">${colors.length}</div>
  837. <div class="stat-label">主要颜色数量</div>
  838. </div>
  839. <div class="stat-item">
  840. <div class="stat-value">${colors[0]?.percentage || 0}%</div>
  841. <div class="stat-label">主色占比</div>
  842. </div>
  843. <div class="stat-item">
  844. <div class="stat-value">${colors.reduce((sum, c) => sum + c.percentage, 0).toFixed(1)}%</div>
  845. <div class="stat-label">总覆盖率</div>
  846. </div>
  847. </div>
  848. </div>
  849. ${this.analysisResult.enhancedAnalysis ? `
  850. <div class="section">
  851. <h2 class="section-title">增强色彩分析</h2>
  852. <div class="enhanced-section">
  853. <div class="enhanced-title">色轮分析</div>
  854. <div class="analysis-grid">
  855. <div class="analysis-card">
  856. <h4>色相分布</h4>
  857. <div class="info-row">
  858. <span class="info-label">主色调:</span>
  859. <span class="info-value">${this.analysisResult.enhancedAnalysis.colorWheel.dominantHue}°</span>
  860. </div>
  861. <div class="info-row">
  862. <span class="info-label">饱和度范围:</span>
  863. <span class="info-value">${this.analysisResult.enhancedAnalysis.colorWheel.saturationRange.min}% - ${this.analysisResult.enhancedAnalysis.colorWheel.saturationRange.max}%</span>
  864. </div>
  865. </div>
  866. <div class="analysis-card">
  867. <h4>色彩心理学</h4>
  868. <div class="info-row">
  869. <span class="info-label">情绪:</span>
  870. <span class="info-value">${this.analysisResult.enhancedAnalysis.colorPsychology.mood}</span>
  871. </div>
  872. <div class="info-row">
  873. <span class="info-label">氛围:</span>
  874. <span class="info-value">${this.analysisResult.enhancedAnalysis.colorPsychology.atmosphere}</span>
  875. </div>
  876. <div class="tag-list">
  877. ${this.analysisResult.enhancedAnalysis.colorPsychology.suitableSpaces.map(space => `<span class="tag">${space}</span>`).join('')}
  878. </div>
  879. </div>
  880. </div>
  881. </div>
  882. </div>
  883. ` : ''}
  884. ${this.analysisResult.formAnalysis ? `
  885. <div class="section">
  886. <h2 class="section-title">形体分析</h2>
  887. <div class="enhanced-section">
  888. <div class="analysis-grid">
  889. <div class="analysis-card">
  890. <h4>线条分析</h4>
  891. <div class="info-row">
  892. <span class="info-label">主导线条:</span>
  893. <span class="info-value">${this.analysisResult.formAnalysis.lineAnalysis.dominantLineType}</span>
  894. </div>
  895. <div class="info-row">
  896. <span class="info-label">视觉流动:</span>
  897. <span class="info-value">${this.analysisResult.formAnalysis.lineAnalysis.visualFlow}</span>
  898. </div>
  899. </div>
  900. <div class="analysis-card">
  901. <h4>整体评估</h4>
  902. <div class="info-row">
  903. <span class="info-label">复杂度:</span>
  904. <span class="info-value">${this.analysisResult.formAnalysis.overallAssessment.complexity}%</span>
  905. </div>
  906. <div class="progress-bar">
  907. <div class="progress-fill" style="width: ${this.analysisResult.formAnalysis.overallAssessment.complexity}%"></div>
  908. </div>
  909. <div class="info-row">
  910. <span class="info-label">视觉冲击:</span>
  911. <span class="info-value">${this.analysisResult.formAnalysis.overallAssessment.visualImpact}%</span>
  912. </div>
  913. <div class="progress-bar">
  914. <div class="progress-fill" style="width: ${this.analysisResult.formAnalysis.overallAssessment.visualImpact}%"></div>
  915. </div>
  916. </div>
  917. </div>
  918. </div>
  919. </div>
  920. ` : ''}
  921. ${this.analysisResult.textureAnalysis ? `
  922. <div class="section">
  923. <h2 class="section-title">质感分析</h2>
  924. <div class="enhanced-section">
  925. <div class="analysis-grid">
  926. <div class="analysis-card">
  927. <h4>表面属性</h4>
  928. <div class="info-row">
  929. <span class="info-label">粗糙度:</span>
  930. <span class="info-value">${this.analysisResult.textureAnalysis.surfaceProperties.roughness}%</span>
  931. </div>
  932. <div class="progress-bar">
  933. <div class="progress-fill" style="width: ${this.analysisResult.textureAnalysis.surfaceProperties.roughness}%"></div>
  934. </div>
  935. <div class="info-row">
  936. <span class="info-label">光泽度:</span>
  937. <span class="info-value">${this.analysisResult.textureAnalysis.surfaceProperties.glossiness}%</span>
  938. </div>
  939. <div class="progress-bar">
  940. <div class="progress-fill" style="width: ${this.analysisResult.textureAnalysis.surfaceProperties.glossiness}%"></div>
  941. </div>
  942. </div>
  943. <div class="analysis-card">
  944. <h4>材质分类</h4>
  945. <div class="info-row">
  946. <span class="info-label">主要材质:</span>
  947. </div>
  948. <div class="tag-list">
  949. ${this.analysisResult.textureAnalysis.materialClassification.primaryMaterials.map((material: string) => `<span class="tag">${material}</span>`).join('')}
  950. </div>
  951. <div class="info-row">
  952. <span class="info-label">次要材质:</span>
  953. </div>
  954. <div class="tag-list">
  955. ${this.analysisResult.textureAnalysis.materialClassification.secondaryMaterials.map((material: string) => `<span class="tag">${material}</span>`).join('')}
  956. </div>
  957. </div>
  958. </div>
  959. </div>
  960. </div>
  961. ` : ''}
  962. ${this.analysisResult.patternAnalysis ? `
  963. <div class="section">
  964. <h2 class="section-title">纹理分析</h2>
  965. <div class="enhanced-section">
  966. <div class="analysis-grid">
  967. <div class="analysis-card">
  968. <h4>图案识别</h4>
  969. <div class="info-row">
  970. <span class="info-label">主要图案:</span>
  971. <span class="info-value">${this.analysisResult.patternAnalysis.patternRecognition.primaryPatterns[0]?.type || '无'}</span>
  972. </div>
  973. <div class="info-row">
  974. <span class="info-label">复杂度:</span>
  975. <span class="info-value">${this.analysisResult.patternAnalysis.patternRecognition.complexity}</span>
  976. </div>
  977. </div>
  978. <div class="analysis-card">
  979. <h4>视觉节奏</h4>
  980. <div class="info-row">
  981. <span class="info-label">节奏类型:</span>
  982. <span class="info-value">${this.analysisResult.patternAnalysis.visualRhythm.rhythmType}</span>
  983. </div>
  984. <div class="info-row">
  985. <span class="info-label">运动感:</span>
  986. <span class="info-value">${this.analysisResult.patternAnalysis.visualRhythm.movement}</span>
  987. </div>
  988. </div>
  989. </div>
  990. </div>
  991. </div>
  992. ` : ''}
  993. ${this.analysisResult.lightingAnalysis ? `
  994. <div class="section">
  995. <h2 class="section-title">灯光分析</h2>
  996. <div class="enhanced-section">
  997. <div class="analysis-grid">
  998. <div class="analysis-card">
  999. <h4>光源识别</h4>
  1000. <div class="info-row">
  1001. interface RequirementMapping {
  1002. sceneGeneration: {
  1003. baseScene: string; // 固定场景模板
  1004. parameters: SceneParams; // 场景参数
  1005. atmospherePreview: string; // 氛围感预览图
  1006. };
  1007. parameterMapping: {
  1008. colorParams: ColorMappingParams;
  1009. spaceParams: SpaceMappingParams;
  1010. materialParams: MaterialMappingParams;
  1011. };
  1012. }interface RequirementMapping {
  1013. sceneGeneration: {
  1014. baseScene: string; // 固定场景模板
  1015. parameters: SceneParams; // 场景参数
  1016. atmospherePreview: string; // 氛围感预览图
  1017. };
  1018. parameterMapping: {
  1019. colorParams: ColorMappingParams;
  1020. spaceParams: SpaceMappingParams;
  1021. materialParams: MaterialMappingParams;
  1022. };
  1023. }interface RequirementMapping {
  1024. sceneGeneration: {
  1025. baseScene: string; // 固定场景模板
  1026. parameters: SceneParams; // 场景参数
  1027. atmospherePreview: string; // 氛围感预览图
  1028. };
  1029. parameterMapping: {
  1030. colorParams: ColorMappingParams;
  1031. spaceParams: SpaceMappingParams;
  1032. materialParams: MaterialMappingParams;
  1033. };
  1034. } <span class="info-label">主要光源:</span>
  1035. <span class="info-value">${this.analysisResult.lightingAnalysis.lightSourceIdentification.primarySources[0]?.type || '无'} - ${this.analysisResult.lightingAnalysis.lightSourceIdentification.primarySources[0]?.subtype || ''}</span>
  1036. </div>
  1037. <div class="info-row">
  1038. <span class="info-label">灯光复杂度:</span>
  1039. <span class="info-value">${this.analysisResult.lightingAnalysis.lightSourceIdentification.lightingSetup.complexity}</span>
  1040. </div>
  1041. <div class="info-row">
  1042. <span class="info-label">灯光风格:</span>
  1043. <span class="info-value">${this.analysisResult.lightingAnalysis.lightSourceIdentification.lightingSetup.lightingStyle}</span>
  1044. </div>
  1045. </div>
  1046. <div class="analysis-card">
  1047. <h4>环境分析</h4>
  1048. <div class="info-row">
  1049. <span class="info-label">环境光强度:</span>
  1050. <span class="info-value">${this.analysisResult.lightingAnalysis.ambientAnalysis.ambientLevel.strength}%</span>
  1051. </div>
  1052. <div class="info-row">
  1053. <span class="info-label">灯光情绪:</span>
  1054. <span class="info-value">${this.analysisResult.lightingAnalysis.ambientAnalysis.lightingMood.primary}</span>
  1055. </div>
  1056. <div class="info-row">
  1057. <span class="info-label">情绪强度:</span>
  1058. <span class="info-value">${this.analysisResult.lightingAnalysis.ambientAnalysis.lightingMood.intensity}%</span>
  1059. </div>
  1060. </div>
  1061. </div>
  1062. </div>
  1063. </div>
  1064. ` : ''}
  1065. <div class="section">
  1066. <h2 class="section-title">颜色描述文字</h2>
  1067. <div class="description-section">
  1068. <div class="description-text">${colorDescription}</div>
  1069. </div>
  1070. </div>
  1071. <div class="footer">
  1072. <p>报告生成时间:${new Date().toLocaleString('zh-CN')}</p>
  1073. <p>由颜色分析系统自动生成</p>
  1074. </div>
  1075. </div>
  1076. </body>
  1077. </html>`;
  1078. }
  1079. // 下载报告文件
  1080. private downloadReport(): void {
  1081. if (!this.analysisResult) return;
  1082. const reportHtml = this.generateFullReport();
  1083. const blob = new Blob([reportHtml], { type: 'text/html;charset=utf-8' });
  1084. const url = URL.createObjectURL(blob);
  1085. const link = document.createElement('a');
  1086. link.href = url;
  1087. link.download = `颜色分析报告_${new Date().toISOString().slice(0, 10)}.html`;
  1088. document.body.appendChild(link);
  1089. link.click();
  1090. document.body.removeChild(link);
  1091. URL.revokeObjectURL(url);
  1092. }
  1093. private checkScreenSize(): void {
  1094. const width = window.innerWidth;
  1095. this.isMobile = width < 768;
  1096. this.isTablet = width >= 768 && width < 1024;
  1097. }
  1098. private setupResizeListener(): void {
  1099. // 可以添加更复杂的响应式逻辑
  1100. }
  1101. // 获取质感分析属性数组,用于模板中的循环显示
  1102. getTextureProperties(): Array<{name: string, value: number}> {
  1103. if (!this.analysisResult?.textureAnalysis?.surfaceProperties) {
  1104. return [];
  1105. }
  1106. const properties = this.analysisResult.textureAnalysis.surfaceProperties;
  1107. return [
  1108. { name: '粗糙度', value: properties.roughness?.value || 0 },
  1109. { name: '光泽度', value: properties.glossiness?.value || 0 },
  1110. { name: '透明度', value: properties.transparency?.value || 0 },
  1111. { name: '反射率', value: properties.glossiness?.reflectivity || 0 }
  1112. ];
  1113. }
  1114. // 获取复杂度分数(用于进度条显示)
  1115. getComplexityScore(complexity: string): number {
  1116. const scoreMap: { [key: string]: number } = {
  1117. 'simple': 33,
  1118. 'moderate': 66,
  1119. 'complex': 90,
  1120. 'very-complex': 100
  1121. };
  1122. return scoreMap[complexity] || 0;
  1123. }
  1124. // 获取材质名称的中文翻译
  1125. getMaterialName(category: string): string {
  1126. const nameMap: { [key: string]: string } = {
  1127. 'wood': '木材',
  1128. 'metal': '金属',
  1129. 'fabric': '织物',
  1130. 'leather': '皮革',
  1131. 'plastic': '塑料',
  1132. 'glass': '玻璃',
  1133. 'ceramic': '陶瓷',
  1134. 'stone': '石材',
  1135. 'composite': '复合材料'
  1136. };
  1137. return nameMap[category] || category;
  1138. }
  1139. // 获取图案类型名称的中文翻译
  1140. getPatternTypeName(type: string): string {
  1141. const nameMap: { [key: string]: string } = {
  1142. 'geometric': '几何图案',
  1143. 'organic': '有机图案',
  1144. 'abstract': '抽象图案',
  1145. 'floral': '花卉图案',
  1146. 'striped': '条纹图案',
  1147. 'checkered': '格子图案',
  1148. 'dotted': '圆点图案',
  1149. 'textured': '纹理图案'
  1150. };
  1151. return nameMap[type] || type;
  1152. }
  1153. // 获取复杂度名称的中文翻译
  1154. getComplexityName(level: string): string {
  1155. const nameMap: { [key: string]: string } = {
  1156. 'simple': '简单',
  1157. 'moderate': '中等',
  1158. 'complex': '复杂',
  1159. 'very-complex': '非常复杂'
  1160. };
  1161. return nameMap[level] || level;
  1162. }
  1163. // 获取节奏类型名称的中文翻译
  1164. getRhythmTypeName(type: string): string {
  1165. const nameMap: { [key: string]: string } = {
  1166. 'regular': '规律节奏',
  1167. 'alternating': '交替节奏',
  1168. 'flowing': '流动节奏',
  1169. 'progressive': '渐进节奏',
  1170. 'random': '随机节奏'
  1171. };
  1172. return nameMap[type] || type;
  1173. }
  1174. // 获取方向名称的中文翻译
  1175. getDirectionName(direction: string): string {
  1176. const nameMap: { [key: string]: string } = {
  1177. 'horizontal': '水平方向',
  1178. 'vertical': '垂直方向',
  1179. 'diagonal': '对角方向',
  1180. 'radial': '径向',
  1181. 'multi-directional': '多方向'
  1182. };
  1183. return nameMap[direction] || direction;
  1184. }
  1185. // 获取光源名称的中文翻译
  1186. getLightSourceName(type: string, subtype: string): string {
  1187. const typeMap: { [key: string]: string } = {
  1188. 'natural': '自然光',
  1189. 'artificial': '人工光',
  1190. 'mixed': '混合光'
  1191. };
  1192. const subtypeMap: { [key: string]: string } = {
  1193. 'sunlight': '日光',
  1194. 'LED': 'LED灯',
  1195. 'fluorescent': '荧光灯',
  1196. 'incandescent': '白炽灯',
  1197. 'candle': '烛光'
  1198. };
  1199. const typeName = typeMap[type] || type;
  1200. const subtypeName = subtypeMap[subtype] || subtype;
  1201. return subtype ? `${typeName}(${subtypeName})` : typeName;
  1202. }
  1203. // 获取灯光风格名称的中文翻译
  1204. getLightingStyleName(style: string): string {
  1205. const nameMap: { [key: string]: string } = {
  1206. 'dramatic': '戏剧性',
  1207. 'soft': '柔和',
  1208. 'even': '均匀',
  1209. 'accent': '重点照明',
  1210. 'task': '任务照明',
  1211. 'ambient': '环境照明'
  1212. };
  1213. return nameMap[style] || style;
  1214. }
  1215. // 获取灯光情绪名称的中文翻译
  1216. getLightingMoodName(mood: string): string {
  1217. const nameMap: { [key: string]: string } = {
  1218. 'dramatic': '戏剧性',
  1219. 'romantic': '浪漫',
  1220. 'energetic': '活力',
  1221. 'calm': '平静',
  1222. 'mysterious': '神秘',
  1223. 'cheerful': '愉悦',
  1224. 'professional': '专业'
  1225. };
  1226. return nameMap[mood] || mood;
  1227. }
  1228. // 获取颜色用途名称的中文翻译
  1229. getColorUsageName(usage: string): string {
  1230. const nameMap: { [key: string]: string } = {
  1231. 'primary': '主色',
  1232. 'secondary': '辅色',
  1233. 'accent': '点缀色',
  1234. 'background': '背景色'
  1235. };
  1236. return nameMap[usage] || usage;
  1237. }
  1238. // 获取色彩和谐类型的中文翻译
  1239. getColorHarmonyName(harmony: string): string {
  1240. const nameMap: { [key: string]: string } = {
  1241. 'monochromatic': '单色调和',
  1242. 'analogous': '类似色调和',
  1243. 'complementary': '互补色调和',
  1244. 'triadic': '三角色调和',
  1245. 'tetradic': '四角色调和',
  1246. 'split-complementary': '分裂互补色调和'
  1247. };
  1248. return nameMap[harmony] || harmony;
  1249. }
  1250. // 获取色温名称的中文翻译
  1251. getTemperatureName(temp: string): string {
  1252. const nameMap: { [key: string]: string } = {
  1253. 'warm': '暖色调',
  1254. 'neutral': '中性色调',
  1255. 'cool': '冷色调'
  1256. };
  1257. return nameMap[temp] || temp;
  1258. }
  1259. // 获取空间布局类型的中文翻译
  1260. getLayoutTypeName(type: string): string {
  1261. const nameMap: { [key: string]: string } = {
  1262. 'open': '开放式',
  1263. 'enclosed': '封闭式',
  1264. 'semi-open': '半开放式',
  1265. 'multi-level': '多层次'
  1266. };
  1267. return nameMap[type] || type;
  1268. }
  1269. // 获取空间流线类型的中文翻译
  1270. getFlowTypeName(flow: string): string {
  1271. const nameMap: { [key: string]: string } = {
  1272. 'linear': '线性流线',
  1273. 'circular': '环形流线',
  1274. 'grid': '网格流线',
  1275. 'organic': '有机流线'
  1276. };
  1277. return nameMap[flow] || flow;
  1278. }
  1279. // 获取尺寸单位的中文翻译
  1280. getDimensionUnitName(unit: string): string {
  1281. const nameMap: { [key: string]: string } = {
  1282. 'meter': '米',
  1283. 'feet': '英尺'
  1284. };
  1285. return nameMap[unit] || unit;
  1286. }
  1287. // 获取表面处理名称的中文翻译
  1288. getFinishName(finish: string): string {
  1289. const nameMap: { [key: string]: string } = {
  1290. 'matte': '哑光',
  1291. 'satin': '缎面',
  1292. 'gloss': '光面',
  1293. 'textured': '纹理',
  1294. 'brushed': '拉丝',
  1295. 'polished': '抛光'
  1296. };
  1297. return nameMap[finish] || finish;
  1298. }
  1299. // 获取优先级名称的中文翻译
  1300. getPriorityName(priority: string): string {
  1301. const nameMap: { [key: string]: string } = {
  1302. 'primary': '主要',
  1303. 'secondary': '次要',
  1304. 'accent': '点缀'
  1305. };
  1306. return nameMap[priority] || priority;
  1307. }
  1308. }