1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099 |
- <!-- 只展示修改处,未变更部分用占位注释表示 -->
- <div class="project-detail-container designer-page">
- <!-- 项目标题栏 -->
- <div class="project-header card">
- <div class="header-left">
- <div class="header-content">
- <h1>项目详情</h1>
- <div class="project-meta">
- <span class="project-id">项目ID: {{ projectId }}</span>
- @if (project) { <span class="project-status">{{ project.status }}</span> }
- <!-- 紧急与异常徽标(使用控制流指令) -->
- <!-- 保持已有@if 徽标逻辑不变 -->
- </div>
- </div>
- </div>
-
- <!-- 导航按钮区域 - 移动到标题左侧 -->
- <div class="header-nav">
- <app-vertical-nav
- [activeTab]="activeTab"
- (tabChange)="switchTab($event)"
- class="header-nav-tabs">
- </app-vertical-nav>
- </div>
- <!-- 四个环节圆圈导航 -->
- <div class="header-center">
- <div class="stage-navigation">
- <div class="stage-nav-item"
- [class.completed]="getSectionStatus('order') === 'completed'"
- [class.active]="getSectionStatus('order') === 'active'"
- (click)="toggleSection('order')">
- <div class="stage-nav-circle">
- <span class="stage-nav-number">1</span>
- </div>
- <div class="stage-nav-label">订单创建</div>
- </div>
- <div class="stage-nav-connector" [class.completed]="getSectionStatus('requirements') === 'completed' || getSectionStatus('requirements') === 'active'"></div>
- <div class="stage-nav-item"
- [class.completed]="getSectionStatus('requirements') === 'completed'"
- [class.active]="getSectionStatus('requirements') === 'active'"
- (click)="toggleSection('requirements')">
- <div class="stage-nav-circle">
- <span class="stage-nav-number">2</span>
- </div>
- <div class="stage-nav-label">确认需求</div>
- </div>
- <div class="stage-nav-connector" [class.completed]="getSectionStatus('delivery') === 'completed' || getSectionStatus('delivery') === 'active'"></div>
- <div class="stage-nav-item"
- [class.completed]="getSectionStatus('delivery') === 'completed'"
- [class.active]="getSectionStatus('delivery') === 'active'"
- (click)="toggleSection('delivery')">
- <div class="stage-nav-circle">
- <span class="stage-nav-number">3</span>
- </div>
- <div class="stage-nav-label">交付执行</div>
- </div>
- <div class="stage-nav-connector" [class.completed]="getSectionStatus('aftercare') === 'completed' || getSectionStatus('aftercare') === 'active'"></div>
- <div class="stage-nav-item"
- [class.completed]="getSectionStatus('aftercare') === 'completed'"
- [class.active]="getSectionStatus('aftercare') === 'active'"
- (click)="toggleSection('aftercare')">
- <div class="stage-nav-circle">
- <span class="stage-nav-number">4</span>
- </div>
- <div class="stage-nav-label">售后</div>
- </div>
- </div>
- </div>
-
- <div class="header-right">
- <div class="header-actions">
- <!-- 导出阶段报告 -->
- <button (click)="exportProjectReport()" class="action-btn secondary-btn">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
- <polyline points="14 2 14 8 20 8"></polyline>
- <line x1="16" y1="13" x2="8" y2="13"></line>
- <line x1="16" y1="17" x2="8" y2="17"></line>
- </svg>
- 导出报告
- </button>
-
- <button (click)="generateReminderMessage()" class="action-btn stagnation-btn">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <circle cx="12" cy="12" r="10"></circle>
- <polyline points="12 6 12 12 16 14"></polyline>
- </svg>
- 设置停滞
- </button>
-
- <!-- 切换项目下拉菜单 -->
- <div class="project-switcher">
- <button (click)="showDropdown = !showDropdown" class="action-btn switch-btn">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
- <polyline points="9 22 9 12 15 12 15 22"></polyline>
- </svg>
- 切换项目
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="dropdown-icon">
- <polyline points="6 9 12 15 18 9"></polyline>
- </svg>
- </button>
- @if (showDropdown) {
- <div class="switch-dropdown" (click)="$event.stopPropagation()">
- @for (p of projects; track p.id) {
- <div (click)="switchProject(p.id); showDropdown = false"
- [class.active]="p.id === projectId"
- class="project-item">
- <span class="project-name">{{ p.name }}</span>
- <span class="project-status-badge"
- [class.ongoing]="p.status === '进行中'"
- [class.completed]="p.status === '已完成'"
- [class.pending]="p.status === '待处理'">
- {{ p.status }}
- </span>
- </div>
- }
- </div>
- }
- </div>
- <!-- 返回工作台按钮 -->
- <button (click)="backToWorkbench()" class="action-btn back-btn primary">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M19 12H5"></path>
- <polyline points="12 19 5 12 12 5"></polyline>
- </svg>
- 返回工作台
- </button>
- </div>
- </div>
- </div>
- <!-- 图片预览模态框 -->
- @if (showImagePreview) {
- <div class="image-preview-modal" (click)="closeImagePreview()">
- <div class="modal-backdrop"></div>
- <div class="modal-content" (click)="$event.stopPropagation()">
- <div class="modal-header">
- <h3>{{ previewImageData?.name }}</h3>
- <button class="close-btn" (click)="closeImagePreview()">
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <line x1="18" y1="6" x2="6" y2="18"></line>
- <line x1="6" y1="6" x2="18" y2="18"></line>
- </svg>
- </button>
- </div>
- <div class="modal-body">
- <img [src]="previewImageData?.url" [alt]="previewImageData?.name" />
- </div>
- <div class="modal-footer">
- <div class="image-info">
- <span>文件大小: {{ previewImageData?.size }}</span>
- @if (previewImageData?.reviewStatus) {
- <span class="status-badge">{{ getImageReviewStatusText(previewImageData) }}</span>
- }
- </div>
- <div class="modal-actions">
- <button class="secondary-btn" (click)="downloadImage(previewImageData)">下载</button>
- <button class="danger-btn" (click)="removeImageFromPreview()">删除</button>
- </div>
- </div>
- </div>
- </div>
- }
- <!-- 提醒消息弹窗 -->
- @if (reminderMessage) {
- <div class="reminder-popup">
- {{ reminderMessage }}
- </div>
- }
- <!-- 标准阶段进度(5阶段) -->
- <!-- 已采用@for,不变 -->
- <!-- 顶部导航标签页 -->
- <!-- 原有代码保留 -->
- <!-- 水平导航栏 - 已移动到顶部,此处删除 -->
- <div class="tab-content">
- <!-- 项目进度标签页 -->
- @if (isActiveTab('progress')) {
- <div class="progress-tab-content">
- <div class="main-content-layout">
- <!-- 左侧保留 -->
- <div class="left-column">
- <div class="project-info-card card">
- <div class="card-header" (click)="toggleCustomerInfo()" style="cursor: pointer;">
- <h2>客户信息</h2>
- <div class="header-actions">
- <div class="sync-status" [class.syncing]="isSyncingCustomerInfo">
- @if (isSyncingCustomerInfo) {
- <span class="sync-indicator">
- <span class="sync-spinner"></span>
- 同步中...
- </span>
- } @else if (lastSyncTime) {
- <span class="sync-time">
- <i class="icon-sync"></i>
- 已同步: {{ formatTime(lastSyncTime) }}
- </span>
- }
- </div>
- <div class="toggle-icon" [class.expanded]="isCustomerInfoExpanded">
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
- </svg>
- </div>
- </div>
- </div>
- @if (project) {
- <div class="info-grid" [class.collapsed]="!isCustomerInfoExpanded">
- <!-- 显示订单创建时填写的客户信息,如果有的话 -->
- @if (orderCreationData?.customerInfo) {
- <!-- 基本客户信息 -->
- <div class="info-item key-info"><label>客户姓名</label><span>{{ orderCreationData.customerInfo.name }}</span></div>
- <div class="info-item key-info"><label>联系电话</label><span>{{ orderCreationData.customerInfo.phone }}</span></div>
- @if (orderCreationData.customerInfo.wechat) {
- <div class="info-item"><label>微信号</label><span>{{ orderCreationData.customerInfo.wechat }}</span></div>
- }
- <div class="info-item"><label>客户类型</label><span>{{ orderCreationData.customerInfo.customerType }}</span></div>
- @if (orderCreationData.customerInfo.source) {
- <div class="info-item"><label>客户来源</label><span>{{ orderCreationData.customerInfo.source }}</span></div>
- }
- @if (orderCreationData.customerInfo.remark) {
- <div class="info-item"><label>备注信息</label><span>{{ orderCreationData.customerInfo.remark }}</span></div>
- }
- } @else {
- <!-- 默认显示项目信息 -->
- <div class="info-item key-info"><label>项目负责人</label><span>{{ project.assigneeName }}</span></div>
- }
- <div class="info-item"><label>项目创建</label><span>{{ formatDate(project.createdAt) }}</span></div>
- <div class="info-item"><label>截止日期</label><span>{{ formatDate(project.deadline) }}</span></div>
- </div>
- <div class="tags-container" [class.collapsed]="!isCustomerInfoExpanded" style="margin-top: 12px;">
- <div class="tag-section">
- <h3>客户标签</h3>
- <div class="tags">
- @for (tag of project.customerTags; track $index) {
- <span class="tag">{{ tag.source }} · {{ tag.needType }} · {{ tag.preference }} · {{ tag.colorAtmosphere }}</span>
- }
- @if (project.customerTags.length === 0) { <span class="desc">暂无标签</span> }
- </div>
- </div>
- </div>
-
- } @else {
- <div class="loading-state">
- <div class="loading-spinner"></div>
- <div>正在加载客户信息...</div>
- </div>
- }
- </div>
-
- <!-- 方案确认卡片 - 优化样式与左侧客户信息卡片保持一致 -->
- <div class="proposal-confirm-card card">
- <div class="card-header">
- <h2>方案确认</h2>
- <div class="sync-status">
- <span class="progress-text">{{ getRequiredStagesProgress() }}% 完成</span>
- </div>
- </div>
-
- <!-- 素材解析状态 -->
- @if (isAnalyzing) {
- <div class="analysis-progress">
- <div class="progress-header">
- <h4>正在解析素材...</h4>
- <span class="progress-percentage">{{ analysisProgress.toFixed(0) }}%</span>
- </div>
- <div class="progress-bar">
- <div class="progress-fill" [style.width.%]="analysisProgress"></div>
- </div>
- <p class="progress-description">AI正在分析您的需求,生成专属设计方案</p>
- </div>
- }
-
- <!-- 方案展示区域 -->
- @if (proposalAnalysis && !isAnalyzing) {
- <div class="proposal-display">
- <!-- 方案概览 -->
- <div class="proposal-overview">
- <div class="overview-header">
- <h3>{{ proposalAnalysis.name }}</h3>
- <div class="proposal-meta">
- <span class="version">{{ proposalAnalysis.version }}</span>
- <span class="feasibility-score">可行性: {{ proposalAnalysis.feasibility.overall }}%</span>
- </div>
- </div>
-
- <!-- 快速摘要 -->
- <div class="quick-summary">
- <div class="summary-item">
- <span class="label">设计风格</span>
- <span class="value">{{ getStyleSummary() }}</span>
- </div>
- <div class="summary-item">
- <span class="label">色彩方案</span>
- <span class="value">{{ getColorSummary() }}</span>
- </div>
- <div class="summary-item">
- <span class="label">空间效率</span>
- <span class="value">{{ getSpaceEfficiency() }}%</span>
- </div>
- </div>
- </div>
-
- <!-- 详细方案内容 -->
- <div class="proposal-details">
- <!-- 材质规格与分类 -->
- <div class="detail-section">
- <div class="section-header">
- <h4>材质规格与分类</h4>
- <span class="section-count">{{ proposalAnalysis.materials.length }} 类材质</span>
- </div>
- <div class="materials-grid">
- @for (material of proposalAnalysis.materials; track material.category) {
- <div class="material-card" [class]="material.usage.priority">
- <div class="material-header">
- <h5>{{ material.category }}</h5>
- <span class="usage-percentage">{{ material.usage.percentage }}%</span>
- </div>
- <div class="material-specs">
- <div class="spec-item">
- <span class="spec-label">类型</span>
- <span class="spec-value">{{ material.specifications.type }}</span>
- </div>
- <div class="spec-item">
- <span class="spec-label">等级</span>
- <span class="spec-value">{{ material.specifications.grade }}</span>
- </div>
- <div class="spec-item">
- <span class="spec-label">应用区域</span>
- <span class="spec-value">{{ material.usage.area }}</span>
- </div>
- </div>
- <div class="material-properties">
- <span class="property-tag">{{ material.properties.texture }}</span>
- <span class="property-tag">{{ material.properties.color }}</span>
- </div>
- </div>
- }
- </div>
- </div>
-
- <!-- 设计风格特征 -->
- <div class="detail-section">
- <div class="section-header">
- <h4>设计风格特征</h4>
- <span class="style-name">{{ proposalAnalysis.designStyle.primaryStyle }}</span>
- </div>
- <div class="style-elements">
- @for (element of proposalAnalysis.designStyle.styleElements; track element.element) {
- <div class="style-element">
- <div class="element-header">
- <span class="element-name">{{ element.element }}</span>
- <span class="influence-score">{{ element.influence }}%</span>
- </div>
- <div class="element-description">{{ element.description }}</div>
- <div class="influence-bar">
- <div class="influence-fill" [style.width.%]="element.influence"></div>
- </div>
- </div>
- }
- </div>
- <div class="style-characteristics">
- @for (char of proposalAnalysis.designStyle.characteristics; track char.feature) {
- <div class="characteristic-item" [class]="char.importance">
- <span class="char-feature">{{ char.feature }}</span>
- <span class="char-value">{{ char.value }}</span>
- </div>
- }
- </div>
- </div>
-
- <!-- 色彩搭配方案及占比分析 -->
- <div class="detail-section">
- <div class="section-header">
- <h4>色彩搭配方案及占比分析</h4>
- <span class="harmony-type">{{ proposalAnalysis.colorScheme.harmony.type }}</span>
- </div>
- <div class="color-palette">
- @for (color of proposalAnalysis.colorScheme.palette; track color.hex) {
- <div class="color-item" [class]="color.role">
- <div class="color-swatch" [style.background-color]="color.hex"></div>
- <div class="color-info">
- <div class="color-name">{{ color.color }}</div>
- <div class="color-percentage">{{ color.percentage }}%</div>
- <div class="color-role">{{ color.role }}</div>
- </div>
- <div class="color-codes">
- <span class="hex-code">{{ color.hex }}</span>
- <span class="rgb-code">RGB({{ color.rgb }})</span>
- </div>
- </div>
- }
- </div>
- <div class="color-psychology">
- <div class="psychology-item">
- <span class="label">氛围营造</span>
- <span class="value">{{ proposalAnalysis.colorScheme.psychology.mood }}</span>
- </div>
- <div class="psychology-item">
- <span class="label">空间感受</span>
- <span class="value">{{ proposalAnalysis.colorScheme.psychology.atmosphere }}</span>
- </div>
- </div>
- </div>
-
- <!-- 空间尺寸数据及功能分区 -->
- <div class="detail-section">
- <div class="section-header">
- <h4>空间尺寸数据及功能分区</h4>
- <span class="total-area">总面积 {{ proposalAnalysis.spaceLayout.dimensions.area }}㎡</span>
- </div>
- <div class="space-dimensions">
- <div class="dimension-item">
- <span class="dim-label">长度</span>
- <span class="dim-value">{{ proposalAnalysis.spaceLayout.dimensions.length }}m</span>
- </div>
- <div class="dimension-item">
- <span class="dim-label">宽度</span>
- <span class="dim-value">{{ proposalAnalysis.spaceLayout.dimensions.width }}m</span>
- </div>
- <div class="dimension-item">
- <span class="dim-label">层高</span>
- <span class="dim-value">{{ proposalAnalysis.spaceLayout.dimensions.height }}m</span>
- </div>
- <div class="dimension-item">
- <span class="dim-label">体积</span>
- <span class="dim-value">{{ proposalAnalysis.spaceLayout.dimensions.volume }}m³</span>
- </div>
- </div>
- <div class="functional-zones">
- @for (zone of proposalAnalysis.spaceLayout.functionalZones; track zone.zone) {
- <div class="zone-card">
- <div class="zone-header">
- <h5>{{ zone.zone }}</h5>
- <div class="zone-stats">
- <span class="zone-area">{{ zone.area }}㎡</span>
- <span class="zone-percentage">{{ zone.percentage }}%</span>
- </div>
- </div>
- <div class="zone-requirements">
- <div class="requirements-label">功能需求</div>
- <div class="requirements-tags">
- @for (req of zone.requirements; track req) {
- <span class="requirement-tag">{{ req }}</span>
- }
- </div>
- </div>
- <div class="zone-furniture">
- <div class="furniture-label">家具配置</div>
- <div class="furniture-list">
- @for (furniture of zone.furniture; track furniture; let isLast = $last) {
- {{ furniture }}@if (!isLast) {、}
- }
- </div>
- </div>
- </div>
- }
- </div>
- </div>
-
- <!-- 预算与时间线 -->
- <div class="detail-section">
- <div class="section-header">
- <h4>预算与时间线</h4>
- <span class="total-budget">总预算 ¥{{ proposalAnalysis.budget.total.toLocaleString() }}</span>
- </div>
- <div class="budget-breakdown">
- @for (item of proposalAnalysis.budget.breakdown; track item.category) {
- <div class="budget-item">
- <div class="budget-category">{{ item.category }}</div>
- <div class="budget-amount">¥{{ item.amount.toLocaleString() }}</div>
- <div class="budget-percentage">{{ item.percentage }}%</div>
- <div class="budget-bar">
- <div class="budget-fill" [style.width.%]="item.percentage"></div>
- </div>
- </div>
- }
- </div>
- <div class="timeline">
- @for (phase of proposalAnalysis.timeline; track phase.phase) {
- <div class="timeline-item">
- <div class="phase-name">{{ phase.phase }}</div>
- <div class="phase-duration">{{ phase.duration }}天</div>
- </div>
- }
- </div>
- </div>
- </div>
-
- <!-- 方案操作按钮 -->
- <div class="proposal-actions">
- <button class="confirm-btn primary" (click)="confirmProposal()">
- 确认此方案
- </button>
- <button class="adjust-btn secondary" (click)="startMaterialAnalysis()">
- 重新分析
- </button>
- </div>
- </div>
- }
-
- <!-- 需求信息展示区域(原有内容,当没有方案分析时显示) -->
- @if (areRequiredStagesCompleted() && !proposalAnalysis && !isAnalyzing) {
- <div class="info-grid">
- <!-- 色调信息 -->
- @if (requirementKeyInfo.colorAtmosphere.description) {
- <div class="info-item">
- <label>色调</label>
- <span>{{ requirementKeyInfo.colorAtmosphere.description }}</span>
- @if (requirementKeyInfo.colorAtmosphere.mainColor) {
- <div class="color-preview" [style.background-color]="requirementKeyInfo.colorAtmosphere.mainColor"></div>
- }
- </div>
- }
-
- <!-- 材质信息 -->
- @if (requirementKeyInfo.colorAtmosphere.materials && requirementKeyInfo.colorAtmosphere.materials.length > 0) {
- <div class="info-item">
- <label>材质</label>
- <span>
- @for (material of requirementKeyInfo.colorAtmosphere.materials; track material; let isLast = $last) {
- {{ material }}@if (!isLast) {, }
- }
- </span>
- </div>
- }
-
- <!-- 空间结构 -->
- @if (requirementKeyInfo.spaceStructure.aspectRatio > 0) {
- <div class="info-item">
- <label>空间比例</label>
- <span>{{ requirementKeyInfo.spaceStructure.aspectRatio.toFixed(1) }}</span>
- </div>
- }
-
- @if (requirementKeyInfo.spaceStructure.ceilingHeight > 0) {
- <div class="info-item">
- <label>层高</label>
- <span>{{ requirementKeyInfo.spaceStructure.ceilingHeight }}m</span>
- </div>
- }
-
- <!-- 材质权重 -->
- @if (requirementKeyInfo.materialWeights.woodRatio > 0) {
- <div class="info-item">
- <label>木质比例</label>
- <span>{{ requirementKeyInfo.materialWeights.woodRatio }}%</span>
- </div>
- }
-
- @if (requirementKeyInfo.materialWeights.fabricRatio > 0) {
- <div class="info-item">
- <label>布艺比例</label>
- <span>{{ requirementKeyInfo.materialWeights.fabricRatio }}%</span>
- </div>
- }
-
- @if (requirementKeyInfo.materialWeights.metalRatio > 0) {
- <div class="info-item">
- <label>金属比例</label>
- <span>{{ requirementKeyInfo.materialWeights.metalRatio }}%</span>
- </div>
- }
-
- <!-- 预设氛围 -->
- @if (requirementKeyInfo.presetAtmosphere.name) {
- <div class="info-item">
- <label>预设氛围</label>
- <span>{{ requirementKeyInfo.presetAtmosphere.name }}</span>
- </div>
- }
-
- <!-- 小图时间 -->
- @if (getEstimatedSmallImageTime()) {
- <div class="info-item">
- <label>预计时间</label>
- <span>{{ getEstimatedSmallImageTime() }}</span>
- </div>
- }
- </div>
-
- <!-- 开始分析按钮 -->
- <div class="proposal-actions">
- <button class="confirm-btn" (click)="startMaterialAnalysis()">
- 开始素材解析
- </button>
- </div>
- } @else if (!areRequiredStagesCompleted()) {
- <!-- 等待状态 -->
- <div class="waiting-state">
- <div class="waiting-content">
- <h5>等待需求信息完善</h5>
- <p>需求沟通阶段完成后,方案确认功能将自动开启</p>
- <div class="progress-info">
- <span>当前进度: {{ getRequiredStagesProgress() }}%</span>
- </div>
- </div>
- </div>
- }
- </div>
- </div>
- <!-- 右侧三分之二 - 制作流程进度 -->
- <div class="right-column">
- <!-- 移除顶部四个圆圈,直接展示阶段内容 -->
-
- <!-- 串式流程:10个阶段横向排列(保持) -->
- <div class="stage-progress-container">
- @for (stage of getVisibleStages(); track stage) {
- <div class="vertical-stage-block" [attr.id]="stageToAnchor(stage)" [class.active]="getStageStatus(stage) === 'active'">
- @if (stage !== '订单创建') {
- <div class="vertical-stage-header">
- <span class="dot" [class.completed]="getStageStatus(stage) === 'completed'" [class.active]="getStageStatus(stage) === 'active'"></span>
- <h3>{{ stage }}</h3>
- </div>
- }
- <!-- 直接复用原阶段内容卡片:按stage匹配显示 -->
- <div class="vertical-stage-body">
- @if (stage === '订单创建') {
- <app-consultation-order-panel
- [syncData]="projectData"
- (orderCreated)="onConsultationOrderSubmit($event)"
- (projectCreated)="onProjectCreated($event)"
- ></app-consultation-order-panel>
- } @else if (stage === '需求沟通') {
- <app-requirements-confirm-card
- (requirementConfirmed)="syncRequirementKeyInfo($event)"
- (progressUpdated)="syncRequirementKeyInfo($event)"
- (stageCompleted)="onRequirementsStageCompleted($event)"
- (dataUpdated)="onRequirementDataUpdated($event)">
- </app-requirements-confirm-card>
- } @else if (stage === '方案确认') {
- <!-- 方案确认阶段 - 已移动到左侧列 -->
- <div class="proposal-confirm-placeholder">
- <div class="placeholder-message">
- <p>方案确认卡片已移动到左侧区域</p>
- </div>
- </div>
- } @else if (stage === '建模') {
- <div class="upload-section">
- <div class="upload-header">
- <h4>上传白模图片</h4>
- <span class="hint">支持:JPG/PNG,不强制4K</span>
- </div>
- @if (canEditSection('delivery')) {
- <div class="upload-dropzone"
- (click)="whiteModelImages.length === 0 ? triggerFileInput('whiteModel') : null"
- (dragover)="whiteModelImages.length === 0 ? onDragOver($event) : null"
- (dragleave)="whiteModelImages.length === 0 ? onDragLeave($event) : null"
- (drop)="whiteModelImages.length === 0 ? onFileDrop($event, 'whiteModel') : null"
- [class.drag-over]="isDragOver && whiteModelImages.length === 0"
- [class.has-images]="whiteModelImages.length > 0">
- @if (whiteModelImages.length === 0) {
- <div class="upload-icon"></div>
- <div class="upload-text">点击此处或拖拽文件到此处上传</div>
- <div class="upload-hint">支持 JPG、PNG 格式,单个文件最大 10MB</div>
- } @else {
- <div class="uploaded-images-grid">
- @for (img of whiteModelImages; track img.id) {
- <div class="uploaded-image-item" (click)="previewImage(img)">
- <img [src]="img.url" [alt]="img.name" />
- <div class="image-overlay">
- <div class="image-name">{{ img.name }}</div>
- <div class="image-actions">
- <button class="preview-btn" (click)="$event.stopPropagation(); previewImage(img)">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
- <circle cx="12" cy="12" r="3"></circle>
- </svg>
- </button>
- <button class="remove-btn" (click)="$event.stopPropagation(); removeWhiteModelImage(img.id)">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <line x1="18" y1="6" x2="6" y2="18"></line>
- <line x1="6" y1="6" x2="18" y2="18"></line>
- </svg>
- </button>
- </div>
- </div>
- </div>
- }
- <div class="add-more-btn" (click)="triggerFileInput('whiteModel')">
- <div class="add-icon">+</div>
- <div class="add-text">添加更多</div>
- </div>
- </div>
- }
- <input type="file"
- id="whiteModelFileInput"
- accept="{{allowedImageTypes}}"
- multiple
- (change)="onWhiteModelSelected($event)"
- style="display: none;" />
- </div>
- }
- <div class="upload-actions">
- @if (canEditSection('delivery')) {
- <button class="primary-btn" [disabled]="whiteModelImages.length===0" (click)="confirmWhiteModelUpload()">确认上传</button>
- }
- @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('white')">同步图片信息</button> }
- @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
- </div>
- </div>
- <div class="model-check-section">
- <h4>模型差异检查清单</h4>
- <div class="checklist">
- @for (item of modelCheckItems; track item.id) {
- <div class="checklist-item">
- <label class="checklist-label">
- <input type="checkbox" class="custom-checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, $any($event.target).checked)" [disabled]="!canEditSection('delivery')">
- <span class="checklist-text">{{ item.name }}</span>
- </label>
- <span class="check-status">{{ item.isPassed ? '已通过' : '待处理' }}</span>
- </div>
- }
- </div>
- </div>
- } @else if (stage === '软装') {
- <div class="softdecor-section">
- <h4>软装清单素材</h4>
- <div class="upload-section">
- <div class="upload-header">
- <h4>上传软装小图</h4>
- <span class="hint">建议 ≤1MB 的 JPG/PNG 小图</span>
- </div>
- @if (canEditSection('delivery')) {
- <div class="upload-dropzone"
- (click)="softDecorImages.length === 0 ? triggerFileInput('softDecor') : null"
- (dragover)="softDecorImages.length === 0 ? onDragOver($event) : null"
- (dragleave)="softDecorImages.length === 0 ? onDragLeave($event) : null"
- (drop)="softDecorImages.length === 0 ? onFileDrop($event, 'softDecor') : null"
- [class.drag-over]="isDragOver && softDecorImages.length === 0"
- [class.has-images]="softDecorImages.length > 0">
- @if (softDecorImages.length === 0) {
- <div class="upload-icon"></div>
- <div class="upload-text">点击此处或拖拽文件到此处上传</div>
- <div class="upload-hint">支持 JPG、PNG 格式,建议文件大小 ≤1MB</div>
- } @else {
- <div class="uploaded-images-grid">
- @for (img of softDecorImages; track img.id) {
- <div class="uploaded-image-item" (click)="previewImage(img)">
- <img [src]="img.url" [alt]="img.name" />
- <div class="image-overlay">
- <div class="image-name">{{ img.name }}</div>
- <div class="image-actions">
- <button class="preview-btn" (click)="$event.stopPropagation(); previewImage(img)">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
- <circle cx="12" cy="12" r="3"></circle>
- </svg>
- </button>
- <button class="remove-btn" (click)="$event.stopPropagation(); removeSoftDecorImage(img.id)">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <line x1="18" y1="6" x2="6" y2="18"></line>
- <line x1="6" y1="6" x2="18" y2="18"></line>
- </svg>
- </button>
- </div>
- </div>
- </div>
- }
- <div class="add-more-btn" (click)="triggerFileInput('softDecor')">
- <div class="add-icon">+</div>
- <div class="add-text">添加更多</div>
- </div>
- </div>
- }
- <input type="file"
- id="softDecorFileInput"
- accept="{{allowedImageTypes}}"
- multiple
- (change)="onSoftDecorSmallPicsSelected($event)"
- style="display: none;" />
- </div>
- }
- <div class="upload-actions">
- @if (canEditSection('delivery')) {
- <button class="primary-btn" [disabled]="softDecorImages.length===0" (click)="confirmSoftDecorUpload()">确认上传</button>
- }
- @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('soft')">同步图片信息</button> }
- @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
- </div>
- </div>
- </div>
- } @else if (stage === '渲染') {
- <div class="render-progress-section">
- @if (isLoadingRenderProgress) {
- <div class="loading-state">
- <div class="loading-spinner"></div>
- <div>正在加载渲染进度...</div>
- </div>
- }
- @if (errorLoadingRenderProgress) {
- <div class="error-state">
- <div>渲染进度加载失败</div>
- <button class="secondary-btn" (click)="retryLoadRenderProgress()">重试</button>
- </div>
- }
- @if (!isLoadingRenderProgress && !errorLoadingRenderProgress && renderProgress) {
- <div class="progress-info" style="display:flex;gap:16px;align-items:center;margin:12px 0;">
- <span>状态:{{ renderProgress.status }}</span>
- <span>完成度:{{ renderProgress.completionRate }}%</span>
- <span>预计剩余:{{ renderProgress.estimatedTimeRemaining }} 小时</span>
- </div>
- }
- <div class="upload-section">
- <div class="upload-header">
- <h4>上传渲染大图</h4>
- <span class="hint">需满足4K标准(最长边 ≥ 4000px)</span>
- </div>
- @if (canEditSection('delivery')) {
- <div class="upload-dropzone"
- (click)="renderLargeImages.length === 0 ? triggerFileInput('render') : null"
- (dragover)="renderLargeImages.length === 0 ? onDragOver($event) : null"
- (dragleave)="renderLargeImages.length === 0 ? onDragLeave($event) : null"
- (drop)="renderLargeImages.length === 0 ? onFileDrop($event, 'render') : null"
- [class.drag-over]="isDragOver && renderLargeImages.length === 0"
- [class.has-images]="renderLargeImages.length > 0">
- @if (renderLargeImages.length === 0) {
- <div class="upload-icon"></div>
- <div class="upload-text">点击此处或拖拽文件到此处上传</div>
- <div class="upload-hint">支持 JPG、PNG 格式,需满足4K标准(最长边 ≥ 4000px)</div>
- } @else {
- <div class="uploaded-images-grid">
- @for (img of renderLargeImages; track img.id) {
- <div class="uploaded-image-item" (click)="previewImage(img)">
- <img [src]="img.url" [alt]="img.name" />
- <div class="image-overlay">
- <div class="image-name">{{ img.name }}</div>
- <div class="image-actions">
- <button class="preview-btn" (click)="$event.stopPropagation(); previewImage(img)">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
- <circle cx="12" cy="12" r="3"></circle>
- </svg>
- </button>
- <button class="remove-btn" (click)="$event.stopPropagation(); removeRenderLargeImage(img.id)">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <line x1="18" y1="6" x2="6" y2="18"></line>
- <line x1="6" y1="6" x2="18" y2="18"></line>
- </svg>
- </button>
- </div>
- </div>
- </div>
- }
- <div class="add-more-btn" (click)="triggerFileInput('render')">
- <div class="add-icon">+</div>
- <div class="add-text">添加更多</div>
- </div>
- </div>
- }
- <input type="file"
- id="renderFileInput"
- accept="{{allowedImageTypes}}"
- multiple
- (change)="onRenderLargePicsSelected($event)"
- style="display: none;" />
- </div>
- }
- <div class="upload-actions">
- @if (canEditSection('delivery')) {
- <button class="primary-btn" [disabled]="renderLargeImages.length===0" (click)="confirmRenderUpload()">确认上传</button>
- }
- @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('render')">同步图片信息</button> }
- @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
- </div>
- </div>
- </div>
- } @else if (stage === '后期') {
- <div class="post-process-section">
- <div class="upload-section">
- <div class="upload-header">
- <h4>上传后期处理图片</h4>
- <span class="hint">包含调色、修图、特效等后期处理结果</span>
- </div>
- @if (canEditSection('delivery')) {
- <div class="upload-dropzone"
- (click)="postProcessImages.length === 0 ? triggerFileInput('postProcess') : null"
- (dragover)="postProcessImages.length === 0 ? onDragOver($event) : null"
- (dragleave)="postProcessImages.length === 0 ? onDragLeave($event) : null"
- (drop)="postProcessImages.length === 0 ? onFileDrop($event, 'postProcess') : null"
- [class.drag-over]="isDragOver && postProcessImages.length === 0"
- [class.has-images]="postProcessImages.length > 0">
- @if (postProcessImages.length === 0) {
- <div class="upload-icon"></div>
- <div class="upload-text">点击此处或拖拽文件到此处上传</div>
- <div class="upload-hint">支持 JPG、PNG 格式,展示后期处理效果</div>
- } @else {
- <div class="uploaded-images-grid">
- @for (img of postProcessImages; track img.id) {
- <div class="uploaded-image-item" (click)="previewImage(img)">
- <img [src]="img.url" [alt]="img.name" />
- <div class="image-overlay">
- <div class="image-name">{{ img.name }}</div>
- <div class="image-actions">
- <button class="preview-btn" (click)="$event.stopPropagation(); previewImage(img)">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
- <circle cx="12" cy="12" r="3"></circle>
- </svg>
- </button>
- <button class="remove-btn" (click)="$event.stopPropagation(); removePostProcessImage(img.id)">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
- <line x1="18" y1="6" x2="6" y2="18"></line>
- <line x1="6" y1="6" x2="18" y2="18"></line>
- </svg>
- </button>
- </div>
- </div>
- </div>
- }
- <div class="add-more-btn" (click)="triggerFileInput('postProcess')">
- <div class="add-icon">+</div>
- <div class="add-text">添加更多</div>
- </div>
- </div>
- }
- <input type="file"
- id="postProcessFileInput"
- accept="{{allowedImageTypes}}"
- multiple
- (change)="onPostProcessPicsSelected($event)"
- style="display: none;" />
- </div>
- }
- <div class="upload-actions">
- @if (canEditSection('delivery')) {
- <button class="primary-btn" [disabled]="postProcessImages.length===0" (click)="confirmPostProcessUpload()">确认上传</button>
- }
- @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('postProcess')">同步图片信息</button> }
- @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
- </div>
- </div>
- </div>
- } @else if (stage === '尾款结算') {
- <div class="aftercare-section-container">
- <app-settlement-card [settlements]="settlements"></app-settlement-card>
- </div>
- @if (canEditSection('aftercare')) {
- <div class="upload-actions">
- <button
- class="primary-btn"
- [class.completed]="isSettlementCompleted"
- [disabled]="isConfirmingSettlement"
- (click)="confirmSettlement()">
- @if (isConfirmingSettlement) {
- <span class="loading-spinner"></span>
- 确认中...
- } @else if (isSettlementCompleted) {
- <span class="check-icon">✓</span>
- 已确认完成
- } @else {
- 确认尾款结算完成
- }
- </button>
- <button class="secondary-btn" (click)="uploadPaymentProof()">
- <span class="upload-icon">📎</span>
- 上传支付凭证
- </button>
- </div>
- }
- } @else if (stage === '客户评价') {
- <div class="aftercare-section-container">
- <app-customer-review-card [feedbacks]="feedbacks"></app-customer-review-card>
- </div>
- @if (canEditSection('aftercare')) {
- <div class="upload-actions">
- <button class="primary-btn" (click)="confirmCustomerReview()">确认客户评价完成</button>
- </div>
- }
- } @else if (stage === '投诉处理') {
- <div class="aftercare-section-container">
- <app-complaint-card [complaints]="exceptionHistories"></app-complaint-card>
- </div>
- @if (canEditSection('aftercare')) {
- <div class="upload-actions">
- <button class="primary-btn" (click)="confirmComplaint()">确认投诉处理完成</button>
- </div>
- }
- }
- </div>
- </div>
- }
- </div>
- </div>
- </div>
- </div>
- }
-
- <!-- 项目人员标签页 -->
- @if (isActiveTab('members')) {
- <div class="members-tab-content">
- <div class="main-content-layout">
- <!-- 项目人员内容 -->
- <div class="members-content">
- <div class="members-header">
- <h2>项目成员</h2>
- <p class="members-count">共 {{ projectMembers.length }} 名成员</p>
- </div>
-
- <div class="members-grid">
- @for (member of projectMembers; track member.id) {
- <div class="member-card">
- <div class="member-avatar">
- <img [src]="member.avatar" [alt]="member.name">
- </div>
- <div class="member-info">
- <h3 class="member-name">{{ member.name }}</h3>
- <p class="member-role">{{ member.role }}</p>
- <div class="member-stats">
- <div class="stat-item">
- <span class="stat-label">技能匹配度</span>
- <div class="progress-bar">
- <div class="progress-fill" [style.width.%]="member.skillMatch"></div>
- </div>
- <span class="stat-value">{{ member.skillMatch }}%</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">项目进度</span>
- <div class="progress-bar">
- <div class="progress-fill" [style.width.%]="member.progress"></div>
- </div>
- <span class="stat-value">{{ member.progress }}%</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">贡献度</span>
- <div class="progress-bar">
- <div class="progress-fill" [style.width.%]="member.contribution"></div>
- </div>
- <span class="stat-value">{{ member.contribution }}%</span>
- </div>
- </div>
- </div>
- </div>
- }
- </div>
-
- @if (projectMembers.length === 0) {
- <div class="empty-state">
- <p>暂无项目成员信息</p>
- </div>
- }
- </div>
- </div>
- </div>
- }
-
- <!-- 项目文件标签页 -->
- @if (isActiveTab('files')) {
- <div class="files-tab-content">
- <div class="main-content-layout">
- <!-- 项目文件内容 -->
- <div class="files-content">
- <div class="files-header">
- <h2>项目文件</h2>
- <p class="files-count">共 {{ projectFiles.length }} 个文件</p>
- </div>
-
- <div class="files-list">
- @for (file of projectFiles; track file.id) {
- <div class="file-item">
- <div class="file-icon">
- <span class="file-type">{{ file.type.toUpperCase() }}</span>
- </div>
- <div class="file-info">
- <h3 class="file-name">{{ file.name }}</h3>
- <div class="file-meta">
- <span class="file-size">{{ file.size }}</span>
- <span class="file-date">{{ file.date }}</span>
- </div>
- </div>
- <div class="file-actions">
- <button class="btn-download" (click)="downloadFile(file)">下载</button>
- <button class="btn-preview" (click)="previewFile(file)">预览</button>
- </div>
- </div>
- }
- </div>
-
- @if (projectFiles.length === 0) {
- <div class="empty-state">
- <p>暂无项目文件</p>
- </div>
- }
- </div>
- </div>
- </div>
- }
- </div>
|