|
@@ -1,4 +1,4 @@
|
|
|
-<div class="project-detail-container">
|
|
|
+<div class="project-detail-container designer-page">
|
|
|
<!-- 项目标题栏 -->
|
|
|
<div class="project-header card">
|
|
|
<div class="header-content">
|
|
@@ -40,380 +40,578 @@
|
|
|
{{ reminderMessage }}
|
|
|
</div>
|
|
|
|
|
|
- <!-- 主内容区 - 网格布局 -->
|
|
|
- <div class="main-content-grid">
|
|
|
- <!-- 项目基本信息卡片 -->
|
|
|
- <div class="project-info-card card">
|
|
|
- <h2>项目基本信息</h2>
|
|
|
- <div class="info-grid">
|
|
|
- <div class="info-item">
|
|
|
- <label>项目名称</label>
|
|
|
- <span>{{ project?.name || '加载中...' }}</span>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <label>客户姓名</label>
|
|
|
- <span>{{ project?.customerName || '加载中...' }}</span>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <label>当前阶段</label>
|
|
|
- <span class="stage-tag">{{ project?.currentStage || '加载中...' }}</span>
|
|
|
- </div>
|
|
|
- <div class="info-item" *ngIf="project">
|
|
|
- <label>预计交付日期</label>
|
|
|
- <span>{{ project.deadline | date:'yyyy-MM-dd' }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <!-- 顶部导航标签页 -->
|
|
|
+ <!-- <div class="project-tabs">
|
|
|
+ <div class="tab-header">
|
|
|
+ <button (click)="switchTab('progress')" [class.active]="isActiveTab('progress')" class="tab-btn">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
|
|
|
+ </svg>
|
|
|
+ <span>项目进度</span>
|
|
|
+ </button>
|
|
|
+ <button (click)="switchTab('members')" [class.active]="isActiveTab('members')" class="tab-btn">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
|
|
+ <circle cx="9" cy="7" r="4"></circle>
|
|
|
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
|
|
|
+ <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
|
|
+ </svg>
|
|
|
+ <span>项目成员</span>
|
|
|
+ </button>
|
|
|
+ <button (click)="switchTab('files')" [class.active]="isActiveTab('files')" class="tab-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>
|
|
|
+ <polyline points="10 9 9 9 8 9"></polyline>
|
|
|
+ </svg>
|
|
|
+ <span>项目文件</span>
|
|
|
+ </button>
|
|
|
+ </div> -->
|
|
|
|
|
|
- <!-- 客户画像卡片 -->
|
|
|
- <div class="customer-profile-card card">
|
|
|
- <h2>客户画像</h2>
|
|
|
-
|
|
|
- <!-- 技能匹配度警告 -->
|
|
|
- <div *ngIf="getSkillMismatchWarning()" class="warning-banner">
|
|
|
- <div class="warning-content">
|
|
|
- <span class="warning-icon">⚠️</span>
|
|
|
- <span class="warning-text">{{ getSkillMismatchWarning() }}</span>
|
|
|
- </div>
|
|
|
- <button (click)="notifyTeamLeader('skill-mismatch')" class="contact-leader-btn">联系组长</button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div *ngIf="project" class="tags-container">
|
|
|
- <div class="tag-section">
|
|
|
- <h3>客户偏好</h3>
|
|
|
- <div class="tags-grid">
|
|
|
- <ng-container *ngIf="project.customerTags && project.customerTags.length > 0">
|
|
|
- <div class="tag-item">
|
|
|
- <span class="tag-label">需求类型</span>
|
|
|
- <span *ngIf="project.customerTags[0].needType" class="tag">
|
|
|
- {{ project.customerTags[0].needType }}
|
|
|
- </span>
|
|
|
+ <!-- 标签页内容 -->
|
|
|
+ <div class="tab-content">
|
|
|
+ <!-- 项目进度标签页 -->
|
|
|
+ <div *ngIf="isActiveTab('progress')" class="progress-tab-content">
|
|
|
+ <!-- 主要内容布局 - 左侧三分之一,右侧三分之二 -->
|
|
|
+ <div class="main-content-layout">
|
|
|
+ <!-- 左侧三分之一 - 项目基本信息和客户画像 -->
|
|
|
+ <div class="left-column">
|
|
|
+ <!-- 项目基本信息 -->
|
|
|
+ <div class="project-info-card card">
|
|
|
+ <h2>项目基本信息</h2>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <label>项目名称</label>
|
|
|
+ <span>{{ project?.name || '加载中...' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>客户姓名</label>
|
|
|
+ <span>{{ project?.customerName || '加载中...' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>当前阶段</label>
|
|
|
+ <span class="stage-tag">{{ project?.currentStage || '加载中...' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item" *ngIf="project">
|
|
|
+ <label>预计交付日期</label>
|
|
|
+ <span>{{ project.deadline | date:'yyyy-MM-dd' }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="tag-item">
|
|
|
- <span class="tag-label">设计风格</span>
|
|
|
- <span *ngIf="project.customerTags[0].preference" class="tag">
|
|
|
- {{ project.customerTags[0].preference }}
|
|
|
- </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 客户画像 -->
|
|
|
+ <div class="customer-profile-card card">
|
|
|
+ <h2>客户画像</h2>
|
|
|
+
|
|
|
+ <!-- 技能匹配度警告 -->
|
|
|
+ <div *ngIf="getSkillMismatchWarning()" class="warning-banner">
|
|
|
+ <div class="warning-content">
|
|
|
+ <span class="warning-icon">⚠️</span>
|
|
|
+ <span class="warning-text">{{ getSkillMismatchWarning() }}</span>
|
|
|
+ </div>
|
|
|
+ <button (click)="notifyTeamLeader('skill-mismatch')" class="contact-leader-btn">联系组长</button>
|
|
|
</div>
|
|
|
- <div class="tag-item">
|
|
|
- <span class="tag-label">色彩氛围</span>
|
|
|
- <span *ngIf="project.customerTags[0].colorAtmosphere" class="tag">
|
|
|
- {{ project.customerTags[0].colorAtmosphere }}
|
|
|
- </span>
|
|
|
+
|
|
|
+ <div *ngIf="project" class="tags-container">
|
|
|
+ <div class="tag-section">
|
|
|
+ <h3>客户偏好</h3>
|
|
|
+ <div class="tags-grid">
|
|
|
+ <ng-container *ngIf="project.customerTags && project.customerTags.length > 0">
|
|
|
+ <div class="tag-item">
|
|
|
+ <span class="tag-label">需求类型</span>
|
|
|
+ <span *ngIf="project.customerTags[0].needType" class="tag">
|
|
|
+ {{ project.customerTags[0].needType }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="tag-item">
|
|
|
+ <span class="tag-label">设计风格</span>
|
|
|
+ <span *ngIf="project.customerTags[0].preference" class="tag">
|
|
|
+ {{ project.customerTags[0].preference }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="tag-item">
|
|
|
+ <span class="tag-label">色彩氛围</span>
|
|
|
+ <span *ngIf="project.customerTags[0].colorAtmosphere" class="tag">
|
|
|
+ {{ project.customerTags[0].colorAtmosphere }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </ng-container>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="tag-section">
|
|
|
+ <h3>项目要求</h3>
|
|
|
+ <div class="tags-flex">
|
|
|
+ <div class="tag-group">
|
|
|
+ <span class="group-label">高优先级需求</span>
|
|
|
+ <div class="tags">
|
|
|
+ <span *ngFor="let priority of project.highPriorityNeeds" class="priority-tag">
|
|
|
+ {{ priority }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="tag-group">
|
|
|
+ <span class="group-label">擅长技能</span>
|
|
|
+ <div class="tags">
|
|
|
+ <span *ngFor="let skill of project.skillsRequired" class="skill-tag">
|
|
|
+ {{ skill }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </ng-container>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="tag-section">
|
|
|
- <h3>项目要求</h3>
|
|
|
- <div class="tags-flex">
|
|
|
- <div class="tag-group">
|
|
|
- <span class="group-label">高优先级需求</span>
|
|
|
- <div class="tags">
|
|
|
- <span *ngFor="let priority of project.highPriorityNeeds" class="priority-tag">
|
|
|
- {{ priority }}
|
|
|
- </span>
|
|
|
+
|
|
|
+ <!-- 右侧三分之二 - 制作流程进度 -->
|
|
|
+ <div class="right-column">
|
|
|
+ <div class="process-card card">
|
|
|
+ <h2>制作流程进度</h2>
|
|
|
+ <div class="stage-progress-container">
|
|
|
+ <div class="stage-progress">
|
|
|
+ <div class="stage" [class.completed]="project?.currentStage !== '建模'" [class.active]="project?.currentStage === '建模'">
|
|
|
+ <div class="stage-icon">
|
|
|
+ <span>{{ project?.currentStage !== '建模' ? '✓' : '1' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="stage-name">建模</div>
|
|
|
+ </div>
|
|
|
+ <div class="progress-line"></div>
|
|
|
+ <div class="stage" [class.completed]="project?.currentStage !== '软装' && project?.currentStage !== '建模'" [class.active]="project?.currentStage === '软装'">
|
|
|
+ <div class="stage-icon">
|
|
|
+ <span>{{ project?.currentStage !== '软装' && project?.currentStage !== '建模' ? '✓' : '2' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="stage-name">软装</div>
|
|
|
+ </div>
|
|
|
+ <div class="progress-line"></div>
|
|
|
+ <div class="stage" [class.completed]="project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装'" [class.active]="project?.currentStage === '渲染'">
|
|
|
+ <div class="stage-icon">
|
|
|
+ <span>{{ project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装' ? '✓' : '3' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="stage-name">渲染</div>
|
|
|
+ </div>
|
|
|
+ <div class="progress-line"></div>
|
|
|
+ <div class="stage" [class.completed]="project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染'" [class.active]="project?.currentStage === '后期'">
|
|
|
+ <div class="stage-icon">
|
|
|
+ <span>{{ project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染' ? '✓' : '4' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="stage-name">后期</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="tag-group">
|
|
|
- <span class="group-label">擅长技能</span>
|
|
|
- <div class="tags">
|
|
|
- <span *ngFor="let skill of project.skillsRequired" class="skill-tag">
|
|
|
- {{ skill }}
|
|
|
- </span>
|
|
|
+
|
|
|
+ <!-- 当前阶段操作 -->
|
|
|
+ <div *ngIf="project" class="current-stage-actions">
|
|
|
+ <div class="current-stage-info">
|
|
|
+ <h3>当前阶段: <span class="stage-highlight">{{ project.currentStage }}</span></h3>
|
|
|
+ </div>
|
|
|
+ <div class="stage-actions">
|
|
|
+ <button *ngIf="project.currentStage === '建模'" (click)="updateProjectStage('软装')" [disabled]="!areAllModelChecksPassed()" class="primary-btn">
|
|
|
+ {{ areAllModelChecksPassed() ? '完成建模' : '完成所有模型检查' }}
|
|
|
+ </button>
|
|
|
+ <button *ngIf="project.currentStage === '软装'" (click)="updateProjectStage('渲染')" class="primary-btn">完成软装</button>
|
|
|
+ <button *ngIf="project.currentStage === '渲染'" (click)="updateProjectStage('后期')" class="primary-btn">完成渲染</button>
|
|
|
+ <button *ngIf="project.currentStage === '后期'" (click)="updateProjectStage('完成')" class="primary-btn">完成后期</button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 制作流程进度卡片 -->
|
|
|
- <div class="process-card card">
|
|
|
- <h2>制作流程进度</h2>
|
|
|
- <div class="stage-progress-container">
|
|
|
- <div class="stage-progress">
|
|
|
- <div class="stage" [class.completed]="project?.currentStage !== '建模'" [class.active]="project?.currentStage === '建模'">
|
|
|
- <div class="stage-icon">
|
|
|
- <span>{{ project?.currentStage !== '建模' ? '✓' : '1' }}</span>
|
|
|
+ <!-- 阶段专属任务卡片 - 仅在对应节点显示 -->
|
|
|
+ <div class="stage-specific-cards">
|
|
|
+ <!-- 误查检查单卡片 - 根据shouldShowCard方法在特定阶段显示 -->
|
|
|
+ <div *ngIf="shouldShowCard('modelCheck')" class="model-check-card card">
|
|
|
+ <h2>模型误差检查清单</h2>
|
|
|
+ <div class="checklist">
|
|
|
+ <div *ngFor="let item of modelCheckItems" class="checklist-item">
|
|
|
+ <input type="checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, !item.isPassed)" class="custom-checkbox">
|
|
|
+ <span class="checklist-text">{{ item.name }}</span>
|
|
|
+ <span class="check-status" [class.passed]="item.isPassed" [class.failed]="!item.isPassed">
|
|
|
+ {{ item.isPassed ? '通过' : '未通过' }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="stage-name">建模</div>
|
|
|
</div>
|
|
|
- <div class="progress-line"></div>
|
|
|
- <div class="stage" [class.completed]="project?.currentStage !== '软装' && project?.currentStage !== '建模'" [class.active]="project?.currentStage === '软装'">
|
|
|
- <div class="stage-icon">
|
|
|
- <span>{{ project?.currentStage !== '软装' && project?.currentStage !== '建模' ? '✓' : '2' }}</span>
|
|
|
+
|
|
|
+ <!-- 渲染阶段专属卡片 -->
|
|
|
+ <div *ngIf="project?.currentStage === '渲染'" class="render-progress-card card">
|
|
|
+ <h2>渲染进度</h2>
|
|
|
+ <div *ngIf="isLoadingRenderProgress" class="loading-state">
|
|
|
+ <div class="loading-spinner"></div>
|
|
|
+ <span>加载中...</span>
|
|
|
</div>
|
|
|
- <div class="stage-name">软装</div>
|
|
|
- </div>
|
|
|
- <div class="progress-line"></div>
|
|
|
- <div class="stage" [class.completed]="project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装'" [class.active]="project?.currentStage === '渲染'">
|
|
|
- <div class="stage-icon">
|
|
|
- <span>{{ project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装' ? '✓' : '3' }}</span>
|
|
|
+ <div *ngIf="errorLoadingRenderProgress" class="error-state">
|
|
|
+ <span>加载失败</span>
|
|
|
+ <button (click)="retryLoadRenderProgress()" class="secondary-btn">点击重试</button>
|
|
|
</div>
|
|
|
- <div class="stage-name">渲染</div>
|
|
|
- </div>
|
|
|
- <div class="progress-line"></div>
|
|
|
- <div class="stage" [class.completed]="project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染'" [class.active]="project?.currentStage === '后期'">
|
|
|
- <div class="stage-icon">
|
|
|
- <span>{{ project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染' ? '✓' : '4' }}</span>
|
|
|
+ <div *ngIf="renderProgress && !isLoadingRenderProgress && !errorLoadingRenderProgress" class="progress-content">
|
|
|
+ <!-- 渲染超时预警 -->
|
|
|
+ <div *ngIf="renderProgress.estimatedTimeRemaining <= 3" class="timeout-warning">
|
|
|
+ <div class="warning-icon">⚠️</div>
|
|
|
+ <div class="warning-text">
|
|
|
+ <span class="warning-title">渲染即将超时</span>
|
|
|
+ <span class="warning-time">预计剩余时间: {{ renderProgress.estimatedTimeRemaining }} 小时</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="stage-name">后期</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 当前阶段操作 -->
|
|
|
- <div *ngIf="project" class="current-stage-actions">
|
|
|
- <div class="current-stage-info">
|
|
|
- <h3>当前阶段: <span class="stage-highlight">{{ project.currentStage }}</span></h3>
|
|
|
- </div>
|
|
|
- <div class="stage-actions">
|
|
|
- <button *ngIf="project.currentStage === '建模'" (click)="updateProjectStage('软装')" [disabled]="!areAllModelChecksPassed()" class="primary-btn">
|
|
|
- {{ areAllModelChecksPassed() ? '完成建模' : '完成所有模型检查' }}
|
|
|
- </button>
|
|
|
- <button *ngIf="project.currentStage === '软装'" (click)="updateProjectStage('渲染')" class="primary-btn">完成软装</button>
|
|
|
- <button *ngIf="project.currentStage === '渲染'" (click)="updateProjectStage('后期')" class="primary-btn">完成渲染</button>
|
|
|
- <button *ngIf="project.currentStage === '后期'" (click)="updateProjectStage('完成')" class="primary-btn">完成后期</button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 渲染进度卡片 -->
|
|
|
- <div class="render-progress-card card">
|
|
|
- <h2>渲染进度</h2>
|
|
|
- <div *ngIf="isLoadingRenderProgress" class="loading-state">
|
|
|
- <div class="loading-spinner"></div>
|
|
|
- <span>加载中...</span>
|
|
|
- </div>
|
|
|
- <div *ngIf="errorLoadingRenderProgress" class="error-state">
|
|
|
- <span>加载失败</span>
|
|
|
- <button (click)="retryLoadRenderProgress()" class="secondary-btn">点击重试</button>
|
|
|
- </div>
|
|
|
- <div *ngIf="renderProgress && !isLoadingRenderProgress && !errorLoadingRenderProgress" class="progress-content">
|
|
|
- <!-- 渲染超时预警 -->
|
|
|
- <div *ngIf="renderProgress.estimatedTimeRemaining <= 3" class="timeout-warning">
|
|
|
- <div class="warning-icon">⚠️</div>
|
|
|
- <div class="warning-text">
|
|
|
- <span class="warning-title">渲染即将超时</span>
|
|
|
- <span class="warning-time">预计剩余时间: {{ renderProgress.estimatedTimeRemaining }} 小时</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <!-- 渲染异常反馈模块 -->
|
|
|
+ <div class="render-exception-section">
|
|
|
+ <h3>渲染异常反馈</h3>
|
|
|
+ <div class="exception-feedback-form">
|
|
|
+ <div class="form-group">
|
|
|
+ <label>异常类型:</label>
|
|
|
+ <select [(ngModel)]="exceptionType" class="exception-select">
|
|
|
+ <option value="failed">渲染失败</option>
|
|
|
+ <option value="stuck">渲染卡顿</option>
|
|
|
+ <option value="quality">渲染质量问题</option>
|
|
|
+ <option value="other">其他问题</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div class="form-group">
|
|
|
+ <label>详细描述:</label>
|
|
|
+ <textarea
|
|
|
+ [(ngModel)]="exceptionDescription"
|
|
|
+ placeholder="请描述渲染过程中遇到的具体问题..."
|
|
|
+ class="exception-textarea"
|
|
|
+ ></textarea>
|
|
|
+ </div>
|
|
|
+ <div class="form-group">
|
|
|
+ <label>上传截图 (可选):</label>
|
|
|
+ <input type="file" (change)="uploadExceptionScreenshot($event)" class="screenshot-upload" id="screenshot-upload">
|
|
|
+ <label for="screenshot-upload" class="upload-btn">选择文件</label>
|
|
|
+ <div *ngIf="exceptionScreenshotUrl" class="screenshot-preview">
|
|
|
+ <img [src]="exceptionScreenshotUrl" alt="异常截图">
|
|
|
+ <button (click)="clearExceptionScreenshot()" class="clear-screenshot-btn">×</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <button
|
|
|
+ (click)="submitExceptionFeedback()"
|
|
|
+ [disabled]="!exceptionDescription.trim()"
|
|
|
+ class="submit-feedback-btn"
|
|
|
+ >
|
|
|
+ 提交反馈并联系技术支持
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 渲染异常反馈模块 -->
|
|
|
- <div class="render-exception-section">
|
|
|
- <h3>渲染异常反馈</h3>
|
|
|
- <div class="exception-feedback-form">
|
|
|
- <div class="form-group">
|
|
|
- <label>异常类型:</label>
|
|
|
- <select [(ngModel)]="exceptionType" class="exception-select">
|
|
|
- <option value="failed">渲染失败</option>
|
|
|
- <option value="stuck">渲染卡顿</option>
|
|
|
- <option value="quality">渲染质量问题</option>
|
|
|
- <option value="other">其他问题</option>
|
|
|
- </select>
|
|
|
- </div>
|
|
|
- <div class="form-group">
|
|
|
- <label>详细描述:</label>
|
|
|
- <textarea
|
|
|
- [(ngModel)]="exceptionDescription"
|
|
|
- placeholder="请描述渲染过程中遇到的具体问题..."
|
|
|
- class="exception-textarea"
|
|
|
- ></textarea>
|
|
|
- </div>
|
|
|
- <div class="form-group">
|
|
|
- <label>上传截图 (可选):</label>
|
|
|
- <input type="file" (change)="uploadExceptionScreenshot($event)" class="screenshot-upload" id="screenshot-upload">
|
|
|
- <label for="screenshot-upload" class="upload-btn">选择文件</label>
|
|
|
- <div *ngIf="exceptionScreenshotUrl" class="screenshot-preview">
|
|
|
- <img [src]="exceptionScreenshotUrl" alt="异常截图">
|
|
|
- <button (click)="clearExceptionScreenshot()" class="clear-screenshot-btn">×</button>
|
|
|
+ <!-- 历史反馈记录 -->
|
|
|
+ <div class="exception-history" *ngIf="exceptionHistories.length > 0">
|
|
|
+ <h4>历史反馈记录</h4>
|
|
|
+ <div class="history-list">
|
|
|
+ <div *ngFor="let history of exceptionHistories" class="history-item">
|
|
|
+ <div class="history-header">
|
|
|
+ <span class="history-type">{{ getExceptionTypeText(history.type) }}</span>
|
|
|
+ <span class="history-time">{{ formatDate(history.submitTime) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="history-description">{{ history.description }}</div>
|
|
|
+ <div class="history-status" [class.status-pending]="history.status === '待处理'" [class.status-processing]="history.status === '处理中'" [class.status-resolved]="history.status === '已解决'">
|
|
|
+ {{ history.status }} - {{ history.response || '暂无回复' }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="progress-bar-container">
|
|
|
+ <div class="progress-bar">
|
|
|
+ <div class="progress-fill" [style.width.percent]="renderProgress.completionRate"></div>
|
|
|
+ </div>
|
|
|
+ <div class="progress-percentage">{{ renderProgress.completionRate }}%</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="progress-details">
|
|
|
+ <div class="progress-info">
|
|
|
+ <span class="info-label">预计剩余时间</span>
|
|
|
+ <span class="info-value">{{ renderProgress.estimatedTimeRemaining }} 小时</span>
|
|
|
+ </div>
|
|
|
+ <div class="progress-info">
|
|
|
+ <span class="info-label">当前状态</span>
|
|
|
+ <span class="info-value">{{ renderProgress.status }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <button
|
|
|
- (click)="submitExceptionFeedback()"
|
|
|
- [disabled]="!exceptionDescription.trim()"
|
|
|
- class="submit-feedback-btn"
|
|
|
- >
|
|
|
- 提交反馈并联系技术支持
|
|
|
- </button>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 历史反馈记录 -->
|
|
|
- <div class="exception-history" *ngIf="exceptionHistories.length > 0">
|
|
|
- <h4>历史反馈记录</h4>
|
|
|
- <div class="history-list">
|
|
|
- <div *ngFor="let history of exceptionHistories" class="history-item">
|
|
|
- <div class="history-header">
|
|
|
- <span class="history-type">{{ getExceptionTypeText(history.type) }}</span>
|
|
|
- <span class="history-time">{{ formatDate(history.submitTime) }}</span>
|
|
|
+ <!-- 客户反馈和设计师变更记录 -->
|
|
|
+ <div class="additional-info-section">
|
|
|
+ <div class="feedback-card card">
|
|
|
+ <h2>客户反馈</h2>
|
|
|
+ <div *ngIf="feedbacks.length === 0" class="empty-state">
|
|
|
+ <div class="empty-icon">📭</div>
|
|
|
+ <span>暂无客户反馈</span>
|
|
|
+ </div>
|
|
|
+ <div *ngFor="let feedback of feedbacks" class="feedback-item">
|
|
|
+ <div class="feedback-header">
|
|
|
+ <div class="feedback-meta">
|
|
|
+ <span class="feedback-type">{{ feedback.isSatisfied ? '满意反馈' : '不满意反馈' }}</span>
|
|
|
+ <span *ngIf="getFeedbackTag(feedback)" class="feedback-tag">{{ getFeedbackTag(feedback) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="feedback-date">{{ feedback.createdAt | date:'yyyy-MM-dd HH:mm' }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="feedback-content">
|
|
|
+ <div class="feedback-status"><span class="status-label">状态:</span> <span class="status-value">{{ feedback.status }}</span></div>
|
|
|
+ <!-- 反馈倒计时 -->
|
|
|
+ <div *ngIf="feedback.status === '待处理' && feedbackTimeoutCountdown > 0" class="feedback-countdown">
|
|
|
+ <span class="countdown-icon">⏱️</span>
|
|
|
+ <span>响应倒计时: {{ formatCountdown(feedbackTimeoutCountdown) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="feedback-details">
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="detail-label">修改部位:</span>
|
|
|
+ <span class="detail-value">{{ feedback.problemLocation || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="detail-label">期望效果:</span>
|
|
|
+ <span class="detail-value">{{ feedback.expectedEffect || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="detail-label">参考案例:</span>
|
|
|
+ <span class="detail-value">{{ feedback.referenceCase || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="history-description">{{ history.description }}</div>
|
|
|
- <div class="history-status" [class.status-pending]="history.status === '待处理'" [class.status-processing]="history.status === '处理中'" [class.status-resolved]="history.status === '已解决'">
|
|
|
- {{ history.status }} - {{ history.response || '暂无回复' }}
|
|
|
+ <div class="feedback-actions">
|
|
|
+ <button (click)="updateFeedbackStatus(feedback.id, '处理中')" [disabled]="feedback.status === '处理中' || feedback.status === '已解决'" class="secondary-btn">
|
|
|
+ 标记为处理中
|
|
|
+ </button>
|
|
|
+ <button (click)="updateFeedbackStatus(feedback.id, '已解决')" [disabled]="feedback.status === '已解决'" class="primary-btn">
|
|
|
+ 标记为已解决
|
|
|
+ </button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="progress-bar-container">
|
|
|
- <div class="progress-bar">
|
|
|
- <div class="progress-fill" [style.width.percent]="renderProgress.completionRate"></div>
|
|
|
- </div>
|
|
|
- <div class="progress-percentage">{{ renderProgress.completionRate }}%</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="progress-details">
|
|
|
- <div class="progress-info">
|
|
|
- <span class="info-label">预计剩余时间</span>
|
|
|
- <span class="info-value">{{ renderProgress.estimatedTimeRemaining }} 小时</span>
|
|
|
- </div>
|
|
|
- <div class="progress-info">
|
|
|
- <span class="info-label">当前状态</span>
|
|
|
- <span class="info-value">{{ renderProgress.status }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 模型误差检查清单卡片 -->
|
|
|
- <div class="model-check-card card">
|
|
|
- <h2>模型误差检查清单</h2>
|
|
|
- <div class="checklist">
|
|
|
- <div *ngFor="let item of modelCheckItems" class="checklist-item">
|
|
|
- <input type="checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, !item.isPassed)" class="custom-checkbox">
|
|
|
- <span class="checklist-text">{{ item.name }}</span>
|
|
|
- <span class="check-status" [class.passed]="item.isPassed" [class.failed]="!item.isPassed">
|
|
|
- {{ item.isPassed ? '通过' : '未通过' }}
|
|
|
- </span>
|
|
|
+ <div class="designer-change-card card">
|
|
|
+ <h2>设计师变更记录</h2>
|
|
|
+ <div class="change-actions">
|
|
|
+ <button (click)="initiateDesignerChange('技能不匹配')" class="secondary-btn">发起变更 - 技能不匹配</button>
|
|
|
+ <button (click)="initiateDesignerChange('休假')" class="secondary-btn">发起变更 - 休假</button>
|
|
|
+ </div>
|
|
|
+ <div *ngIf="designerChanges.length === 0" class="empty-state">
|
|
|
+ <div class="empty-icon">👤</div>
|
|
|
+ <span>暂无设计师变更记录</span>
|
|
|
+ </div>
|
|
|
+ <div *ngFor="let change of designerChanges" class="change-item">
|
|
|
+ <div class="change-header">
|
|
|
+ <div class="change-time">{{ change.changeTime | date:'yyyy-MM-dd' }}</div>
|
|
|
+ <button *ngIf="!change.acceptanceTime" (click)="acceptDesignerChange(change.id)" class="accept-change-btn primary-btn">
|
|
|
+ 确认承接
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <div class="change-details">
|
|
|
+ <div class="designer-change-info">
|
|
|
+ <div class="designer-change">
|
|
|
+ <span class="designer-label">原设计师:</span>
|
|
|
+ <span class="designer-name">{{ change.oldDesignerName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="designer-change">
|
|
|
+ <span class="designer-label">新设计师:</span>
|
|
|
+ <span class="designer-name">{{ change.newDesignerName }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="workload-info">
|
|
|
+ <span>已完成工作量: <strong>{{ change.completedWorkload }}%</strong></span>
|
|
|
+ </div>
|
|
|
+ <div class="achievements">
|
|
|
+ <h4>历史阶段成果:</h4>
|
|
|
+ <ul>
|
|
|
+ <li *ngFor="let achievement of change.historicalAchievements">{{ achievement }}</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ <div *ngIf="change.acceptanceTime" class="change-status">
|
|
|
+ 承接确认时间: {{ change.acceptanceTime | date:'yyyy-MM-dd' }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 客户反馈卡片 -->
|
|
|
- <div class="feedback-card card">
|
|
|
- <h2>客户反馈</h2>
|
|
|
- <div *ngIf="feedbacks.length === 0" class="empty-state">
|
|
|
- <div class="empty-icon">📭</div>
|
|
|
- <span>暂无客户反馈</span>
|
|
|
- </div>
|
|
|
- <div *ngFor="let feedback of feedbacks" class="feedback-item">
|
|
|
- <div class="feedback-header">
|
|
|
- <div class="feedback-meta">
|
|
|
- <span class="feedback-type">{{ feedback.isSatisfied ? '满意反馈' : '不满意反馈' }}</span>
|
|
|
- <span *ngIf="getFeedbackTag(feedback)" class="feedback-tag">{{ getFeedbackTag(feedback) }}</span>
|
|
|
+ <!-- 项目成员标签页 -->
|
|
|
+ <div *ngIf="isActiveTab('members')" class="members-tab-content">
|
|
|
+ <div class="project-members-card card">
|
|
|
+ <h2>项目团队成员</h2>
|
|
|
+ <div class="members-grid">
|
|
|
+ <div *ngFor="let member of projectMembers" class="member-card">
|
|
|
+ <div class="member-avatar">
|
|
|
+ <div class="avatar-placeholder">{{ member.avatar }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="member-info">
|
|
|
+ <div class="member-name">{{ member.name }}</div>
|
|
|
+ <div class="member-role">{{ member.role }}</div>
|
|
|
+ <div class="member-skills">
|
|
|
+ <span *ngIf="member.skillMatch >= 90" class="skill-badge">技能匹配良好</span>
|
|
|
+ <span *ngIf="member.progress >= 80" class="skill-badge">进度领先</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="member-actions">
|
|
|
+ <button class="message-btn">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
+ <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="feedback-date">{{ feedback.createdAt | date:'yyyy-MM-dd HH:mm' }}</div>
|
|
|
</div>
|
|
|
- <div class="feedback-content">
|
|
|
- <div class="feedback-status"><span class="status-label">状态:</span> <span class="status-value">{{ feedback.status }}</span></div>
|
|
|
- <!-- 反馈倒计时 -->
|
|
|
- <div *ngIf="feedback.status === '待处理' && feedbackTimeoutCountdown > 0" class="feedback-countdown">
|
|
|
- <span class="countdown-icon">⏱️</span>
|
|
|
- <span>响应倒计时: {{ formatCountdown(feedbackTimeoutCountdown) }}</span>
|
|
|
- </div>
|
|
|
- <div class="feedback-details">
|
|
|
- <div class="detail-item">
|
|
|
- <span class="detail-label">修改部位:</span>
|
|
|
- <span class="detail-value">{{ feedback.problemLocation || '-' }}</span>
|
|
|
- </div>
|
|
|
- <div class="detail-item">
|
|
|
- <span class="detail-label">期望效果:</span>
|
|
|
- <span class="detail-value">{{ feedback.expectedEffect || '-' }}</span>
|
|
|
- </div>
|
|
|
- <div class="detail-item">
|
|
|
- <span class="detail-label">参考案例:</span>
|
|
|
- <span class="detail-value">{{ feedback.referenceCase || '-' }}</span>
|
|
|
+
|
|
|
+ <div class="members-timeline-card card">
|
|
|
+ <h2>团队协作时间轴</h2>
|
|
|
+ <div class="timeline-entries">
|
|
|
+ <div *ngFor="let event of timelineEvents" class="timeline-entry">
|
|
|
+ <div class="timeline-dot"></div>
|
|
|
+ <div class="timeline-content">
|
|
|
+ <div class="timeline-header">
|
|
|
+ <span class="timeline-author">{{ getEventAuthor(event.action) }}</span>
|
|
|
+ <span class="timeline-time">{{ event.time }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="timeline-text">{{ event.description }}</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="feedback-actions">
|
|
|
- <button (click)="updateFeedbackStatus(feedback.id, '处理中')" [disabled]="feedback.status === '处理中' || feedback.status === '已解决'" class="secondary-btn">
|
|
|
- 标记为处理中
|
|
|
- </button>
|
|
|
- <button (click)="updateFeedbackStatus(feedback.id, '已解决')" [disabled]="feedback.status === '已解决'" class="primary-btn">
|
|
|
- 标记为已解决
|
|
|
- </button>
|
|
|
- </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 设计师变更记录卡片 -->
|
|
|
- <div class="designer-change-card card">
|
|
|
- <h2>设计师变更记录</h2>
|
|
|
- <div class="change-actions">
|
|
|
- <button (click)="initiateDesignerChange('技能不匹配')" class="secondary-btn">发起变更 - 技能不匹配</button>
|
|
|
- <button (click)="initiateDesignerChange('休假')" class="secondary-btn">发起变更 - 休假</button>
|
|
|
- </div>
|
|
|
- <div *ngIf="designerChanges.length === 0" class="empty-state">
|
|
|
- <div class="empty-icon">👤</div>
|
|
|
- <span>暂无设计师变更记录</span>
|
|
|
- </div>
|
|
|
- <div *ngFor="let change of designerChanges" class="change-item">
|
|
|
- <div class="change-header">
|
|
|
- <div class="change-time">{{ change.changeTime | date:'yyyy-MM-dd' }}</div>
|
|
|
- <button *ngIf="!change.acceptanceTime" (click)="acceptDesignerChange(change.id)" class="accept-change-btn primary-btn">
|
|
|
- 确认承接
|
|
|
+ <!-- 项目文件标签页 -->
|
|
|
+ <div *ngIf="isActiveTab('files')" class="files-tab-content">
|
|
|
+ <div class="file-actions">
|
|
|
+ <button class="upload-btn primary-btn">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
|
|
+ <polyline points="17 8 12 3 7 8"></polyline>
|
|
|
+ <line x1="12" y1="3" x2="12" y2="15"></line>
|
|
|
+ </svg>
|
|
|
+ <span>上传文件</span>
|
|
|
</button>
|
|
|
+ <div class="file-filter">
|
|
|
+ <select class="file-filter-select">
|
|
|
+ <option value="all">全部文件</option>
|
|
|
+ <option value="images">图片</option>
|
|
|
+ <option value="documents">文档</option>
|
|
|
+ <option value="models">模型文件</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="change-details">
|
|
|
- <div class="designer-change-info">
|
|
|
- <div class="designer-change">
|
|
|
- <span class="designer-label">原设计师:</span>
|
|
|
- <span class="designer-name">{{ change.oldDesignerName }}</span>
|
|
|
+
|
|
|
+ <div class="files-grid">
|
|
|
+ <div *ngFor="let file of projectFiles" class="file-card">
|
|
|
+ <div class="file-icon">
|
|
|
+ <svg *ngIf="file.type.includes('pdf')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#EA4335" 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>
|
|
|
+ <polyline points="10 9 9 9 8 9"></polyline>
|
|
|
+ </svg>
|
|
|
+ <svg *ngIf="file.type.includes('jpg') || file.type.includes('png')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#34A853" stroke-width="2">
|
|
|
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
|
|
+ <circle cx="8.5" cy="8.5" r="1.5"></circle>
|
|
|
+ <polyline points="21 15 16 10 5 21"></polyline>
|
|
|
+ </svg>
|
|
|
+ <svg *ngIf="file.type.includes('docx') || file.type.includes('doc')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#4285F4" 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>
|
|
|
+ <polyline points="10 9 9 9 8 9"></polyline>
|
|
|
+ </svg>
|
|
|
+ <svg *ngIf="file.type.includes('rar') || file.type.includes('zip')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#FBBC05" 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>
|
|
|
+ <polyline points="10 9 9 9 8 9"></polyline>
|
|
|
+ </svg>
|
|
|
+ <svg *ngIf="file.type.includes('max') || file.type.includes('obj')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#9C27B0" 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>
|
|
|
+ <polyline points="10 9 9 9 8 9"></polyline>
|
|
|
+ </svg>
|
|
|
</div>
|
|
|
- <div class="designer-change">
|
|
|
- <span class="designer-label">新设计师:</span>
|
|
|
- <span class="designer-name">{{ change.newDesignerName }}</span>
|
|
|
+ <div class="file-info">
|
|
|
+ <div class="file-name">{{ file.name }}</div>
|
|
|
+ <div class="file-meta">
|
|
|
+ <span class="file-size">{{ file.size }}</span>
|
|
|
+ <span class="file-date">{{ file.date }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+ <button class="file-action-btn">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
+ <circle cx="12" cy="12" r="1"></circle>
|
|
|
+ <circle cx="19" cy="12" r="1"></circle>
|
|
|
+ <circle cx="5" cy="12" r="1"></circle>
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
</div>
|
|
|
- <div class="workload-info">
|
|
|
- <span>已完成工作量: <strong>{{ change.completedWorkload }}%</strong></span>
|
|
|
+ <div class="file-icon">
|
|
|
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#FBBC05" 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>
|
|
|
+ <polyline points="10 9 9 9 8 9"></polyline>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <div class="file-info">
|
|
|
+ <div class="file-name">色彩方案.xlsx</div>
|
|
|
+ <div class="file-meta">
|
|
|
+ <span class="file-size">0.9MB</span>
|
|
|
+ <span class="file-date">2025-09-04</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <button class="file-action-btn">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
+ <circle cx="12" cy="12" r="1"></circle>
|
|
|
+ <circle cx="19" cy="12" r="1"></circle>
|
|
|
+ <circle cx="5" cy="12" r="1"></circle>
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
</div>
|
|
|
- <div class="achievements">
|
|
|
- <h4>历史阶段成果:</h4>
|
|
|
- <ul>
|
|
|
- <li *ngFor="let achievement of change.historicalAchievements">{{ achievement }}</li>
|
|
|
- </ul>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="file-storage-info">
|
|
|
+ <div class="storage-bar">
|
|
|
+ <div class="storage-used" style="width: 45%"></div>
|
|
|
</div>
|
|
|
- <div *ngIf="change.acceptanceTime" class="change-status">
|
|
|
- 承接确认时间: {{ change.acceptanceTime | date:'yyyy-MM-dd' }}
|
|
|
+ <div class="storage-text">
|
|
|
+ <span>已使用 450MB / 1GB</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 分阶段结算记录卡片 -->
|
|
|
- <div class="settlement-card card">
|
|
|
- <h2>分阶段结算记录</h2>
|
|
|
- <div *ngIf="settlements.length === 0" class="empty-state">
|
|
|
- <div class="empty-icon">💰</div>
|
|
|
- <span>暂无结算记录</span>
|
|
|
- </div>
|
|
|
- <div *ngIf="settlements.length > 0" class="settlement-table">
|
|
|
- <table>
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- <th>阶段</th>
|
|
|
- <th>比例</th>
|
|
|
- <th>金额(元)</th>
|
|
|
- <th>状态</th>
|
|
|
- <th>完成时间</th>
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
- <tr *ngFor="let settlement of settlements">
|
|
|
- <td>{{ settlement.stage }}</td>
|
|
|
- <td>{{ settlement.percentage }}%</td>
|
|
|
- <td>{{ settlement.amount }}</td>
|
|
|
- <td><span class="status-badge" [class.status-pending]="settlement.status === '待结算'" [class.status-settled]="settlement.status === '已结算'">{{ settlement.status }}</span></td>
|
|
|
- <td>{{ settlement.completionTime | date:'yyyy-MM-dd' }}</td>
|
|
|
- </tr>
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
- </div>
|
|
|
+ <!-- 分阶段结算记录卡片 -->
|
|
|
+ <div class="settlement-card card">
|
|
|
+ <h2>分阶段结算记录</h2>
|
|
|
+ <div *ngIf="settlements.length === 0" class="empty-state">
|
|
|
+ <div class="empty-icon">💰</div>
|
|
|
+ <span>暂无结算记录</span>
|
|
|
+ </div>
|
|
|
+ <div *ngIf="settlements.length > 0" class="settlement-table">
|
|
|
+ <table>
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>阶段</th>
|
|
|
+ <th>比例</th>
|
|
|
+ <th>金额(元)</th>
|
|
|
+ <th>状态</th>
|
|
|
+ <th>完成时间</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr *ngFor="let settlement of settlements">
|
|
|
+ <td>{{ settlement.stage }}</td>
|
|
|
+ <td>{{ settlement.percentage }}%</td>
|
|
|
+ <td>{{ settlement.amount }}</td>
|
|
|
+ <td><span class="status-badge" [class.status-pending]="settlement.status === '待结算'" [class.status-settled]="settlement.status === '已结算'">{{ settlement.status }}</span></td>
|
|
|
+ <td>{{ settlement.completionTime | date:'yyyy-MM-dd' }}</td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
</div>
|
|
|
</div>
|
|
|
-</div>
|