123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185 |
- import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ChangeDetectionStrategy, HostListener } from '@angular/core';
- import { CommonModule } from '@angular/common';
- import { ColorAnalysisService, AnalysisProgress, ColorAnalysisResult } from '../../services/color-analysis.service';
- import { FormAnalysisService } from '../../services/form-analysis.service';
- import { TextureAnalysisService } from '../../services/texture-analysis.service';
- import { PatternAnalysisService } from '../../services/pattern-analysis.service';
- import { LightingAnalysisService } from '../../services/lighting-analysis.service';
- import { RequirementMappingService } from '../../../services/requirement-mapping.service';
- import { AtmospherePreviewService } from '../../../services/atmosphere-preview.service';
- import { RequirementMapping, SceneTemplate } from '../../../models/requirement-mapping.interface';
- import { Subscription } from 'rxjs';
- import { modalAnimations } from './upload-success-modal.animations';
- export interface UploadedFile {
- id: string;
- name: string;
- url: string;
- size?: number;
- type?: 'image' | 'cad' | 'text';
- preview?: string;
- }
- export interface ColorInfo {
- hex: string;
- rgb: { r: number; g: number; b: number };
- percentage: number;
- name?: string; // 颜色名称,如"深蓝色"、"暖白色"等
- }
- @Component({
- selector: 'app-upload-success-modal',
- standalone: true,
- imports: [CommonModule],
- templateUrl: './upload-success-modal.component.html',
- styleUrls: ['./upload-success-modal.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush,
- animations: modalAnimations
- })
- export class UploadSuccessModalComponent implements OnInit, OnDestroy {
- @Input() isVisible: boolean = false;
- @Input() uploadedFiles: UploadedFile[] = [];
- @Input() uploadType: 'image' | 'document' | 'mixed' = 'image';
- @Input() analysisResult?: ColorAnalysisResult;
- @Input() isAnalyzing: boolean = false; // 新增:从父组件接收分析状态
- @Output() closeModal = new EventEmitter<void>();
- @Output() analyzeColors = new EventEmitter<UploadedFile[]>();
- @Output() viewReport = new EventEmitter<ColorAnalysisResult>();
- @Output() generateRequirementMapping = new EventEmitter<RequirementMapping>(); // 新增:需求映射生成事件
- // 移除本地的isAnalyzing属性,使用@Input()的isAnalyzing
- // isAnalyzing = false; // 已移除,现在从父组件接收
- analysisProgress: AnalysisProgress | null = null;
- analysisError: string | null = null;
-
- // 需求映射相关状态
- requirementMapping: RequirementMapping | null = null;
- isGeneratingMapping = false;
- mappingError: string | null = null;
-
- // 增强分析标签页状态
- activeTab: 'color' | 'form' | 'texture' | 'pattern' | 'lighting' | 'mapping' = 'color';
-
- // 响应式状态
- isMobile = false;
- isTablet = false;
-
- // 动画状态
- animationState = 'idle';
- buttonHoverState = 'normal';
- copySuccess = false; // 复制成功状态
- private progressSubscription?: Subscription;
- private resizeSubscription?: Subscription;
- constructor(
- private colorAnalysisService: ColorAnalysisService,
- private formAnalysisService: FormAnalysisService,
- private textureAnalysisService: TextureAnalysisService,
- private patternAnalysisService: PatternAnalysisService,
- private lightingAnalysisService: LightingAnalysisService,
- private requirementMappingService: RequirementMappingService,
- private atmospherePreviewService: AtmospherePreviewService
- ) {}
- ngOnInit() {
- this.checkScreenSize();
- this.setupResizeListener();
- }
- ngOnDestroy() {
- this.progressSubscription?.unsubscribe();
- this.resizeSubscription?.unsubscribe();
- }
- // 响应式布局检测
- @HostListener('window:resize', ['$event'])
- onResize(event: any) {
- this.checkScreenSize();
- }
- @HostListener('document:keydown', ['$event'])
- onKeyDown(event: KeyboardEvent) {
- if (event.key === 'Escape' && this.isVisible) {
- this.onClose();
- }
- }
- // 开始颜色分析
- async startColorAnalysis() {
- if (this.uploadedFiles.length === 0 || this.uploadType !== 'image') {
- return;
- }
- this.analysisError = null;
- try {
- // 发射分析事件给父组件处理
- this.analyzeColors.emit(this.uploadedFiles);
-
- // 注意:不再调用本地的simulateAnalysis,而是等待父组件传递分析结果
- // 父组件会通过@Input() analysisResult传递分析结果
- // 父组件也会通过@Input() isAnalyzing控制分析状态
-
- } catch (error) {
- this.analysisError = '颜色分析失败,请重试';
- console.error('Color analysis error:', error);
- }
- }
- // 模拟分析过程
- private async simulateAnalysis(): Promise<void> {
- return new Promise((resolve) => {
- setTimeout(() => {
- // 模拟分析结果
- this.analysisResult = {
- colors: [
- { hex: '#8B4513', rgb: { r: 139, g: 69, b: 19 }, percentage: 35.2 },
- { hex: '#A0522D', rgb: { r: 160, g: 82, b: 45 }, percentage: 27.1 },
- { hex: '#D2B48C', rgb: { r: 210, g: 180, b: 140 }, percentage: 22.4 },
- { hex: '#DEB887', rgb: { r: 222, g: 184, b: 135 }, percentage: 12.5 },
- { hex: '#F5F5DC', rgb: { r: 245, g: 245, b: 220 }, percentage: 2.8 }
- ],
- originalImage: this.uploadedFiles[0]?.url || '',
- mosaicImage: '/assets/images/mock-mosaic.jpg',
- reportPath: '/reports/color-analysis-' + Date.now() + '.html',
- enhancedAnalysis: {
- colorWheel: {
- dominantHue: 45,
- saturationRange: { min: 20, max: 80 },
- brightnessRange: { min: 40, max: 90 },
- colorDistribution: [
- { hue: 45, saturation: 65, brightness: 75, percentage: 62.3 },
- { hue: 30, saturation: 45, brightness: 85, percentage: 22.4 },
- { hue: 40, saturation: 55, brightness: 70, percentage: 12.5 }
- ]
- },
- colorHarmony: {
- harmonyType: 'analogous',
- harmonyScore: 78,
- suggestions: ['保持温暖色调的统一性', '可适当增加对比色作为点缀'],
- relationships: [
- { color1: '#8B4513', color2: '#D2B48C', relationship: '相似色', strength: 85 }
- ]
- },
- colorTemperature: {
- averageTemperature: 3200,
- temperatureRange: { min: 2800, max: 3600 },
- warmCoolBalance: 65,
- temperatureDescription: '温暖色调,适合营造舒适氛围',
- lightingRecommendations: ['适合使用暖白光照明(2700K-3000K)', '营造温馨舒适的氛围']
- },
- colorPsychology: {
- mood: '温馨',
- atmosphere: '舒适',
- suitableSpaces: ['客厅', '卧室', '餐厅'],
- psychologicalEffects: ['放松身心', '增强温暖感', '促进交流'],
- emotionalImpact: {
- energy: 45,
- warmth: 85,
- sophistication: 60,
- comfort: 90
- }
- }
- },
- // 模拟其他分析结果
- formAnalysis: {
- lineAnalysis: {
- dominantLines: ['直线', '曲线'],
- dominantLineType: '直线',
- visualFlow: '流畅'
- },
- overallAssessment: {
- complexity: 65,
- visualImpact: 78
- }
- },
- textureAnalysis: {
- surfaceProperties: {
- roughness: { level: 'moderate', value: 50, description: '中等粗糙度' },
- glossiness: { level: 'satin', value: 40, reflectivity: 35 },
- transparency: { level: 'opaque', value: 10, description: '基本不透明' },
- reflectivity: { level: 'low', value: 25, description: '低反射' }
- },
- materialClassification: {
- primaryMaterials: ['木材', '织物'],
- secondaryMaterials: ['金属', '玻璃']
- }
- },
- patternAnalysis: {
- patternRecognition: {
- primaryPatterns: [{ type: 'geometric', confidence: 80, coverage: 60, characteristics: ['规则', '对称'] }],
- patternComplexity: { level: 'moderate', value: 60, description: '中等复杂度' }
- },
- visualRhythm: {
- rhythmType: { primary: 'regular', secondary: 'flowing' },
- movement: { direction: 'horizontal', intensity: 65 }
- }
- },
- lightingAnalysis: {
- lightSourceIdentification: {
- primarySources: ['自然光', '人工光'],
- lightingSetup: '混合照明'
- },
- ambientAnalysis: {
- ambientLight: '柔和',
- lightingMood: '温馨'
- }
- }
- };
-
- // 添加调试输出
- console.log('=== 分析结果数据结构调试 ===');
- console.log('完整分析结果:', this.analysisResult);
- console.log('形体分析:', this.analysisResult.formAnalysis);
- console.log('质感分析:', this.analysisResult.textureAnalysis);
- console.log('纹理分析:', this.analysisResult.patternAnalysis);
- console.log('灯光分析:', this.analysisResult.lightingAnalysis);
- console.log('色彩分析:', this.analysisResult.enhancedAnalysis);
- console.log('主要光源:', this.analysisResult.lightingAnalysis.lightSourceIdentification.primarySources);
- console.log('材质分类:', this.analysisResult.textureAnalysis.materialClassification);
- console.log('视觉节奏:', this.analysisResult.patternAnalysis.visualRhythm);
-
- // 生成需求映射
- this.generateRequirementMappingFromAnalysis();
-
- resolve();
- }, 2000);
- });
- }
- // 根据分析结果生成需求映射
- private generateRequirementMappingFromAnalysis(): void {
- if (!this.analysisResult) {
- return;
- }
- try {
- this.isGeneratingMapping = true;
- this.mappingError = null;
- // 使用需求映射服务生成映射
- this.requirementMappingService.generateRequirementMapping(this.analysisResult).subscribe({
- next: (mapping) => {
- this.requirementMapping = mapping;
- console.log('=== 需求映射生成成功 ===');
- console.log('需求映射结果:', this.requirementMapping);
-
- // 发出需求映射生成事件
- this.generateRequirementMapping.emit(this.requirementMapping);
- },
- error: (error) => {
- console.error('需求映射生成失败:', error);
- this.mappingError = '需求映射生成失败,请稍后重试';
- },
- complete: () => {
- this.isGeneratingMapping = false;
- }
- });
- } catch (error) {
- console.error('需求映射初始化失败:', error);
- this.mappingError = '需求映射初始化失败,请稍后重试';
- this.isGeneratingMapping = false;
- }
- }
- // 手动重新生成需求映射
- regenerateRequirementMapping(): void {
- if (!this.analysisResult) {
- return;
- }
-
- this.generateRequirementMappingFromAnalysis();
- }
- // 事件处理方法
- onClose() {
- this.closeModal.emit();
- }
- onBackdropClick(event: Event) {
- // 点击背景遮罩关闭弹窗
- this.onClose();
- }
- onAnalyzeColorsClick() {
- if (this.isAnalyzing || this.uploadedFiles.length === 0) {
- return;
- }
- this.animationState = 'loading';
- this.analyzeColors.emit(this.uploadedFiles);
- }
- onViewReportClick() {
- if (this.analysisResult) {
- // 创建一个新的窗口来显示完整报告
- const reportWindow = window.open('', '_blank', 'width=1200,height=800,scrollbars=yes,resizable=yes');
-
- if (reportWindow) {
- // 生成完整的HTML报告内容
- const reportHtml = this.generateFullReport();
- reportWindow.document.write(reportHtml);
- reportWindow.document.close();
- reportWindow.focus();
- } else {
- // 如果弹窗被阻止,则下载报告文件
- this.downloadReport();
- }
-
- // 触发事件给父组件
- this.viewReport.emit(this.analysisResult);
- }
- }
- // 工具方法
- shouldShowColorAnalysis(): boolean {
- return this.uploadType === 'image' || this.hasImageFiles();
- }
- formatFileSize(bytes: number): string {
- if (bytes === 0) return '0 Bytes';
-
- const k = 1024;
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
- const i = Math.floor(Math.log(bytes) / Math.log(k));
-
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
- }
- getFileTypeIcon(file: UploadedFile): string {
- if (file.type?.startsWith('image/')) {
- return 'image';
- } else if (file.type?.includes('pdf')) {
- return 'pdf';
- } else if (file.type?.includes('word') || file.type?.includes('doc')) {
- return 'document';
- } else {
- return 'file';
- }
- }
- hasImageFiles(): boolean {
- return this.uploadedFiles.some(file => file.type?.startsWith('image/'));
- }
- // 生成颜色描述文字
- generateColorDescription(): string {
- if (!this.analysisResult || !this.analysisResult.colors.length) {
- return '';
- }
- const colorDescriptions = this.analysisResult.colors.map(color => {
- const colorName = color.name || this.getColorName(color.hex);
- return `${colorName}(${color.hex}) ${color.percentage}%`;
- });
- return `主要色彩:${colorDescriptions.join('、')}`;
- }
- // 根据色值获取颜色名称
- private getColorName(hex: string): string {
- const colorMap: { [key: string]: string } = {
- '#FFFFFF': '纯白色',
- '#F5F5F5': '白烟',
- '#E5E5E5': '浅灰色',
- '#CCCCCC': '中灰色',
- '#999999': '深灰色',
- '#666666': '暗灰色',
- '#333333': '深暗灰',
- '#000000': '纯黑色',
- '#FF0000': '红色',
- '#00FF00': '酸橙色',
- '#0000FF': '蓝色',
- '#FFFF00': '黄色',
- '#FF00FF': '品红色',
- '#00FFFF': '青色',
- '#FFA500': '橙色',
- '#800080': '紫色',
- '#008000': '绿色',
- '#000080': '海军蓝',
- '#800000': '栗色',
- '#808000': '橄榄色',
- '#008080': '水鸭色',
- '#C0C0C0': '银色',
- '#808080': '灰色',
- '#FFE4E1': '雾玫瑰',
- '#F0F8FF': '爱丽丝蓝',
- '#FAEBD7': '古董白',
- '#F5F5DC': '米色',
- '#DEB887': '硬木色',
- '#A52A2A': '棕色',
- '#D2691E': '巧克力色',
- '#FF7F50': '珊瑚色',
- '#6495ED': '矢车菊蓝',
- '#DC143C': '深红色',
- '#00008B': '深蓝色',
- '#B8860B': '深金色',
- '#A9A9A9': '深灰色',
- '#006400': '深绿色',
- '#BDB76B': '深卡其色',
- '#8B008B': '深品红',
- '#556B2F': '深橄榄绿',
- '#FF8C00': '深橙色',
- '#9932CC': '深兰花紫',
- '#8B0000': '深红色2',
- '#E9967A': '深鲑鱼色',
- '#8FBC8F': '深海绿',
- '#483D8B': '深石板蓝',
- '#2F4F4F': '深石板灰',
- '#00CED1': '深绿松石',
- '#9400D3': '深紫罗兰',
- '#FF1493': '深粉红',
- '#00BFFF': '深天蓝',
- '#696969': '暗灰色2',
- '#1E90FF': '道奇蓝',
- '#B22222': '火砖色',
- '#FFFAF0': '花白色',
- '#228B22': '森林绿',
- '#DCDCDC': '淡灰色',
- '#F8F8FF': '幽灵白',
- '#FFD700': '金色',
- '#DAA520': '金麒麟色',
- '#ADFF2F': '绿黄色',
- '#F0FFF0': '蜜瓜色',
- '#FF69B4': '热粉红',
- '#CD5C5C': '印度红',
- '#4B0082': '靛青色',
- '#FFFFF0': '象牙色',
- '#F0E68C': '卡其色',
- '#E6E6FA': '薰衣草色',
- '#FFF0F5': '薰衣草红',
- '#7CFC00': '草坪绿',
- '#FFFACD': '柠檬绸',
- '#ADD8E6': '浅蓝色',
- '#F08080': '浅珊瑚色',
- '#E0FFFF': '浅青色',
- '#FAFAD2': '浅金菊黄',
- '#D3D3D3': '浅灰色2',
- '#90EE90': '浅绿色',
- '#FFB6C1': '浅粉红',
- '#FFA07A': '浅鲑鱼色',
- '#20B2AA': '浅海绿',
- '#87CEFA': '浅天蓝',
- '#778899': '浅石板灰',
- '#B0C4DE': '浅钢蓝',
- '#FFFFE0': '浅黄色',
- '#32CD32': '酸橙绿',
- '#FAF0E6': '亚麻色',
- '#66CDAA': '中海绿',
- '#0000CD': '中蓝色',
- '#BA55D3': '中兰花紫',
- '#9370DB': '中紫色',
- '#3CB371': '中海春绿',
- '#7B68EE': '中石板蓝',
- '#00FA9A': '中春绿',
- '#48D1CC': '中绿松石',
- '#C71585': '中紫罗兰红',
- '#191970': '午夜蓝',
- '#F5FFFA': '薄荷奶油',
- '#FFDEAD': '那瓦霍白',
- '#FDF5E6': '老花边',
- '#6B8E23': '橄榄褐色',
- '#FF4500': '橙红色',
- '#DA70D6': '兰花紫',
- '#EEE8AA': '灰秋麒麟',
- '#98FB98': '灰绿色',
- '#AFEEEE': '灰绿松石',
- '#DB7093': '灰紫罗兰红',
- '#FFEFD5': '番木瓜鞭',
- '#FFDAB9': '桃扑',
- '#CD853F': '秘鲁色',
- '#FFC0CB': '粉红色',
- '#DDA0DD': '洋李色',
- '#B0E0E6': '粉蓝色',
- '#BC8F8F': '玫瑰棕色',
- '#4169E1': '皇家蓝',
- '#8B4513': '马鞍棕色',
- '#FA8072': '鲑鱼色',
- '#F4A460': '沙棕色',
- '#2E8B57': '海绿色',
- '#FFF5EE': '海贝色',
- '#A0522D': '赭色',
- '#87CEEB': '天蓝色',
- '#6A5ACD': '石板蓝',
- '#708090': '石板灰',
- '#FFFAFA': '雪色',
- '#00FF7F': '春绿色',
- '#4682B4': '钢蓝色',
- '#D2B48C': '棕褐色',
- '#D8BFD8': '蓟色',
- '#FF6347': '番茄色',
- '#40E0D0': '绿松石',
- '#EE82EE': '紫罗兰',
- '#F5DEB3': '小麦色',
- '#9ACD32': '黄绿色'
- };
- // 如果找到精确匹配,返回对应名称
- if (colorMap[hex.toUpperCase()]) {
- return colorMap[hex.toUpperCase()];
- }
- // 否则根据RGB值判断颜色类型
- const rgb = this.hexToRgb(hex);
- if (!rgb) return '未知颜色';
- const { r, g, b } = rgb;
- const brightness = (r * 299 + g * 587 + b * 114) / 1000;
- // 判断是否为灰色系
- const isGray = Math.abs(r - g) < 30 && Math.abs(g - b) < 30 && Math.abs(r - b) < 30;
- if (isGray) {
- if (brightness > 240) return '浅灰白';
- if (brightness > 200) return '浅灰色';
- if (brightness > 160) return '中灰色';
- if (brightness > 120) return '深灰色';
- if (brightness > 80) return '暗灰色';
- return '深暗灰';
- }
- // 判断主要颜色倾向
- const max = Math.max(r, g, b);
- const min = Math.min(r, g, b);
- const saturation = max === 0 ? 0 : (max - min) / max;
- if (saturation < 0.2) {
- // 低饱和度,偏向灰色
- if (brightness > 200) return '浅灰色';
- if (brightness > 100) return '中灰色';
- return '深灰色';
- }
- // 高饱和度,判断色相
- let colorName = '';
- if (r >= g && r >= b) {
- if (g > b) {
- colorName = brightness > 150 ? '浅橙色' : '橙色';
- } else {
- colorName = brightness > 150 ? '浅红色' : '红色';
- }
- } else if (g >= r && g >= b) {
- if (r > b) {
- colorName = brightness > 150 ? '浅黄绿' : '黄绿色';
- } else {
- colorName = brightness > 150 ? '浅绿色' : '绿色';
- }
- } else {
- if (r > g) {
- colorName = brightness > 150 ? '浅紫色' : '紫色';
- } else {
- colorName = brightness > 150 ? '浅蓝色' : '蓝色';
- }
- }
- return colorName;
- }
- // 将十六进制颜色转换为RGB
- private hexToRgb(hex: string): { r: number; g: number; b: number } | null {
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
- return result ? {
- r: parseInt(result[1], 16),
- g: parseInt(result[2], 16),
- b: parseInt(result[3], 16)
- } : null;
- }
- // 复制颜色描述到剪贴板
- async copyColorDescription(): Promise<void> {
- const description = this.generateColorDescription();
- if (!description) return;
- try {
- await navigator.clipboard.writeText(description);
- this.copySuccess = true;
- console.log('颜色描述已复制到剪贴板');
-
- // 2秒后重置复制状态
- setTimeout(() => {
- this.copySuccess = false;
- }, 2000);
- } catch (err) {
- console.error('复制失败:', err);
- // 降级方案:使用传统方法
- this.fallbackCopyTextToClipboard(description);
- }
- }
- // 降级复制方案
- private fallbackCopyTextToClipboard(text: string): void {
- const textArea = document.createElement('textarea');
- textArea.value = text;
- textArea.style.top = '0';
- textArea.style.left = '0';
- textArea.style.position = 'fixed';
- textArea.style.opacity = '0';
- document.body.appendChild(textArea);
- textArea.focus();
- textArea.select();
- try {
- const successful = document.execCommand('copy');
- if (successful) {
- this.copySuccess = true;
- console.log('颜色描述已复制到剪贴板(降级方案)');
-
- // 2秒后重置复制状态
- setTimeout(() => {
- this.copySuccess = false;
- }, 2000);
- }
- } catch (err) {
- console.error('降级复制方案也失败了:', err);
- }
- document.body.removeChild(textArea);
- }
- // 生成完整的HTML报告
- private generateFullReport(): string {
- if (!this.analysisResult) return '';
-
- const colors = this.analysisResult.colors;
- const colorDescription = this.generateColorDescription();
-
- return `
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>图片颜色分析完整报告</title>
- <style>
- body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- line-height: 1.6;
- color: #333;
- max-width: 1200px;
- margin: 0 auto;
- padding: 40px 20px;
- background: #f8f9fa;
- }
- .report-container {
- background: white;
- border-radius: 16px;
- padding: 40px;
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
- }
- h1 {
- color: #1d1d1f;
- font-size: 32px;
- font-weight: 700;
- margin-bottom: 8px;
- text-align: center;
- }
- .subtitle {
- color: #666;
- font-size: 16px;
- text-align: center;
- margin-bottom: 40px;
- }
- .section {
- margin-bottom: 40px;
- }
- .section-title {
- color: #1d1d1f;
- font-size: 24px;
- font-weight: 600;
- margin-bottom: 20px;
- border-bottom: 2px solid #007AFF;
- padding-bottom: 8px;
- }
- .color-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 20px;
- margin-bottom: 30px;
- }
- .color-item {
- background: #f9f9f9;
- border-radius: 12px;
- padding: 20px;
- text-align: center;
- border: 1px solid #e5e5ea;
- transition: transform 0.2s ease;
- }
- .color-item:hover {
- transform: translateY(-2px);
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
- }
- .color-swatch {
- width: 80px;
- height: 80px;
- border-radius: 50%;
- margin: 0 auto 16px;
- border: 3px solid white;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
- }
- .color-name {
- font-weight: 600;
- font-size: 16px;
- color: #1d1d1f;
- margin-bottom: 4px;
- }
- .color-hex {
- font-family: 'Monaco', 'Menlo', monospace;
- font-size: 14px;
- color: #666;
- margin-bottom: 4px;
- }
- .color-percentage {
- font-size: 18px;
- font-weight: 700;
- color: #007AFF;
- }
- .description-section {
- background: #f9f9f9;
- border-radius: 12px;
- padding: 24px;
- border-left: 4px solid #007AFF;
- }
- .description-text {
- font-size: 16px;
- line-height: 1.8;
- color: #333;
- white-space: pre-wrap;
- }
- .stats-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
- gap: 16px;
- margin-top: 30px;
- }
- .stat-item {
- text-align: center;
- padding: 16px;
- background: #f0f8ff;
- border-radius: 8px;
- }
- .stat-value {
- font-size: 24px;
- font-weight: 700;
- color: #007AFF;
- }
- .stat-label {
- font-size: 14px;
- color: #666;
- margin-top: 4px;
- }
- .enhanced-section {
- background: #f8f9fa;
- border-radius: 12px;
- padding: 24px;
- margin-bottom: 30px;
- }
- .enhanced-title {
- font-size: 20px;
- font-weight: 600;
- color: #333;
- margin-bottom: 20px;
- display: flex;
- align-items: center;
- gap: 8px;
- }
- .enhanced-title::before {
- content: '';
- width: 4px;
- height: 20px;
- background: #007AFF;
- border-radius: 2px;
- }
- .analysis-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
- gap: 20px;
- }
- .analysis-card {
- background: white;
- border-radius: 8px;
- padding: 20px;
- border: 1px solid #e5e5ea;
- }
- .analysis-card h4 {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- margin-bottom: 12px;
- }
- .info-row {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 0;
- border-bottom: 1px solid #f0f0f0;
- }
- .info-row:last-child {
- border-bottom: none;
- }
- .info-label {
- font-weight: 500;
- color: #666;
- font-size: 14px;
- }
- .info-value {
- font-weight: 600;
- color: #333;
- font-size: 14px;
- }
- .tag-list {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-top: 8px;
- }
- .tag {
- padding: 4px 10px;
- background: #e3f2fd;
- color: #1976d2;
- border-radius: 12px;
- font-size: 12px;
- font-weight: 500;
- }
- .progress-bar {
- width: 100%;
- height: 8px;
- background: #f0f0f0;
- border-radius: 4px;
- overflow: hidden;
- margin: 8px 0;
- }
- .progress-fill {
- height: 100%;
- background: linear-gradient(90deg, #007AFF, #34C759);
- border-radius: 4px;
- }
- .footer {
- text-align: center;
- margin-top: 40px;
- padding-top: 20px;
- border-top: 1px solid #e5e5ea;
- color: #666;
- font-size: 14px;
- }
- @media print {
- body { background: white; }
- .report-container { box-shadow: none; }
- }
- </style>
- </head>
- <body>
- <div class="report-container">
- <h1>图片颜色分析报告</h1>
- <p class="subtitle">基于AI智能分析生成的详细颜色报告</p>
-
- <div class="section">
- <h2 class="section-title">颜色分析结果</h2>
- <div class="color-grid">
- ${colors.map(color => `
- <div class="color-item">
- <div class="color-swatch" style="background-color: ${color.hex}"></div>
- <div class="color-name">${this.getColorName(color.hex)}</div>
- <div class="color-hex">${color.hex}</div>
- <div class="color-percentage">${color.percentage}%</div>
- </div>
- `).join('')}
- </div>
-
- <div class="stats-grid">
- <div class="stat-item">
- <div class="stat-value">${colors.length}</div>
- <div class="stat-label">主要颜色数量</div>
- </div>
- <div class="stat-item">
- <div class="stat-value">${colors[0]?.percentage || 0}%</div>
- <div class="stat-label">主色占比</div>
- </div>
- <div class="stat-item">
- <div class="stat-value">${colors.reduce((sum, c) => sum + c.percentage, 0).toFixed(1)}%</div>
- <div class="stat-label">总覆盖率</div>
- </div>
- </div>
- </div>
- ${this.analysisResult.enhancedAnalysis ? `
- <div class="section">
- <h2 class="section-title">增强色彩分析</h2>
- <div class="enhanced-section">
- <div class="enhanced-title">色轮分析</div>
- <div class="analysis-grid">
- <div class="analysis-card">
- <h4>色相分布</h4>
- <div class="info-row">
- <span class="info-label">主色调:</span>
- <span class="info-value">${this.analysisResult.enhancedAnalysis.colorWheel.dominantHue}°</span>
- </div>
- <div class="info-row">
- <span class="info-label">饱和度范围:</span>
- <span class="info-value">${this.analysisResult.enhancedAnalysis.colorWheel.saturationRange.min}% - ${this.analysisResult.enhancedAnalysis.colorWheel.saturationRange.max}%</span>
- </div>
- </div>
- <div class="analysis-card">
- <h4>色彩心理学</h4>
- <div class="info-row">
- <span class="info-label">情绪:</span>
- <span class="info-value">${this.analysisResult.enhancedAnalysis.colorPsychology.mood}</span>
- </div>
- <div class="info-row">
- <span class="info-label">氛围:</span>
- <span class="info-value">${this.analysisResult.enhancedAnalysis.colorPsychology.atmosphere}</span>
- </div>
- <div class="tag-list">
- ${this.analysisResult.enhancedAnalysis.colorPsychology.suitableSpaces.map(space => `<span class="tag">${space}</span>`).join('')}
- </div>
- </div>
- </div>
- </div>
- </div>
- ` : ''}
- ${this.analysisResult.formAnalysis ? `
- <div class="section">
- <h2 class="section-title">形体分析</h2>
- <div class="enhanced-section">
- <div class="analysis-grid">
- <div class="analysis-card">
- <h4>线条分析</h4>
- <div class="info-row">
- <span class="info-label">主导线条:</span>
- <span class="info-value">${this.analysisResult.formAnalysis.lineAnalysis.dominantLineType}</span>
- </div>
- <div class="info-row">
- <span class="info-label">视觉流动:</span>
- <span class="info-value">${this.analysisResult.formAnalysis.lineAnalysis.visualFlow}</span>
- </div>
- </div>
- <div class="analysis-card">
- <h4>整体评估</h4>
- <div class="info-row">
- <span class="info-label">复杂度:</span>
- <span class="info-value">${this.analysisResult.formAnalysis.overallAssessment.complexity}%</span>
- </div>
- <div class="progress-bar">
- <div class="progress-fill" style="width: ${this.analysisResult.formAnalysis.overallAssessment.complexity}%"></div>
- </div>
- <div class="info-row">
- <span class="info-label">视觉冲击:</span>
- <span class="info-value">${this.analysisResult.formAnalysis.overallAssessment.visualImpact}%</span>
- </div>
- <div class="progress-bar">
- <div class="progress-fill" style="width: ${this.analysisResult.formAnalysis.overallAssessment.visualImpact}%"></div>
- </div>
- </div>
- </div>
- </div>
- </div>
- ` : ''}
- ${this.analysisResult.textureAnalysis ? `
- <div class="section">
- <h2 class="section-title">质感分析</h2>
- <div class="enhanced-section">
- <div class="analysis-grid">
- <div class="analysis-card">
- <h4>表面属性</h4>
- <div class="info-row">
- <span class="info-label">粗糙度:</span>
- <span class="info-value">${this.analysisResult.textureAnalysis.surfaceProperties.roughness}%</span>
- </div>
- <div class="progress-bar">
- <div class="progress-fill" style="width: ${this.analysisResult.textureAnalysis.surfaceProperties.roughness}%"></div>
- </div>
- <div class="info-row">
- <span class="info-label">光泽度:</span>
- <span class="info-value">${this.analysisResult.textureAnalysis.surfaceProperties.glossiness}%</span>
- </div>
- <div class="progress-bar">
- <div class="progress-fill" style="width: ${this.analysisResult.textureAnalysis.surfaceProperties.glossiness}%"></div>
- </div>
- </div>
- <div class="analysis-card">
- <h4>材质分类</h4>
- <div class="info-row">
- <span class="info-label">主要材质:</span>
- </div>
- <div class="tag-list">
- ${this.analysisResult.textureAnalysis.materialClassification.primaryMaterials.map((material: string) => `<span class="tag">${material}</span>`).join('')}
- </div>
- <div class="info-row">
- <span class="info-label">次要材质:</span>
- </div>
- <div class="tag-list">
- ${this.analysisResult.textureAnalysis.materialClassification.secondaryMaterials.map((material: string) => `<span class="tag">${material}</span>`).join('')}
- </div>
- </div>
- </div>
- </div>
- </div>
- ` : ''}
- ${this.analysisResult.patternAnalysis ? `
- <div class="section">
- <h2 class="section-title">纹理分析</h2>
- <div class="enhanced-section">
- <div class="analysis-grid">
- <div class="analysis-card">
- <h4>图案识别</h4>
- <div class="info-row">
- <span class="info-label">主要图案:</span>
- <span class="info-value">${this.analysisResult.patternAnalysis.patternRecognition.primaryPatterns[0]?.type || '无'}</span>
- </div>
- <div class="info-row">
- <span class="info-label">复杂度:</span>
- <span class="info-value">${this.analysisResult.patternAnalysis.patternRecognition.complexity}</span>
- </div>
- </div>
- <div class="analysis-card">
- <h4>视觉节奏</h4>
- <div class="info-row">
- <span class="info-label">节奏类型:</span>
- <span class="info-value">${this.analysisResult.patternAnalysis.visualRhythm.rhythmType}</span>
- </div>
- <div class="info-row">
- <span class="info-label">运动感:</span>
- <span class="info-value">${this.analysisResult.patternAnalysis.visualRhythm.movement}</span>
- </div>
- </div>
- </div>
- </div>
- </div>
- ` : ''}
- ${this.analysisResult.lightingAnalysis ? `
- <div class="section">
- <h2 class="section-title">灯光分析</h2>
- <div class="enhanced-section">
- <div class="analysis-grid">
- <div class="analysis-card">
- <h4>光源识别</h4>
- <div class="info-row">
- interface RequirementMapping {
- sceneGeneration: {
- baseScene: string; // 固定场景模板
- parameters: SceneParams; // 场景参数
- atmospherePreview: string; // 氛围感预览图
- };
- parameterMapping: {
- colorParams: ColorMappingParams;
- spaceParams: SpaceMappingParams;
- materialParams: MaterialMappingParams;
- };
- }interface RequirementMapping {
- sceneGeneration: {
- baseScene: string; // 固定场景模板
- parameters: SceneParams; // 场景参数
- atmospherePreview: string; // 氛围感预览图
- };
- parameterMapping: {
- colorParams: ColorMappingParams;
- spaceParams: SpaceMappingParams;
- materialParams: MaterialMappingParams;
- };
- }interface RequirementMapping {
- sceneGeneration: {
- baseScene: string; // 固定场景模板
- parameters: SceneParams; // 场景参数
- atmospherePreview: string; // 氛围感预览图
- };
- parameterMapping: {
- colorParams: ColorMappingParams;
- spaceParams: SpaceMappingParams;
- materialParams: MaterialMappingParams;
- };
- } <span class="info-label">主要光源:</span>
- <span class="info-value">${this.analysisResult.lightingAnalysis.lightSourceIdentification.primarySources[0]?.type || '无'} - ${this.analysisResult.lightingAnalysis.lightSourceIdentification.primarySources[0]?.subtype || ''}</span>
- </div>
- <div class="info-row">
- <span class="info-label">灯光复杂度:</span>
- <span class="info-value">${this.analysisResult.lightingAnalysis.lightSourceIdentification.lightingSetup.complexity}</span>
- </div>
- <div class="info-row">
- <span class="info-label">灯光风格:</span>
- <span class="info-value">${this.analysisResult.lightingAnalysis.lightSourceIdentification.lightingSetup.lightingStyle}</span>
- </div>
- </div>
- <div class="analysis-card">
- <h4>环境分析</h4>
- <div class="info-row">
- <span class="info-label">环境光强度:</span>
- <span class="info-value">${this.analysisResult.lightingAnalysis.ambientAnalysis.ambientLevel.strength}%</span>
- </div>
- <div class="info-row">
- <span class="info-label">灯光情绪:</span>
- <span class="info-value">${this.analysisResult.lightingAnalysis.ambientAnalysis.lightingMood.primary}</span>
- </div>
- <div class="info-row">
- <span class="info-label">情绪强度:</span>
- <span class="info-value">${this.analysisResult.lightingAnalysis.ambientAnalysis.lightingMood.intensity}%</span>
- </div>
- </div>
- </div>
- </div>
- </div>
- ` : ''}
-
- <div class="section">
- <h2 class="section-title">颜色描述文字</h2>
- <div class="description-section">
- <div class="description-text">${colorDescription}</div>
- </div>
- </div>
-
- <div class="footer">
- <p>报告生成时间:${new Date().toLocaleString('zh-CN')}</p>
- <p>由颜色分析系统自动生成</p>
- </div>
- </div>
- </body>
- </html>`;
- }
- // 下载报告文件
- private downloadReport(): void {
- if (!this.analysisResult) return;
-
- const reportHtml = this.generateFullReport();
- const blob = new Blob([reportHtml], { type: 'text/html;charset=utf-8' });
- const url = URL.createObjectURL(blob);
-
- const link = document.createElement('a');
- link.href = url;
- link.download = `颜色分析报告_${new Date().toISOString().slice(0, 10)}.html`;
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
-
- URL.revokeObjectURL(url);
- }
- private checkScreenSize(): void {
- const width = window.innerWidth;
- this.isMobile = width < 768;
- this.isTablet = width >= 768 && width < 1024;
- }
- private setupResizeListener(): void {
- // 可以添加更复杂的响应式逻辑
- }
- // 获取质感分析属性数组,用于模板中的循环显示
- getTextureProperties(): Array<{name: string, value: number}> {
- if (!this.analysisResult?.textureAnalysis?.surfaceProperties) {
- return [];
- }
-
- const properties = this.analysisResult.textureAnalysis.surfaceProperties;
- return [
- { name: '粗糙度', value: properties.roughness?.value || 0 },
- { name: '光泽度', value: properties.glossiness?.value || 0 },
- { name: '透明度', value: properties.transparency?.value || 0 },
- { name: '反射率', value: properties.reflectivity?.value || 0 }
- ];
- }
- }
|