|
@@ -5,6 +5,17 @@ import { FormsModule } from '@angular/forms';
|
|
|
import { IonicModule } from '@ionic/angular';
|
|
|
import { WxworkSDK, WxworkCorp, WxworkCurrentChat } from 'fmode-ng/core';
|
|
|
import { FmodeParse, FmodeObject } from 'fmode-ng/parse';
|
|
|
+import { wxdebug } from 'fmode-ng';
|
|
|
+import { addIcons } from 'ionicons';
|
|
|
+import {
|
|
|
+ rocketOutline,
|
|
|
+ addCircleOutline,
|
|
|
+ timeOutline,
|
|
|
+ personCircleOutline,
|
|
|
+ alertCircleOutline,
|
|
|
+ refreshOutline,
|
|
|
+ chevronForwardOutline
|
|
|
+} from 'ionicons/icons';
|
|
|
|
|
|
const Parse = FmodeParse.with('nova');
|
|
|
|
|
@@ -19,6 +30,8 @@ const Parse = FmodeParse.with('nova');
|
|
|
* - 联系人 → 客户画像
|
|
|
*
|
|
|
* 路由:/wxwork/:cid/project-loader
|
|
|
+ *
|
|
|
+ * 参考实现:nova-admin/projects/nova-crm/src/modules/chat/page-chat-context
|
|
|
*/
|
|
|
@Component({
|
|
|
selector: 'app-project-loader',
|
|
@@ -42,8 +55,9 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
wecorp: WxworkCorp | null = null;
|
|
|
|
|
|
// 上下文数据
|
|
|
- currentUser: FmodeObject | null = null; // Profile
|
|
|
+ currentUser: FmodeObject | null = null; // Profile 或 UserSocial
|
|
|
currentChat: WxworkCurrentChat | null = null;
|
|
|
+ chatType: 'group' | 'contact' | 'none' = 'none';
|
|
|
groupChat: FmodeObject | null = null; // GroupChat
|
|
|
contact: FmodeObject | null = null; // ContactInfo
|
|
|
project: FmodeObject | null = null; // Project
|
|
@@ -60,56 +74,126 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
constructor(
|
|
|
private router: Router,
|
|
|
private route: ActivatedRoute
|
|
|
- ) {}
|
|
|
+ ) {
|
|
|
+ addIcons({
|
|
|
+ rocketOutline,
|
|
|
+ addCircleOutline,
|
|
|
+ timeOutline,
|
|
|
+ personCircleOutline,
|
|
|
+ alertCircleOutline,
|
|
|
+ refreshOutline,
|
|
|
+ chevronForwardOutline
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
async ngOnInit() {
|
|
|
// 获取路由参数
|
|
|
- this.cid = this.route.snapshot.paramMap.get('cid') || '';
|
|
|
- this.appId = this.route.snapshot.queryParamMap.get('appId') || 'crm';
|
|
|
-
|
|
|
- if (!this.cid) {
|
|
|
- this.error = '缺少企业ID参数';
|
|
|
- this.loading = false;
|
|
|
- return;
|
|
|
- }
|
|
|
+ this.route.paramMap.subscribe(async params => {
|
|
|
+ this.cid = params.get('cid') || '';
|
|
|
+ this.appId = params.get('appId') || 'crm';
|
|
|
+
|
|
|
+ if (!this.cid) {
|
|
|
+ this.error = '缺少企业ID参数';
|
|
|
+ this.loading = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- await this.loadData();
|
|
|
+ await this.loadData();
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 加载数据主流程
|
|
|
+ * 加载数据主流程(参考 page-chat-context 实现)
|
|
|
*/
|
|
|
async loadData() {
|
|
|
try {
|
|
|
this.loading = true;
|
|
|
this.loadingMessage = '初始化企微SDK...';
|
|
|
|
|
|
- // 1. 初始化SDK
|
|
|
+ // 1️⃣ 初始化 SDK
|
|
|
this.wxwork = new WxworkSDK({ cid: this.cid, appId: this.appId });
|
|
|
this.wecorp = new WxworkCorp(this.cid);
|
|
|
|
|
|
- // 2. 获取企微上下文(群聊或联系人)
|
|
|
- this.loadingMessage = '获取会话信息...';
|
|
|
- const chatObject = await this.wxwork.getCurrentChatObject();
|
|
|
- this.currentChat = chatObject.currentChat;
|
|
|
- this.groupChat = chatObject.GroupChat || null;
|
|
|
- this.contact = chatObject.Contact || null;
|
|
|
+ wxdebug('1. SDK初始化完成', { cid: this.cid, appId: this.appId });
|
|
|
|
|
|
- // 3. 获取当前登录用户
|
|
|
+ // 2️⃣ 加载当前登录员工信息(由 WxworkAuthGuard 自动登录)
|
|
|
this.loadingMessage = '获取用户信息...';
|
|
|
- this.currentUser = await this.wxwork.getCurrentUser();
|
|
|
+ try {
|
|
|
+ this.currentUser = await this.wxwork.getCurrentUser();
|
|
|
+ wxdebug('2. 获取当前用户成功', this.currentUser?.toJSON());
|
|
|
+ } catch (err) {
|
|
|
+ console.error('获取当前用户失败:', err);
|
|
|
+ wxdebug('2. 获取当前用户失败', err);
|
|
|
+ throw new Error('获取用户信息失败,请重试');
|
|
|
+ }
|
|
|
|
|
|
- console.log('当前用户:', this.currentUser?.get('name'), '角色:', this.currentUser?.get('role'));
|
|
|
- console.log('会话类型:', this.currentChat?.type);
|
|
|
+ // 3️⃣ 加载当前聊天上下文
|
|
|
+ this.loadingMessage = '获取会话信息...';
|
|
|
+ try {
|
|
|
+ this.currentChat = await this.wxwork.getCurrentChat();
|
|
|
+ wxdebug('3. getCurrentChat返回', this.currentChat);
|
|
|
+ } catch (err) {
|
|
|
+ console.error('getCurrentChat失败:', err);
|
|
|
+ wxdebug('3. getCurrentChat失败', err);
|
|
|
+ }
|
|
|
|
|
|
- // 4. 根据场景处理
|
|
|
- if (this.groupChat) {
|
|
|
- await this.handleGroupChatScene();
|
|
|
- } else if (this.contact) {
|
|
|
- await this.handleContactScene();
|
|
|
+ // 4️⃣ 根据场景同步数据
|
|
|
+ if (this.currentChat?.type === "chatId" && this.currentChat?.group) {
|
|
|
+ // 群聊场景
|
|
|
+ wxdebug('4. 检测到群聊场景', this.currentChat.group);
|
|
|
+ this.loadingMessage = '同步群聊信息...';
|
|
|
+ try {
|
|
|
+ this.chatType = 'group';
|
|
|
+ this.groupChat = await this.wxwork.syncGroupChat(this.currentChat.group);
|
|
|
+ wxdebug('5. 群聊同步完成', this.groupChat?.toJSON());
|
|
|
+
|
|
|
+ // 处理群聊场景
|
|
|
+ await this.handleGroupChatScene();
|
|
|
+ } catch (err) {
|
|
|
+ console.error('群聊同步失败:', err);
|
|
|
+ wxdebug('5. 群聊同步失败', err);
|
|
|
+ throw new Error('群聊信息同步失败');
|
|
|
+ }
|
|
|
+ } else if (this.currentChat?.type === "userId" && this.currentChat?.id) {
|
|
|
+ // 联系人场景
|
|
|
+ wxdebug('4. 检测到联系人场景', { id: this.currentChat.id });
|
|
|
+ this.loadingMessage = '同步联系人信息...';
|
|
|
+ try {
|
|
|
+ this.chatType = 'contact';
|
|
|
+
|
|
|
+ // 获取完整联系人信息
|
|
|
+ const contactInfo = await this.wecorp!.externalContact.get(this.currentChat.id);
|
|
|
+ wxdebug('5. 获取完整联系人信息', contactInfo);
|
|
|
+
|
|
|
+ this.contact = await this.wxwork.syncContact(contactInfo);
|
|
|
+ wxdebug('6. 联系人同步完成', this.contact?.toJSON());
|
|
|
+
|
|
|
+ // 处理联系人场景
|
|
|
+ await this.handleContactScene();
|
|
|
+ } catch (err) {
|
|
|
+ console.error('联系人同步失败:', err);
|
|
|
+ wxdebug('联系人同步失败', err);
|
|
|
+ throw new Error('联系人信息同步失败');
|
|
|
+ }
|
|
|
} else {
|
|
|
- this.error = '无法识别当前会话类型';
|
|
|
+ // 未检测到有效场景
|
|
|
+ wxdebug('4. 未检测到有效场景', {
|
|
|
+ currentChat: this.currentChat,
|
|
|
+ type: this.currentChat?.type,
|
|
|
+ hasGroup: !!this.currentChat?.group,
|
|
|
+ hasContact: !!this.currentChat?.contact,
|
|
|
+ hasId: !!this.currentChat?.id
|
|
|
+ });
|
|
|
+ throw new Error('无法识别当前会话类型,请在群聊或联系人会话中打开');
|
|
|
}
|
|
|
+
|
|
|
+ wxdebug('加载完成', {
|
|
|
+ chatType: this.chatType,
|
|
|
+ hasGroupChat: !!this.groupChat,
|
|
|
+ hasContact: !!this.contact,
|
|
|
+ hasCurrentUser: !!this.currentUser
|
|
|
+ });
|
|
|
+
|
|
|
} catch (err: any) {
|
|
|
console.error('加载失败:', err);
|
|
|
this.error = err.message || '加载失败,请重试';
|
|
@@ -134,12 +218,13 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
query.include('customer', 'assignee');
|
|
|
this.project = await query.get(projectPointer.id);
|
|
|
|
|
|
- console.log('找到项目:', this.project.get('title'));
|
|
|
+ wxdebug('找到项目', this.project.toJSON());
|
|
|
|
|
|
// 跳转项目详情
|
|
|
await this.navigateToProjectDetail();
|
|
|
} catch (err) {
|
|
|
console.error('加载项目失败:', err);
|
|
|
+ wxdebug('加载项目失败', err);
|
|
|
this.error = '项目已删除或无权访问';
|
|
|
}
|
|
|
} else {
|
|
@@ -153,9 +238,13 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
* 处理联系人场景
|
|
|
*/
|
|
|
async handleContactScene() {
|
|
|
- console.log('联系人场景,跳转客户画像');
|
|
|
+ wxdebug('联系人场景,跳转客户画像', {
|
|
|
+ contactId: this.contact!.id,
|
|
|
+ contactName: this.contact!.get('name')
|
|
|
+ });
|
|
|
+
|
|
|
// 跳转客户画像页面
|
|
|
- await this.router.navigate(['/wxwork', this.cid, 'customer', this.contact!.id], {
|
|
|
+ await this.router.navigate(['/wxwork', this.cid, 'customer-profile', this.contact!.id], {
|
|
|
queryParams: {
|
|
|
profileId: this.currentUser!.id
|
|
|
}
|
|
@@ -171,15 +260,17 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
const pgQuery = new Parse.Query('ProjectGroup');
|
|
|
pgQuery.equalTo('groupChat', this.groupChat!.toPointer());
|
|
|
pgQuery.include('project');
|
|
|
+ pgQuery.descending('createdAt');
|
|
|
const projectGroups = await pgQuery.find();
|
|
|
|
|
|
this.historyProjects = projectGroups
|
|
|
.map(pg => pg.get('project'))
|
|
|
.filter(p => p && !p.get('isDeleted'));
|
|
|
|
|
|
- console.log('找到历史项目:', this.historyProjects.length, '个');
|
|
|
+ wxdebug('找到历史项目', { count: this.historyProjects.length });
|
|
|
} catch (err) {
|
|
|
console.error('加载历史项目失败:', err);
|
|
|
+ wxdebug('加载历史项目失败', err);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -190,6 +281,10 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
this.showCreateGuide = true;
|
|
|
this.defaultProjectName = this.groupChat!.get('name') || '新项目';
|
|
|
this.projectName = this.defaultProjectName;
|
|
|
+ wxdebug('显示创建项目引导', {
|
|
|
+ groupName: this.groupChat!.get('name'),
|
|
|
+ historyProjectsCount: this.historyProjects.length
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -210,6 +305,12 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
|
|
|
try {
|
|
|
this.creating = true;
|
|
|
+ wxdebug('开始创建项目', {
|
|
|
+ projectName: this.projectName,
|
|
|
+ groupChatId: this.groupChat!.id,
|
|
|
+ currentUserId: this.currentUser!.id,
|
|
|
+ role: role
|
|
|
+ });
|
|
|
|
|
|
// 1. 创建项目
|
|
|
const Project = Parse.Object.extend('Project');
|
|
@@ -221,16 +322,17 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
project.set('currentStage', '订单分配');
|
|
|
project.set('data', {
|
|
|
createdBy: this.currentUser!.id,
|
|
|
- createdFrom: 'wxwork_groupchat'
|
|
|
+ createdFrom: 'wxwork_groupchat',
|
|
|
+ groupChatId: this.groupChat!.id
|
|
|
});
|
|
|
|
|
|
await project.save();
|
|
|
-
|
|
|
- console.log('项目创建成功:', project.id);
|
|
|
+ wxdebug('项目创建成功', { projectId: project.id });
|
|
|
|
|
|
// 2. 关联群聊
|
|
|
this.groupChat!.set('project', project.toPointer());
|
|
|
await this.groupChat!.save();
|
|
|
+ wxdebug('群聊关联项目成功');
|
|
|
|
|
|
// 3. 创建 ProjectGroup 关联(支持多项目多群)
|
|
|
const ProjectGroup = Parse.Object.extend('ProjectGroup');
|
|
@@ -238,13 +340,16 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
pg.set('project', project.toPointer());
|
|
|
pg.set('groupChat', this.groupChat!.toPointer());
|
|
|
pg.set('isPrimary', true);
|
|
|
+ pg.set('company', this.currentUser!.get('company'));
|
|
|
await pg.save();
|
|
|
+ wxdebug('ProjectGroup关联创建成功');
|
|
|
|
|
|
// 4. 跳转项目详情
|
|
|
this.project = project;
|
|
|
await this.navigateToProjectDetail();
|
|
|
} catch (err: any) {
|
|
|
console.error('创建项目失败:', err);
|
|
|
+ wxdebug('创建项目失败', err);
|
|
|
alert('创建失败: ' + (err.message || '未知错误'));
|
|
|
} finally {
|
|
|
this.creating = false;
|
|
@@ -255,19 +360,35 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
* 选择历史项目
|
|
|
*/
|
|
|
async selectHistoryProject(project: FmodeObject) {
|
|
|
- // 更新群聊的当前项目
|
|
|
- this.groupChat!.set('project', project.toPointer());
|
|
|
- await this.groupChat!.save();
|
|
|
+ try {
|
|
|
+ wxdebug('选择历史项目', {
|
|
|
+ projectId: project.id,
|
|
|
+ projectTitle: project.get('title')
|
|
|
+ });
|
|
|
|
|
|
- // 跳转项目详情
|
|
|
- this.project = project;
|
|
|
- await this.navigateToProjectDetail();
|
|
|
+ // 更新群聊的当前项目
|
|
|
+ this.groupChat!.set('project', project.toPointer());
|
|
|
+ await this.groupChat!.save();
|
|
|
+
|
|
|
+ // 跳转项目详情
|
|
|
+ this.project = project;
|
|
|
+ await this.navigateToProjectDetail();
|
|
|
+ } catch (err: any) {
|
|
|
+ console.error('关联项目失败:', err);
|
|
|
+ alert('关联失败: ' + (err.message || '未知错误'));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 跳转项目详情
|
|
|
*/
|
|
|
async navigateToProjectDetail() {
|
|
|
+ wxdebug('跳转项目详情', {
|
|
|
+ projectId: this.project!.id,
|
|
|
+ cid: this.cid,
|
|
|
+ groupChatId: this.groupChat?.id
|
|
|
+ });
|
|
|
+
|
|
|
await this.router.navigate(['/wxwork', this.cid, 'project', this.project!.id], {
|
|
|
queryParams: {
|
|
|
groupId: this.groupChat?.id,
|
|
@@ -282,9 +403,27 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
async reload() {
|
|
|
this.error = null;
|
|
|
this.showCreateGuide = false;
|
|
|
+ this.historyProjects = [];
|
|
|
+ this.chatType = 'none';
|
|
|
await this.loadData();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取当前员工姓名
|
|
|
+ */
|
|
|
+ getCurrentUserName(): string {
|
|
|
+ if (!this.currentUser) return '未知';
|
|
|
+ return this.currentUser.get('name') || this.currentUser.get('userid') || '未知';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前员工角色
|
|
|
+ */
|
|
|
+ getCurrentUserRole(): string {
|
|
|
+ if (!this.currentUser) return '未知';
|
|
|
+ return this.currentUser.get('role') || '未知';
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 获取项目状态的显示样式类
|
|
|
*/
|
|
@@ -305,6 +444,6 @@ export class ProjectLoaderComponent implements OnInit {
|
|
|
formatDate(date: Date): string {
|
|
|
if (!date) return '';
|
|
|
const d = new Date(date);
|
|
|
- return `${d.getMonth() + 1}/${d.getDate()}`;
|
|
|
+ return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`;
|
|
|
}
|
|
|
}
|