project-loader.component.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. import { Component, OnInit } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { Router, ActivatedRoute } from '@angular/router';
  4. import { FormsModule } from '@angular/forms';
  5. import { IonicModule } from '@ionic/angular';
  6. import { WxworkSDK, WxworkCorp, WxworkCurrentChat } from 'fmode-ng/core';
  7. import { FmodeParse, FmodeObject } from 'fmode-ng/parse';
  8. const Parse = FmodeParse.with('nova');
  9. /**
  10. * 项目预加载页面
  11. *
  12. * 功能:
  13. * 1. 从企微会话获取上下文(群聊或联系人)
  14. * 2. 获取当前登录用户(Profile)
  15. * 3. 根据场景跳转到对应页面
  16. * - 群聊 → 项目详情 或 创建项目引导
  17. * - 联系人 → 客户画像
  18. *
  19. * 路由:/wxwork/:cid/project-loader
  20. */
  21. @Component({
  22. selector: 'app-project-loader',
  23. standalone: true,
  24. imports: [CommonModule, FormsModule, IonicModule],
  25. templateUrl: './project-loader.component.html',
  26. styleUrls: ['./project-loader.component.scss']
  27. })
  28. export class ProjectLoaderComponent implements OnInit {
  29. // 基础数据
  30. cid: string = '';
  31. appId: string = 'crm';
  32. // 加载状态
  33. loading: boolean = true;
  34. loadingMessage: string = '正在加载...';
  35. error: string | null = null;
  36. // 企微SDK
  37. wxwork: WxworkSDK | null = null;
  38. wecorp: WxworkCorp | null = null;
  39. // 上下文数据
  40. currentUser: FmodeObject | null = null; // Profile
  41. currentChat: WxworkCurrentChat | null = null;
  42. groupChat: FmodeObject | null = null; // GroupChat
  43. contact: FmodeObject | null = null; // ContactInfo
  44. project: FmodeObject | null = null; // Project
  45. // 创建项目引导
  46. showCreateGuide: boolean = false;
  47. defaultProjectName: string = '';
  48. projectName: string = '';
  49. creating: boolean = false;
  50. // 历史项目(当前群聊无项目时展示)
  51. historyProjects: FmodeObject[] = [];
  52. constructor(
  53. private router: Router,
  54. private route: ActivatedRoute
  55. ) {}
  56. async ngOnInit() {
  57. // 获取路由参数
  58. this.cid = this.route.snapshot.paramMap.get('cid') || '';
  59. this.appId = this.route.snapshot.queryParamMap.get('appId') || 'crm';
  60. if (!this.cid) {
  61. this.error = '缺少企业ID参数';
  62. this.loading = false;
  63. return;
  64. }
  65. await this.loadData();
  66. }
  67. /**
  68. * 加载数据主流程
  69. */
  70. async loadData() {
  71. try {
  72. this.loading = true;
  73. this.loadingMessage = '初始化企微SDK...';
  74. // 1. 初始化SDK
  75. this.wxwork = new WxworkSDK({ cid: this.cid, appId: this.appId });
  76. this.wecorp = new WxworkCorp(this.cid);
  77. // 2. 获取企微上下文(群聊或联系人)
  78. this.loadingMessage = '获取会话信息...';
  79. const chatObject = await this.wxwork.getCurrentChatObject();
  80. this.currentChat = chatObject.currentChat;
  81. this.groupChat = chatObject.GroupChat || null;
  82. this.contact = chatObject.Contact || null;
  83. // 3. 获取当前登录用户
  84. this.loadingMessage = '获取用户信息...';
  85. this.currentUser = await this.wxwork.getCurrentUser();
  86. console.log('当前用户:', this.currentUser?.get('name'), '角色:', this.currentUser?.get('role'));
  87. console.log('会话类型:', this.currentChat?.type);
  88. // 4. 根据场景处理
  89. if (this.groupChat) {
  90. await this.handleGroupChatScene();
  91. } else if (this.contact) {
  92. await this.handleContactScene();
  93. } else {
  94. this.error = '无法识别当前会话类型';
  95. }
  96. } catch (err: any) {
  97. console.error('加载失败:', err);
  98. this.error = err.message || '加载失败,请重试';
  99. } finally {
  100. this.loading = false;
  101. }
  102. }
  103. /**
  104. * 处理群聊场景
  105. */
  106. async handleGroupChatScene() {
  107. this.loadingMessage = '查询项目信息...';
  108. // 查询群聊关联的项目
  109. const projectPointer = this.groupChat!.get('project');
  110. if (projectPointer) {
  111. // 有项目,加载项目详情
  112. try {
  113. const query = new Parse.Query('Project');
  114. query.include('customer', 'assignee');
  115. this.project = await query.get(projectPointer.id);
  116. console.log('找到项目:', this.project.get('title'));
  117. // 跳转项目详情
  118. await this.navigateToProjectDetail();
  119. } catch (err) {
  120. console.error('加载项目失败:', err);
  121. this.error = '项目已删除或无权访问';
  122. }
  123. } else {
  124. // 无项目,查询历史项目并显示创建引导
  125. await this.loadHistoryProjects();
  126. this.showCreateProjectGuide();
  127. }
  128. }
  129. /**
  130. * 处理联系人场景
  131. */
  132. async handleContactScene() {
  133. console.log('联系人场景,跳转客户画像');
  134. // 跳转客户画像页面
  135. await this.router.navigate(['/wxwork', this.cid, 'customer', this.contact!.id], {
  136. queryParams: {
  137. profileId: this.currentUser!.id
  138. }
  139. });
  140. }
  141. /**
  142. * 加载历史项目(当前群聊相关的其他项目)
  143. */
  144. async loadHistoryProjects() {
  145. try {
  146. // 通过 ProjectGroup 查询该群聊的所有项目
  147. const pgQuery = new Parse.Query('ProjectGroup');
  148. pgQuery.equalTo('groupChat', this.groupChat!.toPointer());
  149. pgQuery.include('project');
  150. const projectGroups = await pgQuery.find();
  151. this.historyProjects = projectGroups
  152. .map(pg => pg.get('project'))
  153. .filter(p => p && !p.get('isDeleted'));
  154. console.log('找到历史项目:', this.historyProjects.length, '个');
  155. } catch (err) {
  156. console.error('加载历史项目失败:', err);
  157. }
  158. }
  159. /**
  160. * 显示创建项目引导
  161. */
  162. showCreateProjectGuide() {
  163. this.showCreateGuide = true;
  164. this.defaultProjectName = this.groupChat!.get('name') || '新项目';
  165. this.projectName = this.defaultProjectName;
  166. }
  167. /**
  168. * 创建项目
  169. */
  170. async createProject() {
  171. if (!this.projectName.trim()) {
  172. alert('请输入项目名称');
  173. return;
  174. }
  175. // 权限检查
  176. const role = this.currentUser!.get('role');
  177. if (!['客服', '组长', '管理员'].includes(role)) {
  178. alert('您没有权限创建项目');
  179. return;
  180. }
  181. try {
  182. this.creating = true;
  183. // 1. 创建项目
  184. const Project = Parse.Object.extend('Project');
  185. const project = new Project();
  186. project.set('title', this.projectName.trim());
  187. project.set('company', this.currentUser!.get('company'));
  188. project.set('status', '待分配');
  189. project.set('currentStage', '订单分配');
  190. project.set('data', {
  191. createdBy: this.currentUser!.id,
  192. createdFrom: 'wxwork_groupchat'
  193. });
  194. await project.save();
  195. console.log('项目创建成功:', project.id);
  196. // 2. 关联群聊
  197. this.groupChat!.set('project', project.toPointer());
  198. await this.groupChat!.save();
  199. // 3. 创建 ProjectGroup 关联(支持多项目多群)
  200. const ProjectGroup = Parse.Object.extend('ProjectGroup');
  201. const pg = new ProjectGroup();
  202. pg.set('project', project.toPointer());
  203. pg.set('groupChat', this.groupChat!.toPointer());
  204. pg.set('isPrimary', true);
  205. await pg.save();
  206. // 4. 跳转项目详情
  207. this.project = project;
  208. await this.navigateToProjectDetail();
  209. } catch (err: any) {
  210. console.error('创建项目失败:', err);
  211. alert('创建失败: ' + (err.message || '未知错误'));
  212. } finally {
  213. this.creating = false;
  214. }
  215. }
  216. /**
  217. * 选择历史项目
  218. */
  219. async selectHistoryProject(project: FmodeObject) {
  220. // 更新群聊的当前项目
  221. this.groupChat!.set('project', project.toPointer());
  222. await this.groupChat!.save();
  223. // 跳转项目详情
  224. this.project = project;
  225. await this.navigateToProjectDetail();
  226. }
  227. /**
  228. * 跳转项目详情
  229. */
  230. async navigateToProjectDetail() {
  231. await this.router.navigate(['/wxwork', this.cid, 'project', this.project!.id], {
  232. queryParams: {
  233. groupId: this.groupChat?.id,
  234. profileId: this.currentUser!.id
  235. }
  236. });
  237. }
  238. /**
  239. * 重新加载
  240. */
  241. async reload() {
  242. this.error = null;
  243. this.showCreateGuide = false;
  244. await this.loadData();
  245. }
  246. /**
  247. * 获取项目状态的显示样式类
  248. */
  249. getProjectStatusClass(status: string): string {
  250. const classMap: any = {
  251. '待分配': 'status-pending',
  252. '进行中': 'status-active',
  253. '已完成': 'status-completed',
  254. '已暂停': 'status-paused',
  255. '已取消': 'status-cancelled'
  256. };
  257. return classMap[status] || 'status-default';
  258. }
  259. /**
  260. * 格式化日期
  261. */
  262. formatDate(date: Date): string {
  263. if (!date) return '';
  264. const d = new Date(date);
  265. return `${d.getMonth() + 1}/${d.getDate()}`;
  266. }
  267. }