project-members-modal.component.html 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <!-- 模态框背景 -->
  2. <div class="modal-overlay"
  3. *ngIf="isVisible"
  4. (click)="onBackdropClick($event)">
  5. <!-- 模态框内容 -->
  6. <div class="modal-container" (click)="$event.stopPropagation()">
  7. <!-- 模态框头部 -->
  8. <div class="modal-header">
  9. <div class="header-left">
  10. <h2 class="modal-title">
  11. <svg class="title-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  12. <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
  13. <circle cx="9" cy="7" r="4"></circle>
  14. <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
  15. <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
  16. </svg>
  17. 项目成员
  18. </h2>
  19. <div class="member-stats">
  20. <span class="stat-item">
  21. <span class="stat-number">{{ totalMembers }}</span>
  22. <span class="stat-label">总成员</span>
  23. </span>
  24. <span class="stat-item">
  25. <span class="stat-number">{{ projectTeamMembers }}</span>
  26. <span class="stat-label">项目团队</span>
  27. </span>
  28. @if (pendingAddMembers > 0) {
  29. <span class="stat-item stat-pending">
  30. <span class="stat-number">{{ pendingAddMembers }}</span>
  31. <span class="stat-label">待加入群聊</span>
  32. </span>
  33. }
  34. </div>
  35. </div>
  36. <div class="header-right">
  37. <!-- 环境指示器 -->
  38. <div class="environment-indicator">
  39. @if (isWxworkEnvironment) {
  40. <span class="env-badge env-wxwork">
  41. <svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
  42. <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.88c0 .53-.21.9-.74.26-.18.07-.36.06-.51.14-.75.21-.13.18-.3.36-.3.75.06.39.12.69.12.75.21.37.12.69.12 1.06 0 .39-.13.7-.12.75-.06-.06-.18-.15-.51-.14-.75-.21-.53-.21-.9-.74-.26-.18-.07-.36-.06-.51-.14-.75-.21-.13-.18-.3-.36-.3-.75.06-.39.12-.69.12-.75-.21-.37-.12-.69-.12-1.06 0-.39.13-.7-.12-.75.06-.06.18-.15.51-.14-.75.21z"/>
  43. </svg>
  44. 企业微信环境
  45. </span>
  46. } @else {
  47. <span class="env-badge env-default">
  48. <svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
  49. <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17.88c0 .53-.21.9-.74.26-.18.07-.36.06-.51.14-.75.21-.13.18-.3.36-.3.75.06.39.12.69.12.75.21.37.12.69.12 1.06 0 .39-.13.7-.12.75-.06-.06-.18-.15-.51-.14-.75-.21-.53-.21-.9-.74-.26-.18-.07-.36-.06-.51-.14-.75-.21-.13-.18-.3-.36-.3-.75.06-.39.12-.69.12-.75-.21-.37-.12-.69-.12-1.06 0-.39.13-.7-.12-.75.06-.06.18-.15.51-.14-.75.21z"/>
  50. </svg>
  51. 普通环境
  52. </span>
  53. }
  54. </div>
  55. <!-- 搜索框 -->
  56. <div class="search-box">
  57. <svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  58. <circle cx="11" cy="11" r="8"></circle>
  59. <path d="m21 21-4.35-4.35"></path>
  60. </svg>
  61. <input
  62. type="text"
  63. class="search-input"
  64. placeholder="搜索成员..."
  65. [(ngModel)]="searchQuery">
  66. </div>
  67. <!-- 过滤器 -->
  68. <select class="filter-select" [(ngModel)]="memberFilter">
  69. <option value="all">全部成员</option>
  70. <option value="team">项目团队</option>
  71. <option value="ingroup">已在群聊</option>
  72. <option value="pending">待加入群聊</option>
  73. </select>
  74. <!-- 关闭按钮 -->
  75. <button class="close-btn" (click)="onClose()">
  76. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  77. <line x1="18" y1="6" x2="6" y2="18"></line>
  78. <line x1="6" y1="6" x2="18" y2="18"></line>
  79. </svg>
  80. </button>
  81. </div>
  82. </div>
  83. <!-- 模态框内容 -->
  84. <div class="modal-content">
  85. <!-- 加载状态 -->
  86. @if (loading) {
  87. <div class="loading-state">
  88. <div class="loading-spinner"></div>
  89. <p>加载成员信息...</p>
  90. </div>
  91. } @else if (error) {
  92. <!-- 错误状态 -->
  93. <div class="error-state">
  94. <svg class="error-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  95. <circle cx="12" cy="12" r="10"></circle>
  96. <line x1="15" y1="9" x2="9" y2="15"></line>
  97. <line x1="9" y1="9" x2="15" y2="15"></line>
  98. </svg>
  99. <h3>加载失败</h3>
  100. <p>{{ error }}</p>
  101. <button class="retry-btn" (click)="loadMembers()">重试</button>
  102. </div>
  103. } @else if (getFilteredMembers().length === 0) {
  104. <!-- 空状态 -->
  105. <div class="empty-state">
  106. <svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  107. <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
  108. <circle cx="9" cy="7" r="4"></circle>
  109. <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
  110. <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
  111. </svg>
  112. <h3>暂无成员</h3>
  113. <p>该项目还没有添加任何成员</p>
  114. </div>
  115. } @else {
  116. <!-- 成员列表 -->
  117. <div class="members-list">
  118. @for (member of getFilteredMembers(); track member.id) {
  119. <div class="member-card" [class]="getMemberStatusClass(member)">
  120. <!-- 成员头像 -->
  121. <div class="member-avatar">
  122. @if (member.avatar) {
  123. <img [src]="member.avatar" [alt]="member.name" class="avatar-image" />
  124. } @else {
  125. <div class="avatar-placeholder">
  126. {{ member.name.charAt(0).toUpperCase() }}
  127. </div>
  128. }
  129. </div>
  130. <!-- 成员信息 -->
  131. <div class="member-info">
  132. <h4 class="member-name">{{ member.name }}</h4>
  133. <div class="member-meta">
  134. <span class="member-role">
  135. <span class="role-badge" [class]="getRoleBadgeClass(member.role)">
  136. {{ member.role }}
  137. </span>
  138. </span>
  139. @if (member.department) {
  140. <span class="member-department">{{ member.department }}</span>
  141. }
  142. <span class="member-status" [class]="getMemberStatusClass(member)">
  143. {{ getMemberStatusText(member) }}
  144. </span>
  145. </div>
  146. @if (member.isInProjectTeam && !member.isInGroupChat && isWxworkEnvironment) {
  147. <div class="add-to-group-hint">
  148. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  149. <circle cx="12" cy="12" r="10"></circle>
  150. <line x1="12" y1="8" x2="12" y2="16"></line>
  151. <path d="M8 12h8"></path>
  152. </svg>
  153. <span>点击添加到群聊</span>
  154. </div>
  155. }
  156. </div>
  157. <!-- 操作按钮 -->
  158. <div class="member-actions">
  159. @if (member.isInProjectTeam && !member.isInGroupChat && isWxworkEnvironment) {
  160. <button
  161. class="action-btn add-btn"
  162. (click)="addMemberToGroupChat(member)"
  163. title="添加到群聊">
  164. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  165. <circle cx="12" cy="12" r="10"></circle>
  166. <line x1="12" y1="8" x2="12" y2="16"></line>
  167. <path d="M8 12h8"></path>
  168. </svg>
  169. <span>添加</span>
  170. </button>
  171. }
  172. <div class="status-indicator" [class]="getMemberStatusClass(member)">
  173. @if (member.isInProjectTeam && member.isInGroupChat) {
  174. <svg viewBox="0 0 24 24" fill="currentColor">
  175. <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7.41l-1.41-1.41z"/>
  176. </svg>
  177. }
  178. @else if (member.isInProjectTeam && !member.isInGroupChat) {
  179. <svg viewBox="0 0 24 24" fill="currentColor">
  180. <circle cx="12" cy="12" r="10" opacity="0.3"/>
  181. <path d="M12 8v8"/>
  182. <path d="M8 12h8"/>
  183. </svg>
  184. }
  185. @else {
  186. <svg viewBox="0 0 24 24" fill="currentColor">
  187. <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15.88c0 .53-.21.9-.74.26-.18.07-.36.06-.51.14-.75.21-.13.18-.3.36-.3.75.06.39.12.69.12.75.21.37.12.69.12 1.06 0 .39-.13.7-.12.75-.06-.06-.18-.15-.51-.14-.75-.21-.53-.21-.9-.74-.26-.18-.07-.36-.06-.51-.14-.75-.21-.13-.18-.3-.36-.3-.75.06-.39.12-.69.12-.75-.21-.37-.12-.69-.12-1.06 0-.39.13-.7-.12-.75.06-.06.18-.15.51-.14-.75.21z"/>
  188. </svg>
  189. }
  190. </div>
  191. </div>
  192. </div>
  193. }
  194. </div>
  195. }
  196. </div>
  197. </div>
  198. </div>