upload-success-modal.component.html 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. <!-- 上传成功弹窗 -->
  2. @if (isVisible) {
  3. <div class="modal-backdrop"
  4. [@backdropAnimation]
  5. (click)="onBackdropClick($event)">
  6. <div class="modal-container"
  7. [@modalAnimation]
  8. [class.mobile]="isMobile"
  9. [class.tablet]="isTablet"
  10. (click)="$event.stopPropagation()">
  11. <!-- 弹窗头部 -->
  12. <div class="modal-header" [@fadeInOut]>
  13. <div class="header-content">
  14. <div class="success-icon" [@successIconAnimation]>
  15. <svg width="32" height="32" viewBox="0 0 24 24" fill="none">
  16. <circle cx="12" cy="12" r="10" fill="#10B981"/>
  17. <path d="m9 12 2 2 4-4" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
  18. </svg>
  19. </div>
  20. <div class="header-text">
  21. <h2>上传成功!</h2>
  22. <p>已成功上传 {{ uploadedFiles.length }} 个文件</p>
  23. </div>
  24. </div>
  25. <button class="close-button"
  26. (click)="onClose()"
  27. [@buttonHoverAnimation]="buttonHoverState"
  28. (mouseenter)="buttonHoverState = 'hover'"
  29. (mouseleave)="buttonHoverState = 'normal'"
  30. aria-label="关闭弹窗">
  31. <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
  32. <path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
  33. </svg>
  34. </button>
  35. </div>
  36. <!-- 弹窗内容 -->
  37. <div class="modal-content">
  38. <!-- 已上传文件列表 -->
  39. <div class="uploaded-files" [@fileListAnimation]="uploadedFiles.length">
  40. <h3>已上传文件</h3>
  41. <div class="file-list">
  42. @for (file of uploadedFiles; track file.id) {
  43. <div class="file-item" [@slideInOut]>
  44. <div class="file-icon">
  45. @if (file.type?.startsWith('image/')) {
  46. <img [src]="file.preview || '/assets/images/file-image.svg'"
  47. [alt]="file.name"
  48. class="file-preview">
  49. } @else {
  50. <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
  51. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
  52. fill="#6B7280"/>
  53. <polyline points="14,2 14,8 20,8" fill="#9CA3AF"/>
  54. </svg>
  55. }
  56. </div>
  57. <div class="file-info">
  58. <span class="file-name">{{ file.name }}</span>
  59. <span class="file-size">{{ formatFileSize(file.size || 0) }}</span>
  60. </div>
  61. <div class="file-status success">
  62. <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
  63. <circle cx="12" cy="12" r="10" fill="#10B981"/>
  64. <path d="m9 12 2 2 4-4" stroke="white" stroke-width="2" stroke-linecap="round"/>
  65. </svg>
  66. </div>
  67. </div>
  68. }
  69. </div>
  70. </div>
  71. <!-- 颜色分析功能区域 -->
  72. @if (shouldShowColorAnalysis()) {
  73. <div class="color-analysis-section">
  74. <div class="section-header">
  75. <h4>智能颜色分析</h4>
  76. <p>基于上传的参考图片,自动提取主要色彩和材质信息</p>
  77. </div>
  78. <!-- 分析按钮或结果 -->
  79. @if (!analysisResult && !isAnalyzing) {
  80. <div class="analysis-action">
  81. <button class="analyze-btn"
  82. (click)="startColorAnalysis()"
  83. [disabled]="isAnalyzing">
  84. <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  85. <circle cx="12" cy="12" r="3"></circle>
  86. <path d="M12 1v6m0 6v6m11-7h-6m-6 0H1m15.5-6.5l-4.24 4.24M7.76 16.24l-4.24 4.24m0-8.48l4.24 4.24m8.48 0l4.24-4.24"></path>
  87. </svg>
  88. 开始颜色分析
  89. </button>
  90. </div>
  91. }
  92. <!-- 分析进行中 -->
  93. @if (isAnalyzing) {
  94. <div class="analysis-loading">
  95. <div class="loading-spinner"></div>
  96. <div class="loading-text">
  97. <h5>正在分析图片颜色...</h5>
  98. <p>请稍候,系统正在提取主要色彩信息</p>
  99. </div>
  100. </div>
  101. }
  102. <!-- 分析结果 -->
  103. @if (analysisResult && !isAnalyzing) {
  104. <div class="analysis-result">
  105. <div class="result-header">
  106. <h5>颜色分析结果</h5>
  107. <span class="result-count">提取到 {{ analysisResult.colors.length }} 种主要颜色</span>
  108. </div>
  109. <div class="color-palette">
  110. @for (colorInfo of analysisResult.colors; track colorInfo.hex) {
  111. <div class="color-item">
  112. <div class="color-swatch"
  113. [style.background-color]="colorInfo.hex"
  114. [title]="colorInfo.hex + ' (' + colorInfo.percentage + '%)'">
  115. </div>
  116. <div class="color-info">
  117. <div class="color-value">{{ colorInfo.hex }}</div>
  118. <div class="color-percentage">{{ colorInfo.percentage }}%</div>
  119. </div>
  120. </div>
  121. }
  122. </div>
  123. <!-- 增强分析结果 -->
  124. @if (analysisResult.enhancedAnalysis) {
  125. <div class="enhanced-analysis">
  126. <div class="analysis-tabs">
  127. <button class="tab-btn"
  128. [class.active]="activeTab === 'color'"
  129. (click)="activeTab = 'color'">色彩分析</button>
  130. <button class="tab-btn"
  131. [class.active]="activeTab === 'form'"
  132. (click)="activeTab = 'form'">形体分析</button>
  133. <button class="tab-btn"
  134. [class.active]="activeTab === 'texture'"
  135. (click)="activeTab = 'texture'">质感分析</button>
  136. <button class="tab-btn"
  137. [class.active]="activeTab === 'pattern'"
  138. (click)="activeTab = 'pattern'">纹理分析</button>
  139. <button class="tab-btn"
  140. [class.active]="activeTab === 'lighting'"
  141. (click)="activeTab = 'lighting'">灯光分析</button>
  142. <button class="tab-btn"
  143. [class.active]="activeTab === 'mapping'"
  144. (click)="activeTab = 'mapping'">需求映射</button>
  145. </div>
  146. <div class="tab-content">
  147. <!-- 色彩分析标签页 -->
  148. @if (activeTab === 'color') {
  149. <div class="color-analysis-tab">
  150. <div class="analysis-section">
  151. <h6>色轮分析</h6>
  152. <div class="color-wheel-info">
  153. <div class="info-item">
  154. <span class="label">主色调:</span>
  155. <span class="value">{{ analysisResult.enhancedAnalysis.colorWheel.dominantHue }}°</span>
  156. </div>
  157. <div class="info-item">
  158. <span class="label">饱和度范围:</span>
  159. <span class="value">{{ (analysisResult.enhancedAnalysis.colorWheel.saturationRange?.min || 0) }}% - {{ (analysisResult.enhancedAnalysis.colorWheel.saturationRange?.max || 100) }}%</span>
  160. </div>
  161. </div>
  162. </div>
  163. <div class="analysis-section">
  164. <h6>色彩心理学</h6>
  165. <div class="psychology-info">
  166. <div class="mood-atmosphere">
  167. <span class="mood">{{ analysisResult.enhancedAnalysis.colorPsychology.mood || '无' }}</span>
  168. <span class="atmosphere">{{ analysisResult.enhancedAnalysis.colorPsychology.atmosphere || '无' }}</span>
  169. </div>
  170. <div class="suitable-spaces">
  171. @for (space of analysisResult.enhancedAnalysis.colorPsychology.suitableSpaces; track space) {
  172. <span class="space-tag">{{ space }}</span>
  173. }
  174. </div>
  175. </div>
  176. </div>
  177. </div>
  178. }
  179. <!-- 形体分析标签页 -->
  180. @if (activeTab === 'form' && analysisResult.formAnalysis) {
  181. <div class="form-analysis-tab">
  182. <div class="analysis-section">
  183. <h6>线条分析</h6>
  184. <div class="form-info">
  185. <div class="info-item">
  186. <span class="label">主导线条:</span>
  187. <span class="value">{{ analysisResult.formAnalysis.lineAnalysis.dominantLineType || '无' }}</span>
  188. </div>
  189. <div class="info-item">
  190. <span class="label">视觉流动:</span>
  191. <span class="value">{{ analysisResult.formAnalysis.lineAnalysis.visualFlow || '无' }}</span>
  192. </div>
  193. </div>
  194. </div>
  195. <div class="analysis-section">
  196. <h6>整体评估</h6>
  197. <div class="overall-info">
  198. <div class="metric">
  199. <span class="metric-label">复杂度</span>
  200. <div class="metric-bar">
  201. <div class="metric-fill" [style.width.%]="analysisResult.formAnalysis.overallAssessment.complexity"></div>
  202. </div>
  203. <span class="metric-value">{{ analysisResult.formAnalysis.overallAssessment.complexity || 0 }}%</span>
  204. </div>
  205. <div class="metric">
  206. <span class="metric-label">视觉冲击</span>
  207. <div class="metric-bar">
  208. <div class="metric-fill" [style.width.%]="analysisResult.formAnalysis.overallAssessment.visualImpact"></div>
  209. </div>
  210. <span class="metric-value">{{ analysisResult.formAnalysis.overallAssessment.visualImpact || 0 }}%</span>
  211. </div>
  212. </div>
  213. </div>
  214. </div>
  215. }
  216. <!-- 质感分析标签页 -->
  217. @if (activeTab === 'texture' && analysisResult.textureAnalysis) {
  218. <div class="texture-analysis-tab">
  219. <div class="analysis-section">
  220. <h6>表面属性</h6>
  221. <div class="texture-properties">
  222. @for (property of getTextureProperties(); track property.name) {
  223. <div class="property-item">
  224. <span class="property-name">{{ property.name }}</span>
  225. <div class="property-bar">
  226. <div class="property-fill" [style.width.%]="property.value"></div>
  227. </div>
  228. <span class="property-value">{{ property.value }}%</span>
  229. </div>
  230. }
  231. </div>
  232. </div>
  233. <div class="analysis-section">
  234. <h6>材质分类</h6>
  235. <div class="material-tags">
  236. @for (material of analysisResult.textureAnalysis.materialClassification.primaryMaterials; track material; let i = $index) {
  237. <span class="material-tag primary">{{ material }}</span>
  238. }
  239. @for (material of analysisResult.textureAnalysis.materialClassification.secondaryMaterials; track material; let i = $index) {
  240. <span class="material-tag secondary">{{ material }}</span>
  241. }
  242. </div>
  243. </div>
  244. </div>
  245. }
  246. <!-- 纹理分析标签页 -->
  247. @if (activeTab === 'pattern' && analysisResult.patternAnalysis) {
  248. <div class="pattern-analysis-tab">
  249. <div class="analysis-section">
  250. <h6>图案识别</h6>
  251. <div class="pattern-info">
  252. <div class="info-item">
  253. <span class="label">主要图案:</span>
  254. <span class="value">{{ analysisResult.patternAnalysis.patternRecognition.primaryPatterns[0]?.type || '无' }}</span>
  255. </div>
  256. <div class="info-item">
  257. <span class="label">复杂度:</span>
  258. <span class="value">{{ analysisResult.patternAnalysis.patternRecognition.patternComplexity.level || '无' }}</span>
  259. </div>
  260. </div>
  261. </div>
  262. <div class="analysis-section">
  263. <h6>视觉节奏</h6>
  264. <div class="rhythm-info">
  265. <div class="rhythm-tags">
  266. <span class="rhythm-tag">{{ analysisResult.patternAnalysis.visualRhythm.rhythmType.primary || '无' }}</span>
  267. <span class="rhythm-tag">{{ analysisResult.patternAnalysis.visualRhythm.movement.direction || '无' }}</span>
  268. </div>
  269. </div>
  270. </div>
  271. </div>
  272. }
  273. <!-- 灯光分析标签页 -->
  274. @if (activeTab === 'lighting' && analysisResult.lightingAnalysis) {
  275. <div class="lighting-analysis-tab">
  276. <div class="analysis-section">
  277. <h6>光源识别</h6>
  278. <div class="lighting-info">
  279. <div class="info-item">
  280. <span class="label">主要光源:</span>
  281. <span class="value">{{ analysisResult.lightingAnalysis.lightSourceIdentification.primarySources?.join(', ') || '无' }}</span>
  282. </div>
  283. <div class="info-item">
  284. <span class="label">灯光设置:</span>
  285. <span class="value">{{ analysisResult.lightingAnalysis.lightSourceIdentification.lightingSetup || '无' }}</span>
  286. </div>
  287. </div>
  288. </div>
  289. <div class="analysis-section">
  290. <h6>环境分析</h6>
  291. <div class="ambient-info">
  292. <div class="info-item">
  293. <span class="label">环境光:</span>
  294. <span class="value">{{ analysisResult.lightingAnalysis.ambientAnalysis.ambientLight || '无' }}</span>
  295. </div>
  296. <div class="info-item">
  297. <span class="label">灯光情绪:</span>
  298. <span class="value">{{ analysisResult.lightingAnalysis.ambientAnalysis.lightingMood || '无' }}</span>
  299. </div>
  300. </div>
  301. </div>
  302. </div>
  303. }
  304. <!-- 需求映射标签页 -->
  305. @if (activeTab === 'mapping') {
  306. <div class="mapping-analysis-tab">
  307. @if (isGeneratingMapping) {
  308. <div class="mapping-loading">
  309. <div class="loading-spinner"></div>
  310. <div class="loading-text">
  311. <h6>正在生成需求映射...</h6>
  312. <p>基于分析结果生成场景参数和映射关系</p>
  313. </div>
  314. </div>
  315. } @else if (mappingError) {
  316. <div class="mapping-error">
  317. <div class="error-icon">⚠️</div>
  318. <div class="error-text">
  319. <h6>需求映射生成失败</h6>
  320. <p>{{ mappingError }}</p>
  321. <button class="retry-btn" (click)="regenerateRequirementMapping()">
  322. 重新生成
  323. </button>
  324. </div>
  325. </div>
  326. } @else if (requirementMapping) {
  327. <div class="mapping-result">
  328. <!-- 场景生成部分 -->
  329. <div class="analysis-section">
  330. <h6>场景生成</h6>
  331. <div class="scene-info">
  332. <div class="info-item">
  333. <span class="label">基础场景:</span>
  334. <span class="value">{{ requirementMapping.sceneGeneration.baseScene }}</span>
  335. </div>
  336. @if (requirementMapping.sceneGeneration.atmospherePreview) {
  337. <div class="atmosphere-preview">
  338. <img [src]="requirementMapping.sceneGeneration.atmospherePreview"
  339. alt="氛围感预览图"
  340. class="preview-image">
  341. </div>
  342. }
  343. </div>
  344. </div>
  345. <!-- 参数映射部分 -->
  346. <div class="analysis-section">
  347. <h6>参数映射</h6>
  348. <!-- 颜色参数 -->
  349. <div class="mapping-subsection">
  350. <h6>颜色映射</h6>
  351. <div class="color-mappings">
  352. @for (mapping of requirementMapping.parameterMapping.colorParams.primaryColors; track mapping.originalColor) {
  353. <div class="color-mapping-item">
  354. <div class="color-swatch" [style.background-color]="mapping.originalColor"></div>
  355. <span class="mapping-arrow">→</span>
  356. <span class="target-material">{{ mapping.mappedColor }}</span>
  357. <span class="confidence">({{ mapping.weight }}%)</span>
  358. </div>
  359. }
  360. </div>
  361. </div>
  362. <!-- 空间参数 -->
  363. <div class="mapping-subsection">
  364. <h6>空间映射</h6>
  365. <div class="space-info">
  366. <div class="info-item">
  367. <span class="label">空间类型:</span>
  368. <span class="value">{{ requirementMapping.parameterMapping.spaceParams.layout?.type }}</span>
  369. </div>
  370. <div class="info-item">
  371. <span class="label">开放程度:</span>
  372. <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale?.openness }}%</span>
  373. </div>
  374. </div>
  375. </div>
  376. <!-- 材质参数 -->
  377. <div class="mapping-subsection">
  378. <h6>材质映射</h6>
  379. <div class="material-mappings">
  380. @for (mapping of requirementMapping.parameterMapping.materialParams.surfaceMaterials; track mapping.category) {
  381. <div class="material-mapping-item">
  382. <span class="source-texture">{{ mapping.category }}</span>
  383. <span class="mapping-arrow">→</span>
  384. <span class="target-material">{{ mapping.subtype }}</span>
  385. <span class="properties">{{ mapping.finish }}, {{ mapping.priority }}</span>
  386. </div>
  387. }
  388. </div>
  389. </div>
  390. </div>
  391. <!-- 重新生成按钮 -->
  392. <div class="mapping-actions">
  393. <button class="regenerate-btn" (click)="regenerateRequirementMapping()">
  394. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  395. <polyline points="23 4 23 10 17 10"></polyline>
  396. <polyline points="1 20 1 14 7 14"></polyline>
  397. <path d="m3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
  398. </svg>
  399. 重新生成映射
  400. </button>
  401. </div>
  402. </div>
  403. } @else {
  404. <div class="mapping-placeholder">
  405. <div class="placeholder-icon">🎯</div>
  406. <div class="placeholder-text">
  407. <h6>需求映射未生成</h6>
  408. <p>请先完成分析,系统将自动生成需求映射</p>
  409. </div>
  410. </div>
  411. }
  412. </div>
  413. }
  414. </div>
  415. </div>
  416. }
  417. <div class="result-actions">
  418. <button class="view-report-btn" (click)="onViewReportClick()">
  419. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  420. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
  421. <polyline points="14,2 14,8 20,8"></polyline>
  422. </svg>
  423. 查看完整报告
  424. </button>
  425. </div>
  426. <!-- 颜色描述文字区域 -->
  427. <div class="color-description-section">
  428. <div class="description-header">
  429. <h6>颜色描述文字</h6>
  430. <p>适用于即梦等AI工具的颜色描述</p>
  431. </div>
  432. <div class="description-content">
  433. <div class="description-text"
  434. [class.has-content]="generateColorDescription()"
  435. #descriptionText>
  436. {{ generateColorDescription() || '暂无颜色分析结果' }}
  437. </div>
  438. @if (generateColorDescription()) {
  439. <button class="copy-description-btn"
  440. (click)="copyColorDescription()"
  441. [class.copied]="copySuccess"
  442. title="复制颜色描述">
  443. @if (copySuccess) {
  444. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  445. <polyline points="20,6 9,17 4,12"></polyline>
  446. </svg>
  447. 已复制
  448. } @else {
  449. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  450. <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
  451. <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
  452. </svg>
  453. 复制
  454. }
  455. </button>
  456. }
  457. </div>
  458. <div class="description-tips">
  459. <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  460. <circle cx="12" cy="12" r="10"></circle>
  461. <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
  462. <line x1="12" y1="17" x2="12.01" y2="17"></line>
  463. </svg>
  464. <span>复制后可直接粘贴到即梦等AI工具中,用于生成对应颜色风格的室内设计</span>
  465. </div>
  466. </div>
  467. </div>
  468. }
  469. <!-- 分析错误 -->
  470. @if (analysisError) {
  471. <div class="analysis-error">
  472. <div class="error-icon">
  473. <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  474. <circle cx="12" cy="12" r="10"></circle>
  475. <line x1="15" y1="9" x2="9" y2="15"></line>
  476. <line x1="9" y1="9" x2="15" y2="15"></line>
  477. </svg>
  478. </div>
  479. <div class="error-text">
  480. <h5>分析失败</h5>
  481. <p>{{ analysisError }}</p>
  482. </div>
  483. <button class="retry-btn" (click)="startColorAnalysis()">
  484. 重试
  485. </button>
  486. </div>
  487. }
  488. </div>
  489. }
  490. </div>
  491. <!-- 弹窗底部 -->
  492. <div class="modal-footer">
  493. <div class="footer-actions">
  494. <button class="secondary-btn" (click)="onClose()">
  495. 关闭
  496. </button>
  497. @if (shouldShowColorAnalysis() && !analysisResult) {
  498. <button class="primary-btn"
  499. (click)="startColorAnalysis()"
  500. [disabled]="isAnalyzing">
  501. @if (isAnalyzing) {
  502. <div class="btn-spinner"></div>
  503. 分析中...
  504. } @else {
  505. 开始分析
  506. }
  507. </button>
  508. }
  509. </div>
  510. </div>
  511. </div>
  512. </div>
  513. }