upload-success-modal.component.html 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. <!-- 上传成功弹窗 -->
  2. @if (isVisible) {
  3. <div class="modal-backdrop"
  4. [@backdropAnimation]
  5. (click)="onBackdropClick($event)"
  6. style="position: fixed !important;
  7. top: 0 !important;
  8. left: 0 !important;
  9. right: 0 !important;
  10. bottom: 0 !important;
  11. width: 100vw !important;
  12. height: 100vh !important;
  13. display: flex !important;
  14. align-items: center !important;
  15. justify-content: center !important;
  16. z-index: 99999 !important;
  17. pointer-events: auto !important;
  18. background: rgba(0, 0, 0, 0.6) !important;">
  19. <div class="modal-container"
  20. [@modalAnimation]
  21. [class.mobile]="isMobile"
  22. [class.tablet]="isTablet"
  23. (click)="$event.stopPropagation()"
  24. style="pointer-events: auto !important;
  25. z-index: 100000 !important;
  26. position: relative !important;
  27. margin: auto !important;
  28. max-width: 600px !important;
  29. width: 90% !important;">
  30. <!-- 弹窗头部 -->
  31. <div class="modal-header" [@fadeInOut]>
  32. <div class="header-content">
  33. <div class="success-icon" [@successIconAnimation]>
  34. <svg width="32" height="32" viewBox="0 0 24 24" fill="none">
  35. <circle cx="12" cy="12" r="10" fill="#10B981"/>
  36. <path d="m9 12 2 2 4-4" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
  37. </svg>
  38. </div>
  39. <div class="header-text">
  40. <h2>上传成功!</h2>
  41. <p>已成功上传 {{ uploadedFiles.length }} 个文件</p>
  42. </div>
  43. </div>
  44. <button class="close-button"
  45. (click)="onClose()"
  46. [@buttonHoverAnimation]="buttonHoverState"
  47. (mouseenter)="buttonHoverState = 'hover'"
  48. (mouseleave)="buttonHoverState = 'normal'"
  49. aria-label="关闭弹窗"
  50. style="pointer-events: auto !important; z-index: 100001 !important; cursor: pointer !important; position: relative !important;">
  51. <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
  52. <path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
  53. </svg>
  54. </button>
  55. </div>
  56. <!-- 弹窗内容 -->
  57. <div class="modal-content">
  58. <!-- 已上传文件列表 -->
  59. <div class="uploaded-files" [@fileListAnimation]="uploadedFiles.length">
  60. <h3>已上传文件</h3>
  61. <div class="file-list">
  62. @for (file of uploadedFiles; track file.id) {
  63. <div class="file-item" [@slideInOut]>
  64. <div class="file-icon">
  65. @if (file.type?.startsWith('image/')) {
  66. <img [src]="file.preview || '/assets/images/file-image.svg'"
  67. [alt]="file.name"
  68. class="file-preview">
  69. } @else {
  70. <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
  71. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
  72. fill="#6B7280"/>
  73. <polyline points="14,2 14,8 20,8" fill="#9CA3AF"/>
  74. </svg>
  75. }
  76. </div>
  77. <div class="file-info">
  78. <span class="file-name">{{ file.name }}</span>
  79. <span class="file-size">{{ formatFileSize(file.size || 0) }}</span>
  80. </div>
  81. <div class="file-status success">
  82. <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
  83. <circle cx="12" cy="12" r="10" fill="#10B981"/>
  84. <path d="m9 12 2 2 4-4" stroke="white" stroke-width="2" stroke-linecap="round"/>
  85. </svg>
  86. </div>
  87. </div>
  88. }
  89. </div>
  90. </div>
  91. <!-- 颜色分析功能区域 -->
  92. @if (shouldShowColorAnalysis()) {
  93. <div class="color-analysis-section">
  94. <div class="section-header">
  95. <h4>智能颜色分析</h4>
  96. <p>基于上传的参考图片,自动提取主要色彩和材质信息</p>
  97. </div>
  98. <!-- 分析按钮或结果 -->
  99. @if (!analysisResult && !isAnalyzing) {
  100. <div class="analysis-action">
  101. <button class="analyze-btn"
  102. (click)="startColorAnalysis()"
  103. [disabled]="isAnalyzing">
  104. <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  105. <circle cx="12" cy="12" r="3"></circle>
  106. <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>
  107. </svg>
  108. 开始颜色分析
  109. </button>
  110. </div>
  111. }
  112. <!-- 分析进行中 -->
  113. @if (isAnalyzing) {
  114. <div class="analysis-loading">
  115. <div class="loading-spinner"></div>
  116. <div class="loading-text">
  117. <h5>正在分析图片颜色...</h5>
  118. <p>请稍候,系统正在提取主要色彩信息</p>
  119. </div>
  120. </div>
  121. }
  122. <!-- 分析结果 -->
  123. @if (analysisResult && !isAnalyzing) {
  124. <div class="analysis-result">
  125. <div class="result-header">
  126. <h5>颜色分析结果</h5>
  127. <span class="result-count">提取到 {{ analysisResult.colors.length }} 种主要颜色</span>
  128. </div>
  129. <div class="color-palette">
  130. @for (colorInfo of analysisResult.colors; track colorInfo.hex) {
  131. <div class="color-item">
  132. <div class="color-swatch"
  133. [style.background-color]="colorInfo.hex"
  134. [title]="colorInfo.hex + ' (' + colorInfo.percentage + '%)'">
  135. </div>
  136. <div class="color-info">
  137. <div class="color-value">{{ colorInfo.hex }}</div>
  138. <div class="color-percentage">{{ colorInfo.percentage }}%</div>
  139. </div>
  140. </div>
  141. }
  142. </div>
  143. <!-- 增强分析结果 -->
  144. @if (analysisResult.enhancedAnalysis) {
  145. <div class="enhanced-analysis">
  146. <div class="analysis-tabs">
  147. <button class="tab-btn"
  148. [class.active]="activeTab === 'color'"
  149. (click)="activeTab = 'color'">色彩分析</button>
  150. <button class="tab-btn"
  151. [class.active]="activeTab === 'form'"
  152. (click)="activeTab = 'form'">形体分析</button>
  153. <button class="tab-btn"
  154. [class.active]="activeTab === 'texture'"
  155. (click)="activeTab = 'texture'">质感分析</button>
  156. <button class="tab-btn"
  157. [class.active]="activeTab === 'pattern'"
  158. (click)="activeTab = 'pattern'">纹理分析</button>
  159. <button class="tab-btn"
  160. [class.active]="activeTab === 'lighting'"
  161. (click)="activeTab = 'lighting'">灯光分析</button>
  162. <button class="tab-btn"
  163. [class.active]="activeTab === 'mapping'"
  164. (click)="activeTab = 'mapping'">需求映射</button>
  165. </div>
  166. <div class="tab-content">
  167. <!-- 色彩分析标签页 -->
  168. @if (activeTab === 'color') {
  169. <div class="color-analysis-tab">
  170. <div class="analysis-section">
  171. <h6>色轮分析</h6>
  172. <!-- 色轮可视化 -->
  173. <div class="color-wheel-visualization">
  174. <svg width="200" height="200" viewBox="0 0 200 200" class="color-wheel-svg">
  175. <!-- 背景色轮 -->
  176. <defs>
  177. <linearGradient id="colorWheelGradient" x1="0%" y1="0%" x2="100%" y2="0%">
  178. <stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
  179. <stop offset="16.67%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
  180. <stop offset="33.33%" style="stop-color:rgb(0,255,0);stop-opacity:1" />
  181. <stop offset="50%" style="stop-color:rgb(0,255,255);stop-opacity:1" />
  182. <stop offset="66.67%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
  183. <stop offset="83.33%" style="stop-color:rgb(255,0,255);stop-opacity:1" />
  184. <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
  185. </linearGradient>
  186. </defs>
  187. <!-- 色轮圆环 -->
  188. <circle cx="100" cy="100" r="80" fill="none" stroke="url(#colorWheelGradient)" stroke-width="30" opacity="0.3"/>
  189. <!-- 主色调指示器 -->
  190. <line
  191. x1="100"
  192. y1="100"
  193. [attr.x2]="100 + 70 * Math.cos((analysisResult.enhancedAnalysis.colorWheel.dominantHue - 90) * Math.PI / 180)"
  194. [attr.y2]="100 + 70 * Math.sin((analysisResult.enhancedAnalysis.colorWheel.dominantHue - 90) * Math.PI / 180)"
  195. stroke="#333"
  196. stroke-width="3"
  197. stroke-linecap="round"/>
  198. <!-- 中心圆点 -->
  199. <circle cx="100" cy="100" r="5" fill="#333"/>
  200. <!-- 色彩分布点 -->
  201. @for (color of analysisResult.enhancedAnalysis.colorWheel.colorDistribution; track color.hue) {
  202. <circle
  203. [attr.cx]="100 + (60 + color.saturation * 0.2) * Math.cos((color.hue - 90) * Math.PI / 180)"
  204. [attr.cy]="100 + (60 + color.saturation * 0.2) * Math.sin((color.hue - 90) * Math.PI / 180)"
  205. [attr.r]="3 + color.percentage * 0.1"
  206. [attr.fill]="'hsl(' + color.hue + ',' + color.saturation + '%,' + color.brightness + '%)'"
  207. opacity="0.8"/>
  208. }
  209. </svg>
  210. </div>
  211. <div class="color-wheel-info">
  212. <div class="info-item">
  213. <span class="label">主色调:</span>
  214. <span class="value">{{ analysisResult.enhancedAnalysis.colorWheel.dominantHue }}°</span>
  215. </div>
  216. <div class="info-item">
  217. <span class="label">饱和度范围:</span>
  218. <span class="value">{{ (analysisResult.enhancedAnalysis.colorWheel.saturationRange?.min || 0) }}% - {{ (analysisResult.enhancedAnalysis.colorWheel.saturationRange?.max || 100) }}%</span>
  219. </div>
  220. </div>
  221. </div>
  222. <div class="analysis-section">
  223. <h6>色彩心理学</h6>
  224. <div class="psychology-info">
  225. <div class="mood-atmosphere">
  226. <span class="mood">{{ analysisResult.enhancedAnalysis.colorPsychology.mood || '无' }}</span>
  227. <span class="atmosphere">{{ analysisResult.enhancedAnalysis.colorPsychology.atmosphere || '无' }}</span>
  228. </div>
  229. <div class="suitable-spaces">
  230. @for (space of analysisResult.enhancedAnalysis.colorPsychology.suitableSpaces; track space) {
  231. <span class="space-tag">{{ space }}</span>
  232. }
  233. </div>
  234. </div>
  235. </div>
  236. </div>
  237. }
  238. <!-- 形体分析标签页 -->
  239. @if (activeTab === 'form' && analysisResult.formAnalysis) {
  240. <div class="form-analysis-tab">
  241. <div class="analysis-section">
  242. <h6>线条分析</h6>
  243. <div class="form-info">
  244. <div class="info-item">
  245. <span class="label">主导线条:</span>
  246. <span class="value">
  247. @switch (analysisResult.formAnalysis.lineAnalysis.dominantLineType) {
  248. @case ('straight') { 直线型 }
  249. @case ('curved') { 曲线型 }
  250. @case ('mixed') { 混合型 }
  251. @default { 未知 }
  252. }
  253. </span>
  254. </div>
  255. <div class="info-item">
  256. <span class="label">视觉流动:</span>
  257. <span class="value">
  258. 连续性 {{ analysisResult.formAnalysis.lineAnalysis.lineQuality.continuity || 0 }}%,
  259. 流畅度 {{ analysisResult.formAnalysis.lineAnalysis.lineQuality.smoothness || 0 }}%
  260. </span>
  261. </div>
  262. </div>
  263. </div>
  264. <div class="analysis-section">
  265. <h6>几何形状</h6>
  266. <div class="overall-info">
  267. <div class="metric">
  268. <span class="metric-label">复杂度</span>
  269. <div class="metric-bar">
  270. <div class="metric-fill" [style.width.%]="getComplexityScore(analysisResult.formAnalysis.geometryAnalysis.complexity)"></div>
  271. </div>
  272. <span class="metric-value">{{ analysisResult.formAnalysis.geometryAnalysis.complexity || '未知' }}</span>
  273. </div>
  274. <div class="metric">
  275. <span class="metric-label">视觉平衡</span>
  276. <div class="metric-bar">
  277. <div class="metric-fill" [style.width.%]="analysisResult.formAnalysis.geometryAnalysis.balance.visual"></div>
  278. </div>
  279. <span class="metric-value">{{ analysisResult.formAnalysis.geometryAnalysis.balance.visual || 0 }}%</span>
  280. </div>
  281. </div>
  282. </div>
  283. </div>
  284. }
  285. <!-- 质感分析标签页 -->
  286. @if (activeTab === 'texture' && analysisResult.textureAnalysis) {
  287. <div class="texture-analysis-tab">
  288. <div class="analysis-section">
  289. <h6>表面属性</h6>
  290. <div class="texture-properties">
  291. @for (property of getTextureProperties(); track property.name) {
  292. <div class="property-item">
  293. <span class="property-name">{{ property.name }}</span>
  294. <div class="property-bar">
  295. <div class="property-fill" [style.width.%]="property.value"></div>
  296. </div>
  297. <span class="property-value">{{ property.value }}%</span>
  298. </div>
  299. }
  300. </div>
  301. </div>
  302. <div class="analysis-section">
  303. <h6>材质分类</h6>
  304. <div class="material-tags">
  305. <span class="material-tag primary">
  306. {{ getMaterialName(analysisResult.textureAnalysis.materialClassification.primaryMaterial.category) }}
  307. @if (analysisResult.textureAnalysis.materialClassification.primaryMaterial.subcategory) {
  308. ({{ analysisResult.textureAnalysis.materialClassification.primaryMaterial.subcategory }})
  309. }
  310. </span>
  311. @for (material of analysisResult.textureAnalysis.materialClassification.secondaryMaterials; track material.category; let i = $index) {
  312. <span class="material-tag secondary">
  313. {{ getMaterialName(material.category) }} ({{ material.percentage }}%)
  314. </span>
  315. }
  316. </div>
  317. </div>
  318. </div>
  319. }
  320. <!-- 纹理分析标签页 -->
  321. @if (activeTab === 'pattern' && analysisResult.patternAnalysis) {
  322. <div class="pattern-analysis-tab">
  323. <div class="analysis-section">
  324. <h6>图案识别</h6>
  325. <div class="pattern-info">
  326. <div class="info-item">
  327. <span class="label">主要图案:</span>
  328. <span class="value">{{ getPatternTypeName(analysisResult.patternAnalysis.patternRecognition.primaryPatterns[0]?.type) || '无' }}</span>
  329. </div>
  330. <div class="info-item">
  331. <span class="label">复杂度:</span>
  332. <span class="value">{{ getComplexityName(analysisResult.patternAnalysis.patternRecognition.patternComplexity.level) || '无' }}</span>
  333. </div>
  334. </div>
  335. </div>
  336. <div class="analysis-section">
  337. <h6>视觉节奏</h6>
  338. <div class="rhythm-info">
  339. <div class="rhythm-tags">
  340. <span class="rhythm-tag">{{ getRhythmTypeName(analysisResult.patternAnalysis.visualRhythm.rhythmType.primary) || '无' }}</span>
  341. <span class="rhythm-tag">{{ getDirectionName(analysisResult.patternAnalysis.visualRhythm.movement.direction) || '无' }}</span>
  342. </div>
  343. </div>
  344. </div>
  345. </div>
  346. }
  347. <!-- 灯光分析标签页 -->
  348. @if (activeTab === 'lighting' && analysisResult.lightingAnalysis) {
  349. <div class="lighting-analysis-tab">
  350. <div class="analysis-section">
  351. <h6>光源识别</h6>
  352. <div class="lighting-info">
  353. <div class="info-item">
  354. <span class="label">主要光源:</span>
  355. <span class="value">
  356. @if (analysisResult.lightingAnalysis.lightSourceIdentification.primarySources && analysisResult.lightingAnalysis.lightSourceIdentification.primarySources.length > 0) {
  357. @for (source of analysisResult.lightingAnalysis.lightSourceIdentification.primarySources; track source.type; let last = $last) {
  358. {{ getLightSourceName(source.type, source.subtype) }}@if (!last) {, }
  359. }
  360. } @else {
  361. }
  362. </span>
  363. </div>
  364. <div class="info-item">
  365. <span class="label">灯光设置:</span>
  366. <span class="value">
  367. {{ getLightingStyleName(analysisResult.lightingAnalysis.lightSourceIdentification.lightingSetup.lightingStyle) || '无' }}
  368. ({{ analysisResult.lightingAnalysis.lightSourceIdentification.lightingSetup.sourceCount || 0 }} 个光源)
  369. </span>
  370. </div>
  371. </div>
  372. </div>
  373. <div class="analysis-section">
  374. <h6>环境分析</h6>
  375. <div class="ambient-info">
  376. <div class="info-item">
  377. <span class="label">环境光:</span>
  378. <span class="value">
  379. 强度 {{ analysisResult.lightingAnalysis.ambientAnalysis.ambientLevel.strength || 0 }}%,
  380. 均匀度 {{ analysisResult.lightingAnalysis.ambientAnalysis.ambientLevel.uniformity || 0 }}%
  381. </span>
  382. </div>
  383. <div class="info-item">
  384. <span class="label">灯光情绪:</span>
  385. <span class="value">{{ getLightingMoodName(analysisResult.lightingAnalysis.ambientAnalysis.lightingMood.primary) || '无' }}</span>
  386. </div>
  387. </div>
  388. </div>
  389. </div>
  390. }
  391. <!-- 需求映射标签页 -->
  392. @if (activeTab === 'mapping') {
  393. <div class="mapping-analysis-tab">
  394. @if (isGeneratingMapping) {
  395. <div class="mapping-loading">
  396. <div class="loading-spinner"></div>
  397. <div class="loading-text">
  398. <h6>正在生成需求映射...</h6>
  399. <p>基于分析结果生成场景参数和映射关系</p>
  400. </div>
  401. </div>
  402. } @else if (mappingError) {
  403. <div class="mapping-error">
  404. <div class="error-icon">⚠️</div>
  405. <div class="error-text">
  406. <h6>需求映射生成失败</h6>
  407. <p>{{ mappingError }}</p>
  408. <button class="retry-btn" (click)="regenerateRequirementMapping()">
  409. 重新生成
  410. </button>
  411. </div>
  412. </div>
  413. } @else if (requirementMapping) {
  414. <div class="mapping-result">
  415. <!-- 场景生成部分 -->
  416. <div class="analysis-section">
  417. <h6>场景生成</h6>
  418. <div class="scene-info">
  419. <div class="info-item">
  420. <span class="label">基础场景:</span>
  421. <span class="value">{{ requirementMapping.sceneGeneration.baseScene }}</span>
  422. </div>
  423. @if (requirementMapping.sceneGeneration.atmospherePreview) {
  424. <div class="atmosphere-preview">
  425. <img [src]="requirementMapping.sceneGeneration.atmospherePreview"
  426. alt="氛围感预览图"
  427. class="preview-image">
  428. </div>
  429. }
  430. </div>
  431. </div>
  432. <!-- 参数映射部分 -->
  433. <div class="analysis-section">
  434. <h6>参数映射</h6>
  435. <!-- 颜色参数 -->
  436. <div class="mapping-subsection">
  437. <h6>颜色映射</h6>
  438. <div class="color-mappings">
  439. @for (mapping of requirementMapping.parameterMapping.colorParams.primaryColors; track mapping.originalColor) {
  440. <div class="color-mapping-item">
  441. <div class="color-swatch" [style.background-color]="mapping.originalColor"></div>
  442. <span class="mapping-arrow">→</span>
  443. <div class="color-swatch" [style.background-color]="mapping.mappedColor"></div>
  444. <span class="usage-tag">{{ getColorUsageName(mapping.usage) }}</span>
  445. <span class="confidence">(权重 {{ mapping.weight }}%)</span>
  446. </div>
  447. }
  448. </div>
  449. <div class="color-params-info">
  450. <div class="param-item">
  451. <span class="label">色彩和谐:</span>
  452. <span class="value">{{ getColorHarmonyName(requirementMapping.parameterMapping.colorParams.colorHarmony) }}</span>
  453. </div>
  454. <div class="param-item">
  455. <span class="label">色温:</span>
  456. <span class="value">{{ getTemperatureName(requirementMapping.parameterMapping.colorParams.temperature) }}</span>
  457. </div>
  458. </div>
  459. </div>
  460. <!-- 空间参数 -->
  461. <div class="mapping-subsection">
  462. <h6>空间映射</h6>
  463. <div class="space-info">
  464. <div class="info-item">
  465. <span class="label">空间类型:</span>
  466. <span class="value">{{ getLayoutTypeName(requirementMapping.parameterMapping.spaceParams.layout.type) }}</span>
  467. </div>
  468. <div class="info-item">
  469. <span class="label">空间流线:</span>
  470. <span class="value">{{ getFlowTypeName(requirementMapping.parameterMapping.spaceParams.layout.flow) }}</span>
  471. </div>
  472. <div class="info-item">
  473. <span class="label">开放程度:</span>
  474. <span class="value">{{ requirementMapping.parameterMapping.spaceParams.scale.openness }}%</span>
  475. </div>
  476. <div class="info-item">
  477. <span class="label">尺寸:</span>
  478. <span class="value">
  479. {{ requirementMapping.parameterMapping.spaceParams.dimensions.width }} ×
  480. {{ requirementMapping.parameterMapping.spaceParams.dimensions.height }} ×
  481. {{ requirementMapping.parameterMapping.spaceParams.dimensions.depth }}
  482. {{ getDimensionUnitName(requirementMapping.parameterMapping.spaceParams.dimensions.unit) }}
  483. </span>
  484. </div>
  485. </div>
  486. </div>
  487. <!-- 材质参数 -->
  488. <div class="mapping-subsection">
  489. <h6>材质映射</h6>
  490. <div class="material-mappings">
  491. @for (mapping of requirementMapping.parameterMapping.materialParams.surfaceMaterials; track mapping.category) {
  492. <div class="material-mapping-item">
  493. <span class="source-texture">{{ getMaterialName(mapping.category) }}</span>
  494. <span class="mapping-arrow">→</span>
  495. <span class="target-material">{{ mapping.subtype }}</span>
  496. <span class="properties">
  497. {{ getFinishName(mapping.finish) }},
  498. {{ getPriorityName(mapping.priority) }}
  499. </span>
  500. <span class="coverage">({{ mapping.coverage }}%)</span>
  501. </div>
  502. }
  503. </div>
  504. </div>
  505. </div>
  506. <!-- 重新生成按钮 -->
  507. <div class="mapping-actions">
  508. <button class="regenerate-btn" (click)="regenerateRequirementMapping()">
  509. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  510. <polyline points="23 4 23 10 17 10"></polyline>
  511. <polyline points="1 20 1 14 7 14"></polyline>
  512. <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>
  513. </svg>
  514. 重新生成映射
  515. </button>
  516. </div>
  517. </div>
  518. } @else {
  519. <div class="mapping-placeholder">
  520. <div class="placeholder-icon">🎯</div>
  521. <div class="placeholder-text">
  522. <h6>需求映射未生成</h6>
  523. <p>请先完成分析,系统将自动生成需求映射</p>
  524. </div>
  525. </div>
  526. }
  527. </div>
  528. }
  529. </div>
  530. </div>
  531. }
  532. <div class="result-actions">
  533. <button class="view-report-btn" (click)="onViewReportClick()">
  534. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  535. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
  536. <polyline points="14,2 14,8 20,8"></polyline>
  537. </svg>
  538. 查看完整报告
  539. </button>
  540. </div>
  541. <!-- 颜色描述文字区域 -->
  542. <div class="color-description-section">
  543. <div class="description-header">
  544. <h6>颜色描述文字</h6>
  545. <p>适用于即梦等AI工具的颜色描述</p>
  546. </div>
  547. <div class="description-content">
  548. <div class="description-text"
  549. [class.has-content]="generateColorDescription()"
  550. #descriptionText>
  551. {{ generateColorDescription() || '暂无颜色分析结果' }}
  552. </div>
  553. @if (generateColorDescription()) {
  554. <button class="copy-description-btn"
  555. (click)="copyColorDescription()"
  556. [class.copied]="copySuccess"
  557. title="复制颜色描述">
  558. @if (copySuccess) {
  559. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  560. <polyline points="20,6 9,17 4,12"></polyline>
  561. </svg>
  562. 已复制
  563. } @else {
  564. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  565. <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
  566. <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
  567. </svg>
  568. 复制
  569. }
  570. </button>
  571. }
  572. </div>
  573. <div class="description-tips">
  574. <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  575. <circle cx="12" cy="12" r="10"></circle>
  576. <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
  577. <line x1="12" y1="17" x2="12.01" y2="17"></line>
  578. </svg>
  579. <span>复制后可直接粘贴到即梦等AI工具中,用于生成对应颜色风格的室内设计</span>
  580. </div>
  581. </div>
  582. </div>
  583. }
  584. <!-- 分析错误 -->
  585. @if (analysisError) {
  586. <div class="analysis-error">
  587. <div class="error-icon">
  588. <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  589. <circle cx="12" cy="12" r="10"></circle>
  590. <line x1="15" y1="9" x2="9" y2="15"></line>
  591. <line x1="9" y1="9" x2="15" y2="15"></line>
  592. </svg>
  593. </div>
  594. <div class="error-text">
  595. <h5>分析失败</h5>
  596. <p>{{ analysisError }}</p>
  597. </div>
  598. <button class="retry-btn" (click)="startColorAnalysis()">
  599. 重试
  600. </button>
  601. </div>
  602. }
  603. </div>
  604. }
  605. </div>
  606. <!-- 弹窗底部 -->
  607. <div class="modal-footer">
  608. <div class="footer-actions">
  609. <button class="secondary-btn" (click)="onClose()">
  610. 关闭
  611. </button>
  612. @if (shouldShowColorAnalysis() && !analysisResult) {
  613. <button class="primary-btn"
  614. (click)="startColorAnalysis()"
  615. [disabled]="isAnalyzing">
  616. @if (isAnalyzing) {
  617. <div class="btn-spinner"></div>
  618. 分析中...
  619. } @else {
  620. 开始分析
  621. }
  622. </button>
  623. }
  624. </div>
  625. </div>
  626. </div>
  627. </div>
  628. }