project-members-modal.component.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. import { FmodeObject, FmodeQuery, FmodeParse } from 'fmode-ng/parse';
  5. import { WxworkCorp, WxworkSDK } from 'fmode-ng/core';
  6. const Parse = FmodeParse.with('nova');
  7. export interface ProjectMember {
  8. id: string;
  9. name: string;
  10. userid: string;
  11. avatar?: string;
  12. role: string;
  13. department?: string;
  14. isInGroupChat: boolean;
  15. isInProjectTeam: boolean;
  16. projectTeamId?: string;
  17. profileId?: string;
  18. }
  19. export interface GroupChatMember {
  20. userid: string;
  21. name: string;
  22. type: number; // 1: 内部成员 2: 外部联系人
  23. avatar?: string;
  24. }
  25. @Component({
  26. selector: 'app-project-members-modal',
  27. standalone: true,
  28. imports: [CommonModule, FormsModule],
  29. templateUrl: './project-members-modal.component.html',
  30. styleUrls: ['./project-members-modal.component.scss']
  31. })
  32. export class ProjectMembersModalComponent implements OnInit {
  33. @Input() project: FmodeObject | null = null;
  34. @Input() groupChat: FmodeObject | null = null;
  35. @Input() currentUser: FmodeObject | null = null;
  36. @Input() isVisible: boolean = false;
  37. @Input() cid: string = '';
  38. @Output() close = new EventEmitter<void>();
  39. members: ProjectMember[] = [];
  40. loading: boolean = false;
  41. error: string | null = null;
  42. isWxworkEnvironment: boolean = false;
  43. // 统计信息
  44. totalMembers: number = 0;
  45. groupChatMembers: number = 0;
  46. projectTeamMembers: number = 0;
  47. pendingAddMembers: number = 0;
  48. // 过滤和搜索
  49. searchQuery: string = '';
  50. memberFilter: 'all' | 'ingroup' | 'team' | 'pending' = 'all';
  51. // 企业微信API
  52. private wecorp: WxworkCorp | null = null;
  53. private wwsdk: WxworkSDK | null = null;
  54. isWechat:boolean = false;
  55. constructor() {
  56. let ua = navigator.userAgent.toLowerCase();
  57. this.isWechat = ua.indexOf('micromessenger') !== -1;
  58. this.checkWxworkEnvironment();
  59. }
  60. ngOnInit(): void {
  61. if (this.isVisible && this.project) {
  62. this.loadMembers();
  63. }
  64. }
  65. ngOnChanges(): void {
  66. if (this.isVisible && this.project) {
  67. this.loadMembers();
  68. }
  69. }
  70. private checkWxworkEnvironment(): void {
  71. // 检查是否在企业微信环境中
  72. this.wecorp = new WxworkCorp(this.cid);
  73. this.wwsdk = new WxworkSDK({cid:this.cid,appId:'crm'});
  74. console.log('✅ 企业微信环境检测成功');
  75. }
  76. async loadMembers(): Promise<void> {
  77. if (!this.project) return;
  78. try {
  79. this.loading = true;
  80. this.error = null;
  81. // 1. 加载项目团队成员
  82. const projectTeamMembers = await this.loadProjectTeamMembers();
  83. // 2. 加载群聊成员
  84. if(!this.groupChat?.id){
  85. const gcQuery2 = new Parse.Query('GroupChat');
  86. gcQuery2.equalTo('project', this.project?.id);
  87. this.groupChat = await gcQuery2.first();
  88. }
  89. const groupChatMembers = await this.loadGroupChatMembers();
  90. // 3. 合并成员数据
  91. console.log("999",projectTeamMembers, groupChatMembers)
  92. this.mergeMembersData(projectTeamMembers, groupChatMembers);
  93. this.calculateStats();
  94. console.log(`✅ 加载了 ${this.members.length} 个成员信息`);
  95. } catch (error) {
  96. console.error('❌ 加载成员失败:', error);
  97. this.error = error instanceof Error ? error.message : '加载成员失败';
  98. } finally {
  99. this.loading = false;
  100. }
  101. }
  102. private async loadProjectTeamMembers(): Promise<FmodeObject[]> {
  103. try {
  104. const query = new FmodeQuery('ProjectTeam');
  105. if (this.project) {
  106. query.equalTo('project', this.project.toPointer());
  107. }
  108. query.include('profile','department','profile.department');
  109. query.notEqualTo('isDeleted', true);
  110. return await query.find();
  111. } catch (error) {
  112. console.error('加载项目团队成员失败:', error);
  113. return [];
  114. }
  115. }
  116. private async loadGroupChatMembers(): Promise<GroupChatMember[]> {
  117. if (!this.groupChat) return [];
  118. try {
  119. const memberList = this.groupChat.get('member_list') || [];
  120. return memberList
  121. } catch (error) {
  122. console.error('加载群聊成员失败:', error);
  123. return [];
  124. }
  125. }
  126. private mergeMembersData(projectTeamMembers: FmodeObject[], groupChatMembers: GroupChatMember[]): void {
  127. const memberMap = new Map<string, ProjectMember>();
  128. // 1. 添加项目团队成员
  129. projectTeamMembers.forEach(team => {
  130. const profile = team.get('profile');
  131. if (profile) {
  132. const member: ProjectMember = {
  133. id: profile.id,
  134. name: profile.get('name') || '未知',
  135. userid: profile.get('userid') || '',
  136. avatar: profile.get('data')?.avatar,
  137. role: profile.get('roleName') || '未知',
  138. department: profile.get('department')?.get('name'),
  139. isInGroupChat: false,
  140. isInProjectTeam: true,
  141. projectTeamId: team.id,
  142. profileId: profile.id
  143. };
  144. memberMap.set(profile.id, member);
  145. }
  146. });
  147. // 2. 添加群聊成员(包括外部联系人)
  148. groupChatMembers.forEach(groupMember => {
  149. // 查找是否已在项目团队中
  150. const existingMember = Array.from(memberMap.values()).find(
  151. m => m.userid === groupMember.userid || m.name === groupMember.name
  152. );
  153. if (existingMember) {
  154. existingMember.isInGroupChat = true;
  155. } else {
  156. // 添加仅存在于群聊中的成员
  157. const member: ProjectMember = {
  158. id: groupMember.userid,
  159. name: groupMember.name,
  160. userid: groupMember.userid,
  161. avatar: groupMember.avatar,
  162. role: groupMember.type === 1 ? '外部联系人' : '内部成员',
  163. isInGroupChat: true,
  164. isInProjectTeam: false,
  165. projectTeamId: undefined,
  166. profileId: undefined
  167. };
  168. memberMap.set(groupMember.userid, member);
  169. }
  170. });
  171. this.members = Array.from(memberMap.values());
  172. }
  173. calculateStats(): void {
  174. this.totalMembers = this.members.length;
  175. this.groupChatMembers = this.members.filter(m => m.isInGroupChat).length;
  176. this.projectTeamMembers = this.members.filter(m => m.isInProjectTeam).length;
  177. this.pendingAddMembers = this.members.filter(m => m.isInProjectTeam && !m.isInGroupChat).length;
  178. }
  179. onClose(): void {
  180. this.close.emit();
  181. }
  182. onBackdropClick(event: MouseEvent): void {
  183. if (event.target === event.currentTarget) {
  184. this.onClose();
  185. }
  186. }
  187. getFilteredMembers(): ProjectMember[] {
  188. let filtered = this.members;
  189. // 搜索过滤
  190. if (this.searchQuery) {
  191. const query = this.searchQuery.toLowerCase();
  192. filtered = filtered.filter(member =>
  193. member.name.toLowerCase().includes(query) ||
  194. member.role.toLowerCase().includes(query) ||
  195. member.department?.toLowerCase().includes(query)
  196. );
  197. }
  198. // 状态过滤
  199. switch (this.memberFilter) {
  200. case 'ingroup':
  201. filtered = filtered.filter(m => m.isInGroupChat);
  202. break;
  203. case 'team':
  204. filtered = filtered.filter(m => m.isInProjectTeam);
  205. break;
  206. case 'pending':
  207. filtered = filtered.filter(m => m.isInProjectTeam && !m.isInGroupChat);
  208. break;
  209. }
  210. return filtered.sort((a, b) => {
  211. // 优先级:项目团队成员 > 仅群聊成员
  212. if (a.isInProjectTeam && !b.isInProjectTeam) return -1;
  213. if (!a.isInProjectTeam && b.isInProjectTeam) return 1;
  214. // 按名称排序
  215. return a.name.localeCompare(b.name, 'zh-CN');
  216. });
  217. }
  218. async addMemberToGroupChat(member: ProjectMember): Promise<void> {
  219. if(!this.isWechat){
  220. alert("请在企业微信客户端添加")
  221. }
  222. if (!member.userid) {
  223. alert('该成员没有用户ID,无法添加到群聊');
  224. return;
  225. }
  226. try {
  227. const chatId = this.groupChat?.get('chat_id');
  228. if (!chatId) {
  229. alert('群聊ID不存在');
  230. return;
  231. }
  232. console.log(`🚀 开始添加成员 ${member.name} (${member.userid}) 到群聊 ${chatId}`);
  233. // TODO: 实现正确的企业微信API调用
  234. let result = await this.wwsdk?.ww.updateEnterpriseChat({
  235. chatId: chatId,
  236. userIdsToAdd: [member.userid]
  237. });
  238. // 临时:直接更新本地状态用于演示
  239. if(result){
  240. member.isInGroupChat = true;
  241. this.calculateStats();
  242. }
  243. alert(`✅ 已将 ${member.name} 添加到群聊`);
  244. console.log(`✅ 成功添加成员 ${member.name} 到群聊`);
  245. } catch (error) {
  246. console.error('❌ 添加成员到群聊失败:', error);
  247. alert(`添加失败: ${error instanceof Error ? error.message : '未知错误'}`);
  248. }
  249. }
  250. getRoleBadgeClass(role: string): string {
  251. switch (role) {
  252. case '客服':
  253. return 'role-customer-service';
  254. case '组员':
  255. case '设计师':
  256. return 'role-designer';
  257. case '组长':
  258. return 'role-team-leader';
  259. case '管理员':
  260. return 'role-admin';
  261. case '外部联系人':
  262. return 'role-external';
  263. default:
  264. return 'role-default';
  265. }
  266. }
  267. getMemberStatusClass(member: ProjectMember): string {
  268. if (member.isInProjectTeam && member.isInGroupChat) {
  269. return 'status-active';
  270. } else if (member.isInProjectTeam) {
  271. return 'status-pending';
  272. } else {
  273. return 'status-group-only';
  274. }
  275. }
  276. getMemberStatusText(member: ProjectMember): string {
  277. if (member.isInProjectTeam && member.isInGroupChat) {
  278. return '已加入';
  279. } else if (member.isInProjectTeam) {
  280. return '待加入';
  281. } else {
  282. return '仅群聊';
  283. }
  284. }
  285. }