project-detail.html 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. <div class="project-detail-container designer-page">
  2. <!-- 项目标题栏 -->
  3. <div class="project-header card">
  4. <div class="header-content">
  5. <h1>项目详情</h1>
  6. <div class="project-meta">
  7. <span class="project-id">项目ID: {{ projectId }}</span>
  8. <span class="project-status" *ngIf="project">{{ project.status }}</span>
  9. </div>
  10. </div>
  11. <div class="header-actions">
  12. <!-- 返回工作台按钮 -->
  13. <button (click)="backToWorkbench()" class="back-btn">返回工作台</button>
  14. <!-- 切换项目下拉菜单 -->
  15. <div class="project-switcher">
  16. <button (click)="showDropdown = !showDropdown" class="switch-btn">切换项目</button>
  17. <div *ngIf="showDropdown" class="switch-dropdown" (click)="$event.stopPropagation()">
  18. <div *ngFor="let p of projects"
  19. (click)="switchProject(p.id); showDropdown = false"
  20. [class.active]="p.id === projectId"
  21. class="project-item">
  22. <span class="project-name">{{ p.name }}</span>
  23. <span class="project-status-badge"
  24. [class.ongoing]="p.status === '进行中'"
  25. [class.completed]="p.status === '已完成'"
  26. [class.pending]="p.status === '待处理'">
  27. {{ p.status }}
  28. </span>
  29. </div>
  30. </div>
  31. </div>
  32. <button (click)="generateReminderMessage()" class="stagnation-btn">设置停滞</button>
  33. </div>
  34. </div>
  35. <!-- 提醒消息弹窗 -->
  36. <div *ngIf="reminderMessage" class="reminder-popup">
  37. {{ reminderMessage }}
  38. </div>
  39. <!-- 顶部导航标签页 -->
  40. <!-- <div class="project-tabs">
  41. <div class="tab-header">
  42. <button (click)="switchTab('progress')" [class.active]="isActiveTab('progress')" class="tab-btn">
  43. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  44. <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
  45. </svg>
  46. <span>项目进度</span>
  47. </button>
  48. <button (click)="switchTab('members')" [class.active]="isActiveTab('members')" class="tab-btn">
  49. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  50. <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
  51. <circle cx="9" cy="7" r="4"></circle>
  52. <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
  53. <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
  54. </svg>
  55. <span>项目成员</span>
  56. </button>
  57. <button (click)="switchTab('files')" [class.active]="isActiveTab('files')" class="tab-btn">
  58. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  59. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
  60. <polyline points="14 2 14 8 20 8"></polyline>
  61. <line x1="16" y1="13" x2="8" y2="13"></line>
  62. <line x1="16" y1="17" x2="8" y2="17"></line>
  63. <polyline points="10 9 9 9 8 9"></polyline>
  64. </svg>
  65. <span>项目文件</span>
  66. </button>
  67. </div> -->
  68. <!-- 标签页内容 -->
  69. <div class="tab-content">
  70. <!-- 项目进度标签页 -->
  71. <div *ngIf="isActiveTab('progress')" class="progress-tab-content">
  72. <!-- 主要内容布局 - 左侧三分之一,右侧三分之二 -->
  73. <div class="main-content-layout">
  74. <!-- 左侧三分之一 - 项目基本信息和客户画像 -->
  75. <div class="left-column">
  76. <!-- 项目基本信息 -->
  77. <div class="project-info-card card">
  78. <h2>项目基本信息</h2>
  79. <div class="info-grid">
  80. <div class="info-item">
  81. <label>项目名称</label>
  82. <span>{{ project?.name || '加载中...' }}</span>
  83. </div>
  84. <div class="info-item">
  85. <label>客户姓名</label>
  86. <span>{{ project?.customerName || '加载中...' }}</span>
  87. </div>
  88. <div class="info-item">
  89. <label>当前阶段</label>
  90. <span class="stage-tag">{{ project?.currentStage || '加载中...' }}</span>
  91. </div>
  92. <div class="info-item" *ngIf="project">
  93. <label>预计交付日期</label>
  94. <span>{{ project.deadline | date:'yyyy-MM-dd' }}</span>
  95. </div>
  96. </div>
  97. </div>
  98. <!-- 客户画像 -->
  99. <div class="customer-profile-card card">
  100. <h2>客户画像</h2>
  101. <!-- 技能匹配度警告 -->
  102. <div *ngIf="getSkillMismatchWarning()" class="warning-banner">
  103. <div class="warning-content">
  104. <span class="warning-icon">⚠️</span>
  105. <span class="warning-text">{{ getSkillMismatchWarning() }}</span>
  106. </div>
  107. <button (click)="notifyTeamLeader('skill-mismatch')" class="contact-leader-btn">联系组长</button>
  108. </div>
  109. <div *ngIf="project" class="tags-container">
  110. <div class="tag-section">
  111. <h3>客户偏好</h3>
  112. <div class="tags-grid">
  113. <ng-container *ngIf="project.customerTags && project.customerTags.length > 0">
  114. <div class="tag-item">
  115. <span class="tag-label">需求类型</span>
  116. <span *ngIf="project.customerTags[0].needType" class="tag">
  117. {{ project.customerTags[0].needType }}
  118. </span>
  119. </div>
  120. <div class="tag-item">
  121. <span class="tag-label">设计风格</span>
  122. <span *ngIf="project.customerTags[0].preference" class="tag">
  123. {{ project.customerTags[0].preference }}
  124. </span>
  125. </div>
  126. <div class="tag-item">
  127. <span class="tag-label">色彩氛围</span>
  128. <span *ngIf="project.customerTags[0].colorAtmosphere" class="tag">
  129. {{ project.customerTags[0].colorAtmosphere }}
  130. </span>
  131. </div>
  132. </ng-container>
  133. </div>
  134. </div>
  135. <div class="tag-section">
  136. <h3>项目要求</h3>
  137. <div class="tags-flex">
  138. <div class="tag-group">
  139. <span class="group-label">高优先级需求</span>
  140. <div class="tags">
  141. <span *ngFor="let priority of project.highPriorityNeeds" class="priority-tag">
  142. {{ priority }}
  143. </span>
  144. </div>
  145. </div>
  146. <div class="tag-group">
  147. <span class="group-label">擅长技能</span>
  148. <div class="tags">
  149. <span *ngFor="let skill of project.skillsRequired" class="skill-tag">
  150. {{ skill }}
  151. </span>
  152. </div>
  153. </div>
  154. </div>
  155. </div>
  156. </div>
  157. </div>
  158. </div>
  159. <!-- 右侧三分之二 - 制作流程进度 -->
  160. <div class="right-column">
  161. <div class="process-card card">
  162. <h2>制作流程进度</h2>
  163. <div class="stage-progress-container">
  164. <div class="stage-progress">
  165. <div class="stage" [class.completed]="project?.currentStage !== '建模'" [class.active]="project?.currentStage === '建模'">
  166. <div class="stage-icon">
  167. <span>{{ project?.currentStage !== '建模' ? '✓' : '1' }}</span>
  168. </div>
  169. <div class="stage-name">建模</div>
  170. </div>
  171. <div class="progress-line"></div>
  172. <div class="stage" [class.completed]="project?.currentStage !== '软装' && project?.currentStage !== '建模'" [class.active]="project?.currentStage === '软装'">
  173. <div class="stage-icon">
  174. <span>{{ project?.currentStage !== '软装' && project?.currentStage !== '建模' ? '✓' : '2' }}</span>
  175. </div>
  176. <div class="stage-name">软装</div>
  177. </div>
  178. <div class="progress-line"></div>
  179. <div class="stage" [class.completed]="project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装'" [class.active]="project?.currentStage === '渲染'">
  180. <div class="stage-icon">
  181. <span>{{ project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装' ? '✓' : '3' }}</span>
  182. </div>
  183. <div class="stage-name">渲染</div>
  184. </div>
  185. <div class="progress-line"></div>
  186. <div class="stage" [class.completed]="project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染'" [class.active]="project?.currentStage === '后期'">
  187. <div class="stage-icon">
  188. <span>{{ project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染' ? '✓' : '4' }}</span>
  189. </div>
  190. <div class="stage-name">后期</div>
  191. </div>
  192. </div>
  193. </div>
  194. <!-- 当前阶段操作 -->
  195. <div *ngIf="project" class="current-stage-actions">
  196. <div class="current-stage-info">
  197. <h3>当前阶段: <span class="stage-highlight">{{ project.currentStage }}</span></h3>
  198. </div>
  199. <div class="stage-actions">
  200. <button *ngIf="project.currentStage === '建模'" (click)="updateProjectStage('软装')" [disabled]="!areAllModelChecksPassed()" class="primary-btn">
  201. {{ areAllModelChecksPassed() ? '完成建模' : '完成所有模型检查' }}
  202. </button>
  203. <button *ngIf="project.currentStage === '软装'" (click)="updateProjectStage('渲染')" class="primary-btn">完成软装</button>
  204. <button *ngIf="project.currentStage === '渲染'" (click)="updateProjectStage('后期')" class="primary-btn">完成渲染</button>
  205. <button *ngIf="project.currentStage === '后期'" (click)="updateProjectStage('完成')" class="primary-btn">完成后期</button>
  206. </div>
  207. </div>
  208. <!-- 模型误差检查清单 - 仅在建模阶段显示 -->
  209. <div *ngIf="project?.currentStage === '建模'" class="model-check-section">
  210. <h3>模型误差检查清单</h3>
  211. <div class="checklist">
  212. <div *ngFor="let item of modelCheckItems" class="checklist-item">
  213. <input type="checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, !item.isPassed)" class="custom-checkbox">
  214. <span class="checklist-text">{{ item.name }}</span>
  215. <span class="check-status" [class.passed]="item.isPassed" [class.failed]="!item.isPassed">
  216. {{ item.isPassed ? '通过' : '未通过' }}
  217. </span>
  218. </div>
  219. </div>
  220. </div>
  221. </div>
  222. </div>
  223. </div>
  224. <!-- 阶段专属任务卡片 - 仅在对应节点显示 -->
  225. <div class="stage-specific-cards">
  226. <!-- 渲染阶段专属卡片 -->
  227. <div *ngIf="project?.currentStage === '渲染'" class="render-progress-card card">
  228. <h2>渲染进度</h2>
  229. <div *ngIf="isLoadingRenderProgress" class="loading-state">
  230. <div class="loading-spinner"></div>
  231. <span>加载中...</span>
  232. </div>
  233. <div *ngIf="errorLoadingRenderProgress" class="error-state">
  234. <span>加载失败</span>
  235. <button (click)="retryLoadRenderProgress()" class="secondary-btn">点击重试</button>
  236. </div>
  237. <div *ngIf="renderProgress && !isLoadingRenderProgress && !errorLoadingRenderProgress" class="progress-content">
  238. <!-- 渲染超时预警 -->
  239. <div *ngIf="renderProgress.estimatedTimeRemaining <= 3" class="timeout-warning">
  240. <div class="warning-icon">⚠️</div>
  241. <div class="warning-text">
  242. <span class="warning-title">渲染即将超时</span>
  243. <span class="warning-time">预计剩余时间: {{ renderProgress.estimatedTimeRemaining }} 小时</span>
  244. </div>
  245. </div>
  246. <!-- 渲染异常反馈模块 -->
  247. <div class="render-exception-section">
  248. <h3>渲染异常反馈</h3>
  249. <div class="exception-feedback-form">
  250. <div class="form-group">
  251. <label>异常类型:</label>
  252. <select [(ngModel)]="exceptionType" class="exception-select">
  253. <option value="failed">渲染失败</option>
  254. <option value="stuck">渲染卡顿</option>
  255. <option value="quality">渲染质量问题</option>
  256. <option value="other">其他问题</option>
  257. </select>
  258. </div>
  259. <div class="form-group">
  260. <label>详细描述:</label>
  261. <textarea
  262. [(ngModel)]="exceptionDescription"
  263. placeholder="请描述渲染过程中遇到的具体问题..."
  264. class="exception-textarea"
  265. ></textarea>
  266. </div>
  267. <div class="form-group">
  268. <label>上传截图 (可选):</label>
  269. <input type="file" (change)="uploadExceptionScreenshot($event)" class="screenshot-upload" id="screenshot-upload">
  270. <label for="screenshot-upload" class="upload-btn">选择文件</label>
  271. <div *ngIf="exceptionScreenshotUrl" class="screenshot-preview">
  272. <img [src]="exceptionScreenshotUrl" alt="异常截图">
  273. <button (click)="clearExceptionScreenshot()" class="clear-screenshot-btn">×</button>
  274. </div>
  275. </div>
  276. <button
  277. (click)="submitExceptionFeedback()"
  278. [disabled]="!exceptionDescription.trim()"
  279. class="submit-feedback-btn"
  280. >
  281. 提交反馈并联系技术支持
  282. </button>
  283. </div>
  284. <!-- 历史反馈记录 -->
  285. <div class="exception-history" *ngIf="exceptionHistories.length > 0">
  286. <h4>历史反馈记录</h4>
  287. <div class="history-list">
  288. <div *ngFor="let history of exceptionHistories" class="history-item">
  289. <div class="history-header">
  290. <span class="history-type">{{ getExceptionTypeText(history.type) }}</span>
  291. <span class="history-time">{{ formatDate(history.submitTime) }}</span>
  292. </div>
  293. <div class="history-description">{{ history.description }}</div>
  294. <div class="history-status" [class.status-pending]="history.status === '待处理'" [class.status-processing]="history.status === '处理中'" [class.status-resolved]="history.status === '已解决'">
  295. {{ history.status }} - {{ history.response || '暂无回复' }}
  296. </div>
  297. </div>
  298. </div>
  299. </div>
  300. </div>
  301. <div class="progress-bar-container">
  302. <div class="progress-bar">
  303. <div class="progress-fill" [style.width.percent]="renderProgress.completionRate"></div>
  304. </div>
  305. <div class="progress-percentage">{{ renderProgress.completionRate }}%</div>
  306. </div>
  307. <div class="progress-details">
  308. <div class="progress-info">
  309. <span class="info-label">预计剩余时间</span>
  310. <span class="info-value">{{ renderProgress.estimatedTimeRemaining }} 小时</span>
  311. </div>
  312. <div class="progress-info">
  313. <span class="info-label">当前状态</span>
  314. <span class="info-value">{{ renderProgress.status }}</span>
  315. </div>
  316. </div>
  317. </div>
  318. </div>
  319. <!-- 客户反馈和设计师变更记录 -->
  320. <div class="additional-info-section">
  321. <div class="feedback-card card">
  322. <h2>客户反馈</h2>
  323. <div *ngIf="feedbacks.length === 0" class="empty-state">
  324. <div class="empty-icon">📭</div>
  325. <span>暂无客户反馈</span>
  326. </div>
  327. <div *ngFor="let feedback of feedbacks" class="feedback-item">
  328. <div class="feedback-header">
  329. <div class="feedback-meta">
  330. <span class="feedback-type">{{ feedback.isSatisfied ? '满意反馈' : '不满意反馈' }}</span>
  331. <span *ngIf="getFeedbackTag(feedback)" class="feedback-tag">{{ getFeedbackTag(feedback) }}</span>
  332. </div>
  333. <div class="feedback-date">{{ feedback.createdAt | date:'yyyy-MM-dd HH:mm' }}</div>
  334. </div>
  335. <div class="feedback-content">
  336. <div class="feedback-status"><span class="status-label">状态:</span> <span class="status-value">{{ feedback.status }}</span></div>
  337. <!-- 反馈倒计时 -->
  338. <div *ngIf="feedback.status === '待处理' && feedbackTimeoutCountdown > 0" class="feedback-countdown">
  339. <span class="countdown-icon">⏱️</span>
  340. <span>响应倒计时: {{ formatCountdown(feedbackTimeoutCountdown) }}</span>
  341. </div>
  342. <div class="feedback-details">
  343. <div class="detail-item">
  344. <span class="detail-label">修改部位:</span>
  345. <span class="detail-value">{{ feedback.problemLocation || '-' }}</span>
  346. </div>
  347. <div class="detail-item">
  348. <span class="detail-label">期望效果:</span>
  349. <span class="detail-value">{{ feedback.expectedEffect || '-' }}</span>
  350. </div>
  351. <div class="detail-item">
  352. <span class="detail-label">参考案例:</span>
  353. <span class="detail-value">{{ feedback.referenceCase || '-' }}</span>
  354. </div>
  355. </div>
  356. </div>
  357. <div class="feedback-actions">
  358. <button (click)="updateFeedbackStatus(feedback.id, '处理中')" [disabled]="feedback.status === '处理中' || feedback.status === '已解决'" class="secondary-btn">
  359. 标记为处理中
  360. </button>
  361. <button (click)="updateFeedbackStatus(feedback.id, '已解决')" [disabled]="feedback.status === '已解决'" class="primary-btn">
  362. 标记为已解决
  363. </button>
  364. </div>
  365. </div>
  366. </div>
  367. <div class="designer-change-card card">
  368. <h2>设计师变更记录</h2>
  369. <div class="change-actions">
  370. <button (click)="initiateDesignerChange('技能不匹配')" class="secondary-btn">发起变更 - 技能不匹配</button>
  371. <button (click)="initiateDesignerChange('休假')" class="secondary-btn">发起变更 - 休假</button>
  372. </div>
  373. <div *ngIf="designerChanges.length === 0" class="empty-state">
  374. <div class="empty-icon">👤</div>
  375. <span>暂无设计师变更记录</span>
  376. </div>
  377. <div *ngFor="let change of designerChanges" class="change-item">
  378. <div class="change-header">
  379. <div class="change-time">{{ change.changeTime | date:'yyyy-MM-dd' }}</div>
  380. <button *ngIf="!change.acceptanceTime" (click)="acceptDesignerChange(change.id)" class="accept-change-btn primary-btn">
  381. 确认承接
  382. </button>
  383. </div>
  384. <div class="change-details">
  385. <div class="designer-change-info">
  386. <div class="designer-change">
  387. <span class="designer-label">原设计师:</span>
  388. <span class="designer-name">{{ change.oldDesignerName }}</span>
  389. </div>
  390. <div class="designer-change">
  391. <span class="designer-label">新设计师:</span>
  392. <span class="designer-name">{{ change.newDesignerName }}</span>
  393. </div>
  394. </div>
  395. <div class="workload-info">
  396. <span>已完成工作量: <strong>{{ change.completedWorkload }}%</strong></span>
  397. </div>
  398. <div class="achievements">
  399. <h4>历史阶段成果:</h4>
  400. <ul>
  401. <li *ngFor="let achievement of change.historicalAchievements">{{ achievement }}</li>
  402. </ul>
  403. </div>
  404. <div *ngIf="change.acceptanceTime" class="change-status">
  405. 承接确认时间: {{ change.acceptanceTime | date:'yyyy-MM-dd' }}
  406. </div>
  407. </div>
  408. </div>
  409. </div>
  410. </div>
  411. </div>
  412. <!-- 项目成员标签页 -->
  413. <div *ngIf="isActiveTab('members')" class="members-tab-content">
  414. <div class="project-members-card card">
  415. <h2>项目团队成员</h2>
  416. <div class="members-grid">
  417. <div *ngFor="let member of projectMembers" class="member-card">
  418. <div class="member-avatar">
  419. <div class="avatar-placeholder">{{ member.avatar }}</div>
  420. </div>
  421. <div class="member-info">
  422. <div class="member-name">{{ member.name }}</div>
  423. <div class="member-role">{{ member.role }}</div>
  424. <div class="member-skills">
  425. <span *ngIf="member.skillMatch >= 90" class="skill-badge">技能匹配良好</span>
  426. <span *ngIf="member.progress >= 80" class="skill-badge">进度领先</span>
  427. </div>
  428. </div>
  429. <div class="member-actions">
  430. <button class="message-btn">
  431. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  432. <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>
  433. </svg>
  434. </button>
  435. </div>
  436. </div>
  437. </div>
  438. </div>
  439. <div class="members-timeline-card card">
  440. <h2>团队协作时间轴</h2>
  441. <div class="timeline-entries">
  442. <div *ngFor="let event of timelineEvents" class="timeline-entry">
  443. <div class="timeline-dot"></div>
  444. <div class="timeline-content">
  445. <div class="timeline-header">
  446. <span class="timeline-author">{{ getEventAuthor(event.action) }}</span>
  447. <span class="timeline-time">{{ event.time }}</span>
  448. </div>
  449. <div class="timeline-text">{{ event.description }}</div>
  450. </div>
  451. </div>
  452. </div>
  453. </div>
  454. </div>
  455. <!-- 项目文件标签页 -->
  456. <div *ngIf="isActiveTab('files')" class="files-tab-content">
  457. <div class="file-actions">
  458. <button class="upload-btn primary-btn">
  459. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  460. <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
  461. <polyline points="17 8 12 3 7 8"></polyline>
  462. <line x1="12" y1="3" x2="12" y2="15"></line>
  463. </svg>
  464. <span>上传文件</span>
  465. </button>
  466. <div class="file-filter">
  467. <select class="file-filter-select">
  468. <option value="all">全部文件</option>
  469. <option value="images">图片</option>
  470. <option value="documents">文档</option>
  471. <option value="models">模型文件</option>
  472. </select>
  473. </div>
  474. </div>
  475. <div class="files-grid">
  476. <div *ngFor="let file of projectFiles" class="file-card">
  477. <div class="file-icon">
  478. <svg *ngIf="file.type.includes('pdf')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#EA4335" stroke-width="2">
  479. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
  480. <polyline points="14 2 14 8 20 8"></polyline>
  481. <line x1="16" y1="13" x2="8" y2="13"></line>
  482. <line x1="16" y1="17" x2="8" y2="17"></line>
  483. <polyline points="10 9 9 9 8 9"></polyline>
  484. </svg>
  485. <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">
  486. <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
  487. <circle cx="8.5" cy="8.5" r="1.5"></circle>
  488. <polyline points="21 15 16 10 5 21"></polyline>
  489. </svg>
  490. <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">
  491. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
  492. <polyline points="14 2 14 8 20 8"></polyline>
  493. <line x1="16" y1="13" x2="8" y2="13"></line>
  494. <line x1="16" y1="17" x2="8" y2="17"></line>
  495. <polyline points="10 9 9 9 8 9"></polyline>
  496. </svg>
  497. <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">
  498. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
  499. <polyline points="14 2 14 8 20 8"></polyline>
  500. <line x1="16" y1="13" x2="8" y2="13"></line>
  501. <line x1="16" y1="17" x2="8" y2="17"></line>
  502. <polyline points="10 9 9 9 8 9"></polyline>
  503. </svg>
  504. <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">
  505. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
  506. <polyline points="14 2 14 8 20 8"></polyline>
  507. <line x1="16" y1="13" x2="8" y2="13"></line>
  508. <line x1="16" y1="17" x2="8" y2="17"></line>
  509. <polyline points="10 9 9 9 8 9"></polyline>
  510. </svg>
  511. </div>
  512. <div class="file-info">
  513. <div class="file-name">{{ file.name }}</div>
  514. <div class="file-meta">
  515. <span class="file-size">{{ file.size }}</span>
  516. <span class="file-date">{{ file.date }}</span>
  517. </div>
  518. </div>
  519. <button class="file-action-btn">
  520. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  521. <circle cx="12" cy="12" r="1"></circle>
  522. <circle cx="19" cy="12" r="1"></circle>
  523. <circle cx="5" cy="12" r="1"></circle>
  524. </svg>
  525. </button>
  526. </div>
  527. <div class="file-icon">
  528. <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#FBBC05" stroke-width="2">
  529. <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
  530. <polyline points="14 2 14 8 20 8"></polyline>
  531. <line x1="16" y1="13" x2="8" y2="13"></line>
  532. <line x1="16" y1="17" x2="8" y2="17"></line>
  533. <polyline points="10 9 9 9 8 9"></polyline>
  534. </svg>
  535. </div>
  536. <div class="file-info">
  537. <div class="file-name">色彩方案.xlsx</div>
  538. <div class="file-meta">
  539. <span class="file-size">0.9MB</span>
  540. <span class="file-date">2025-09-04</span>
  541. </div>
  542. </div>
  543. <button class="file-action-btn">
  544. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  545. <circle cx="12" cy="12" r="1"></circle>
  546. <circle cx="19" cy="12" r="1"></circle>
  547. <circle cx="5" cy="12" r="1"></circle>
  548. </svg>
  549. </button>
  550. </div>
  551. </div>
  552. <div class="file-storage-info">
  553. <div class="storage-bar">
  554. <div class="storage-used" style="width: 45%"></div>
  555. </div>
  556. <div class="storage-text">
  557. <span>已使用 450MB / 1GB</span>
  558. </div>
  559. </div>
  560. </div>
  561. </div>
  562. </div>
  563. <!-- 分阶段结算记录卡片 -->
  564. <div class="settlement-card card">
  565. <h2>分阶段结算记录</h2>
  566. <div *ngIf="settlements.length === 0" class="empty-state">
  567. <div class="empty-icon">💰</div>
  568. <span>暂无结算记录</span>
  569. </div>
  570. <div *ngIf="settlements.length > 0" class="settlement-table">
  571. <table>
  572. <thead>
  573. <tr>
  574. <th>阶段</th>
  575. <th>比例</th>
  576. <th>金额(元)</th>
  577. <th>状态</th>
  578. <th>完成时间</th>
  579. </tr>
  580. </thead>
  581. <tbody>
  582. <tr *ngFor="let settlement of settlements">
  583. <td>{{ settlement.stage }}</td>
  584. <td>{{ settlement.percentage }}%</td>
  585. <td>{{ settlement.amount }}</td>
  586. <td><span class="status-badge" [class.status-pending]="settlement.status === '待结算'" [class.status-settled]="settlement.status === '已结算'">{{ settlement.status }}</span></td>
  587. <td>{{ settlement.completionTime | date:'yyyy-MM-dd' }}</td>
  588. </tr>
  589. </tbody>
  590. </table>
  591. </div>
  592. </div>