project-loader.component.html 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. <div class="project-loader">
  2. <!-- 头部 -->
  3. <div class="header">
  4. <h1 class="title">项目管理</h1>
  5. </div>
  6. <!-- 加载中状态 -->
  7. @if (loading) {
  8. <div class="loading-container">
  9. <div class="skeleton-loader">
  10. <!-- 骨架屏动画 -->
  11. <div class="skeleton-header"></div>
  12. <div class="skeleton-card"></div>
  13. <div class="skeleton-card"></div>
  14. <div class="skeleton-buttons">
  15. <div></div>
  16. <div></div>
  17. </div>
  18. </div>
  19. <div class="spinner">
  20. <div class="spinner-circle"></div>
  21. </div>
  22. <p class="loading-message">{{ loadingMessage }}</p>
  23. </div>
  24. }
  25. <!-- 错误状态 -->
  26. @if (error && !loading) {
  27. <div class="error-container">
  28. <div class="error-icon">
  29. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  30. <path d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm0 319.91a20 20 0 1120-20 20 20 0 01-20 20zm21.72-201.15l-5.74 122a16 16 0 01-32 0l-5.74-121.94v-.05a21.74 21.74 0 1143.44 0z"/>
  31. </svg>
  32. </div>
  33. <h2 class="error-title">加载失败</h2>
  34. <p class="error-message">{{ error }}</p>
  35. <button class="btn btn-primary" (click)="reload()">
  36. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  37. <path d="M320 146s24.36-12-64-12a160 160 0 10160 160" fill="none" stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="32"/>
  38. <path fill="currentColor" d="M256 58l80 80-80 80"/>
  39. </svg>
  40. 重新加载
  41. </button>
  42. </div>
  43. }
  44. <!-- 创建项目引导 -->
  45. @if (showCreateGuide && !loading && !error) {
  46. <div class="create-guide-container">
  47. <!-- 群聊信息卡片 -->
  48. <div class="card group-info-card">
  49. <div class="card-header">
  50. <h3 class="card-title">{{ groupChat?.get('name') }}</h3>
  51. <p class="card-subtitle">当前群聊暂无关联项目</p>
  52. </div>
  53. <div class="card-content">
  54. <p>您可以为该群聊创建新项目,或选择已有项目关联。</p>
  55. </div>
  56. </div>
  57. <!-- 创建新项目 -->
  58. <div class="card create-project-card">
  59. <div class="card-header">
  60. <h3 class="card-title">
  61. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  62. <path d="M448 256c0-106-86-192-192-192S64 150 64 256s86 192 192 192 192-86 192-192z" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/>
  63. <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M256 176v160M336 256H176"/>
  64. </svg>
  65. 创建新项目
  66. </h3>
  67. </div>
  68. <div class="card-content">
  69. <div class="form-group">
  70. <label for="projectName">项目名称</label>
  71. <input
  72. id="projectName"
  73. type="text"
  74. class="form-input"
  75. [(ngModel)]="projectName"
  76. placeholder="输入项目名称"
  77. [disabled]="creating">
  78. </div>
  79. <button
  80. class="btn btn-primary btn-block"
  81. (click)="createProject()"
  82. [disabled]="creating || !projectName.trim()">
  83. @if (creating) {
  84. <div class="btn-spinner"></div>
  85. <span>创建中...</span>
  86. } @else {
  87. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  88. <path d="M461.81 53.81a4.47 4.47 0 00-3.3-3.39c-54.38-13.3-180 34.09-248.13 102.17a294.9 294.9 0 00-33.09 39.08c-21-1.9-42-.3-59.88 7.5-50.49 22.2-65.18 80.18-69.28 105.07a9 9 0 009.8 10.4l81.07-8.9a180.29 180.29 0 001.1 18.3 18.15 18.15 0 005.3 11.09l31.39 31.39a18.15 18.15 0 0011.1 5.3 179.91 179.91 0 0018.19 1.1l-8.89 81a9 9 0 0010.39 9.79c24.9-4 83-18.69 105.07-69.17 7.8-17.9 9.4-38.79 7.6-59.69a293.91 293.91 0 0039.19-33.09c68.38-68 115.47-190.86 102.37-247.95zM298.66 213.67a42.7 42.7 0 1160.38 0 42.65 42.65 0 01-60.38 0z"/>
  89. <path d="M109.64 352a45.06 45.06 0 00-26.35 12.84C65.67 382.52 64 448 64 448s65.52-1.67 83.15-19.31A44.73 44.73 0 00160 402.32"/>
  90. </svg>
  91. 创建项目
  92. }
  93. </button>
  94. </div>
  95. </div>
  96. <!-- 历史项目列表 -->
  97. @if (historyProjects.length > 0) {
  98. <div class="card history-projects-card">
  99. <div class="card-header">
  100. <h3 class="card-title">
  101. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  102. <path d="M256 64C150 64 64 150 64 256s86 192 192 192 192-86 192-192S362 64 256 64z" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/>
  103. <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M256 128v144h96"/>
  104. </svg>
  105. 群聊相关的历史项目
  106. </h3>
  107. <p class="card-subtitle">点击关联到当前群聊</p>
  108. </div>
  109. <div class="card-content">
  110. <div class="list">
  111. @for (proj of historyProjects; track proj.id) {
  112. <div class="list-item" (click)="selectHistoryProject(proj)">
  113. <div class="list-item-content">
  114. <h4 class="list-item-title">{{ proj.get('title') }}</h4>
  115. <div class="list-item-meta">
  116. <span class="badge" [ngClass]="getProjectStatusClass(proj.get('status'))">
  117. {{ proj.get('status') }}
  118. </span>
  119. <span class="list-item-stage">{{ proj.get('currentStage') }}</span>
  120. </div>
  121. <p class="list-item-date">
  122. 创建时间: {{ formatDate(proj.get('createdAt')) }}
  123. </p>
  124. </div>
  125. <div class="list-item-arrow">
  126. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  127. <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="48" d="M184 112l144 144-144 144"/>
  128. </svg>
  129. </div>
  130. </div>
  131. }
  132. </div>
  133. </div>
  134. </div>
  135. }
  136. <!-- 用户信息底部 -->
  137. @if (currentUser) {
  138. <div class="user-info-footer">
  139. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  140. <path d="M258.9 48C141.92 46.42 46.42 141.92 48 258.9c1.56 112.19 92.91 203.54 205.1 205.1 117 1.6 212.48-93.9 210.88-210.88C462.44 140.91 371.09 49.56 258.9 48zM385.32 375.25a4 4 0 01-6.14-.32 124.27 124.27 0 00-32.35-29.59C321.37 329 289.11 320 256 320s-65.37 9-90.83 25.34a124.24 124.24 0 00-32.35 29.58 4 4 0 01-6.14.32A175.32 175.32 0 0180 259c-1.63-97.31 78.22-178.76 175.57-179S432 158.81 432 256a175.32 175.32 0 01-46.68 119.25z"/>
  141. <path d="M256 144c-19.72 0-37.55 7.39-50.22 20.82s-19 32-17.57 51.93C191.11 256 221.52 288 256 288s64.83-32 67.79-71.24c1.48-19.74-4.8-38.14-17.68-51.82C293.39 151.44 275.59 144 256 144z"/>
  142. </svg>
  143. <span>当前用户: {{ getCurrentUserName() }} ({{ getCurrentUserRole() }})</span>
  144. </div>
  145. }
  146. </div>
  147. }
  148. </div>