groupchats.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import { Component, OnInit, signal } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. import { GroupChatService } from '../services/groupchat.service';
  5. import { ProjectService } from '../services/project.service';
  6. import { WxworkCorp } from 'fmode-ng/core';
  7. import { FmodeObject } from 'fmode-ng/core';
  8. interface GroupChat {
  9. id: string;
  10. chat_id: string;
  11. name: string;
  12. project?: string;
  13. projectId?: string;
  14. memberCount: number;
  15. isDisabled?: boolean;
  16. createdAt?: Date;
  17. }
  18. interface Project {
  19. id: string;
  20. title: string;
  21. }
  22. @Component({
  23. selector: 'app-groupchats',
  24. standalone: true,
  25. imports: [CommonModule, FormsModule],
  26. templateUrl: './groupchats.html',
  27. styleUrls: ['./groupchats.scss']
  28. })
  29. export class GroupChats implements OnInit {
  30. groupChats = signal<GroupChat[]>([]);
  31. projects = signal<Project[]>([]);
  32. loading = signal(false);
  33. keyword = signal('');
  34. total = signal(0);
  35. showPanel = false;
  36. panelMode: 'detail' | 'edit' = 'detail';
  37. currentGroupChat: GroupChat | null = null;
  38. formModel: Partial<GroupChat> = {};
  39. // 群组详情数据
  40. groupChatDetail: any = null;
  41. groupMembers: any[] = [];
  42. qrCodeUrl: string = '';
  43. loadingDetail = signal(false);
  44. // 企微Corp(需要配置cid)
  45. private wecorp: WxworkCorp | null = null;
  46. private readonly COMPANY_ID = 'cDL6R1hgSi'; // 映三色帐套
  47. constructor(
  48. private groupChatService: GroupChatService,
  49. private projectService: ProjectService
  50. ) {
  51. // 初始化企微Corp
  52. this.wecorp = new WxworkCorp(this.COMPANY_ID);
  53. }
  54. ngOnInit(): void {
  55. this.loadGroupChats();
  56. this.loadProjects();
  57. }
  58. async loadGroupChats(): Promise<void> {
  59. this.loading.set(true);
  60. try {
  61. const groups = await this.groupChatService.findGroupChats();
  62. const groupList: GroupChat[] = groups.map(g => {
  63. const json = this.groupChatService.toJSON(g);
  64. return {
  65. id: json.objectId,
  66. chat_id: json.chat_id || '',
  67. name: json.name || '未命名群组',
  68. project: json.projectTitle,
  69. projectId: json.projectId,
  70. memberCount: json.member_list?.length || 0,
  71. isDisabled: json.isDisabled || false,
  72. createdAt: json.createdAt
  73. };
  74. });
  75. this.groupChats.set(groupList);
  76. this.total.set(groupList.length);
  77. } catch (error) {
  78. console.error('加载群组列表失败:', error);
  79. } finally {
  80. this.loading.set(false);
  81. }
  82. }
  83. async loadProjects(): Promise<void> {
  84. try {
  85. const projs = await this.projectService.findProjects({ limit: 200 });
  86. this.projects.set(
  87. projs.map(p => {
  88. const json = this.projectService.toJSON(p);
  89. return {
  90. id: json.objectId,
  91. title: json.title
  92. };
  93. })
  94. );
  95. } catch (error) {
  96. console.error('加载项目列表失败:', error);
  97. }
  98. }
  99. get filtered() {
  100. const kw = this.keyword().trim().toLowerCase();
  101. if (!kw) return this.groupChats();
  102. return this.groupChats().filter(g => g.name.toLowerCase().includes(kw));
  103. }
  104. resetFilters() {
  105. this.keyword.set('');
  106. }
  107. async viewGroupChat(group: GroupChat) {
  108. this.currentGroupChat = group;
  109. this.panelMode = 'detail';
  110. this.showPanel = true;
  111. // 加载群组详情
  112. await this.loadGroupChatDetail(group);
  113. }
  114. /**
  115. * 加载群组详情(从企微获取)
  116. */
  117. async loadGroupChatDetail(group: GroupChat) {
  118. if (!this.wecorp || !group.chat_id) {
  119. console.error('企微Corp未初始化或群组chat_id为空');
  120. return;
  121. }
  122. this.loadingDetail.set(true);
  123. try {
  124. // 从企微获取群组详情
  125. const chatInfo: any = await this.wecorp.externalContact.groupChat.get(
  126. group.chat_id
  127. );
  128. console.log('群组详情:', chatInfo);
  129. if (chatInfo && chatInfo.group_chat) {
  130. this.groupChatDetail = chatInfo.group_chat;
  131. this.groupMembers = chatInfo.group_chat.member_list || [];
  132. // 获取入群二维码(如果有)
  133. // 注意:入群二维码需要单独调用API获取
  134. // 这里简化处理,实际可能需要根据API文档调整
  135. if (chatInfo.group_chat.qr_code) {
  136. this.qrCodeUrl = chatInfo.group_chat.qr_code;
  137. } else {
  138. this.qrCodeUrl = '';
  139. }
  140. }
  141. } catch (error) {
  142. console.error('加载群组详情失败:', error);
  143. alert('加载群组详情失败,可能是企微API调用失败');
  144. } finally {
  145. this.loadingDetail.set(false);
  146. }
  147. }
  148. editGroupChat(group: GroupChat) {
  149. this.currentGroupChat = group;
  150. this.formModel = { ...group };
  151. this.panelMode = 'edit';
  152. this.showPanel = true;
  153. }
  154. closePanel() {
  155. this.showPanel = false;
  156. this.currentGroupChat = null;
  157. this.formModel = {};
  158. }
  159. async updateGroupChat() {
  160. if (!this.currentGroupChat) return;
  161. try {
  162. await this.groupChatService.updateGroupChat(this.currentGroupChat.id, {
  163. projectId: this.formModel.projectId || null,
  164. isDisabled: this.formModel.isDisabled
  165. });
  166. await this.loadGroupChats();
  167. this.closePanel();
  168. } catch (error) {
  169. console.error('更新群组失败:', error);
  170. alert('更新群组失败,请重试');
  171. }
  172. }
  173. async toggleGroupChat(group: GroupChat) {
  174. const action = group.isDisabled ? '启用' : '禁用';
  175. if (!confirm(`确定要${action}群组 "${group.name}" 吗?`)) {
  176. return;
  177. }
  178. try {
  179. await this.groupChatService.toggleGroupChat(group.id, !group.isDisabled);
  180. await this.loadGroupChats();
  181. } catch (error) {
  182. console.error(`${action}群组失败:`, error);
  183. alert(`${action}群组失败,请重试`);
  184. }
  185. }
  186. exportGroupChats() {
  187. const header = ['群名称', '企微群ID', '关联项目', '成员数', '状态', '创建时间'];
  188. const rows = this.filtered.map(g => [
  189. g.name,
  190. g.chat_id,
  191. g.project || '未关联',
  192. String(g.memberCount),
  193. g.isDisabled ? '已禁用' : '正常',
  194. g.createdAt instanceof Date
  195. ? g.createdAt.toISOString().slice(0, 10)
  196. : String(g.createdAt || '')
  197. ]);
  198. this.downloadCSV('群组列表.csv', [header, ...rows]);
  199. }
  200. private downloadCSV(filename: string, rows: (string | number)[][]) {
  201. const escape = (val: string | number) => {
  202. const s = String(val ?? '');
  203. if (/[",\n]/.test(s)) return '"' + s.replace(/"/g, '""') + '"';
  204. return s;
  205. };
  206. const csv = rows.map(r => r.map(escape).join(',')).join('\n');
  207. const blob = new Blob(['\ufeff', csv], {
  208. type: 'text/csv;charset=utf-8;'
  209. });
  210. const url = URL.createObjectURL(blob);
  211. const a = document.createElement('a');
  212. a.href = url;
  213. a.download = filename;
  214. a.click();
  215. URL.revokeObjectURL(url);
  216. }
  217. }