import { Component, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Router, ActivatedRoute } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { WxworkSDK, WxworkCorp } from 'fmode-ng/core'; import { FmodeParse, FmodeObject } from 'fmode-ng/parse'; import { ActivityLogService } from '../../../../app/services/activity-log.service'; // WxworkCurrentChat 类型定义 interface WxworkCurrentChat { type?: string; chatId?: string; group?: any; contact?: any; id?: string; [key: string]: any; } // 个人技能评分 interface SkillRating { name: string; currentScore: number; targetScore: number; category: '设计能力' | '沟通能力' | '技术能力' | '项目管理'; } // 案例作品 interface CaseWork { id: string; projectId: string; projectTitle: string; coverImage: string; description: string; tags: string[]; completionDate: Date; customerName: string; status: string; totalPrice?: number; roomType?: string; } // 月度统计 interface MonthlyStats { month: string; totalProjects: number; completedProjects: number; revenue: number; avgScore: number; } // 自我评价 interface SelfEvaluation { strengths: string[]; // 优势 improvements: string[]; // 待提升 personalStatement: string; // 个人陈述 lastUpdated: Date; } function wxdebug(...params:any[]){ console.log(params) } const Parse = FmodeParse.with('nova'); /** * 个人看板页面(重构自项目预加载页面) * * 功能: * 1. 展示个人信息和自我评价 * 2. 技能评分和发展目标 * 3. 案例作品集(从完成项目选择) * 4. 月度接单量统计 * 5. 支持编辑个人资料和案例 * * 路由:/wxwork/:cid/project-loader */ @Component({ selector: 'app-project-loader', standalone: true, imports: [CommonModule, FormsModule], templateUrl: './project-loader.component.html', styleUrls: ['./project-loader.component.scss'] }) export class ProjectLoaderComponent implements OnInit { // 基础数据 cid: string = ''; appId: string = 'crm'; // 加载状态 loading: boolean = true; loadingMessage: string = '正在加载...'; error: string | null = null; // 企微SDK wxwork: WxworkSDK | null = null; wecorp: WxworkCorp | null = null; // 上下文数据 currentUser: FmodeObject | null = null; // Profile currentChat: WxworkCurrentChat | null = null; chatType: 'group' | 'contact' | 'personal' = 'personal'; groupChat: FmodeObject | null = null; // GroupChat contact: FmodeObject | null = null; // ContactInfo project: FmodeObject | null = null; // Project // 个人看板数据 skillRatings: SkillRating[] = []; caseWorks: CaseWork[] = []; monthlyStats: MonthlyStats[] = []; selfEvaluation: SelfEvaluation = { strengths: [], improvements: [], personalStatement: '', lastUpdated: new Date() }; // UI状态 activeTab: 'overview' | 'cases' | 'stats' | 'skills' = 'overview'; showEditEvaluation: boolean = false; showCaseSelector: boolean = false; showSkillEditor: boolean = false; // 编辑状态 editingEvaluation: SelfEvaluation | null = null; availableProjects: FmodeObject[] = []; selectedProjectIds: string[] = []; // 统计数据 totalProjects: number = 0; completedProjects: number = 0; currentMonthProjects: number = 0; avgCustomerRating: number = 0; // 创建项目引导(保留原有功能) showCreateGuide: boolean = false; defaultProjectName: string = ''; projectName: string = ''; creating: boolean = false; historyProjects: FmodeObject[] = []; constructor( private router: Router, private route: ActivatedRoute, private activityLogService: ActivityLogService ) {} async ngOnInit() { // 获取路由参数 this.route.paramMap.subscribe(async params => { this.cid = params.get('cid') || localStorage.getItem("company") || ''; this.appId = params.get('appId') || 'crm'; if (!this.cid) { this.error = '缺少企业ID参数'; this.loading = false; return; } await this.loadData(); }); } /** * 加载数据主流程 */ async loadData() { try { this.loading = true; this.loadingMessage = '初始化企微SDK...'; // 1️⃣ 初始化 SDK // @ts-ignore - fmode-ng type issue this.wxwork = new WxworkSDK({ cid: this.cid, appId: this.appId }); // @ts-ignore - fmode-ng type issue this.wecorp = new WxworkCorp(this.cid); wxdebug('1. SDK初始化完成', { cid: this.cid, appId: this.appId }); // 2️⃣ 加载当前登录员工信息 this.loadingMessage = '获取用户信息...'; try { this.currentUser = await this.wxwork.getCurrentUser(); wxdebug('2. 获取当前用户成功', this.currentUser?.toJSON()); } catch (err) { console.error('获取当前用户失败:', err); throw new Error('获取用户信息失败,请重试'); } // 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.currentChat?.type === "chatId" && this.currentChat?.group) { // 群聊场景 - 保留原有逻辑 this.chatType = 'group'; this.groupChat = await this.wxwork.syncGroupChat(this.currentChat.group); await this.handleGroupChatScene(); } else if (this.currentChat?.type === "userId" && this.currentChat?.id) { // 联系人场景 - 保留原有逻辑 this.chatType = 'contact'; const contactInfo = await this.wecorp!.externalContact.get(this.currentChat.id); this.contact = await this.wxwork.syncContact(contactInfo); await this.handleContactScene(); } else { // 个人看板场景(默认) this.chatType = 'personal'; await this.loadPersonalBoard(); } wxdebug('加载完成', { chatType: this.chatType, hasCurrentUser: !!this.currentUser }); } catch (err: any) { console.error('加载失败:', err); this.error = err.message || '加载失败,请重试'; } finally { this.loading = false; } } /** * 加载个人看板数据 */ async loadPersonalBoard() { if (!this.currentUser) { throw new Error('用户信息不存在'); } this.loadingMessage = '加载个人信息...'; try { // 并行加载所有数据 await Promise.all([ this.loadProfileData(), this.loadSkillRatings(), this.loadCaseWorks(), this.loadMonthlyStats(), this.loadSelfEvaluation() ]); console.log('✅ 个人看板数据加载完成'); } catch (err) { console.error('加载个人看板数据失败:', err); throw err; } } /** * 加载个人资料数据 */ async loadProfileData() { try { // 从Profile表获取最新数据 const query = new Parse.Query('Profile'); const profile = await query.get(this.currentUser!.id); this.currentUser = profile; const data = profile.get('data') || {}; // 计算统计数据 await this.calculateStatistics(); } catch (err) { console.error('加载个人资料失败:', err); } } /** * 加载技能评分 */ async loadSkillRatings() { try { const data = this.currentUser!.get('data') || {}; const skills = data.skillRatings || []; // 如果没有技能评分,创建默认值 if (skills.length === 0) { this.skillRatings = this.getDefaultSkillRatings(); } else { this.skillRatings = skills; } } catch (err) { console.error('加载技能评分失败:', err); this.skillRatings = this.getDefaultSkillRatings(); } } /** * 加载案例作品 */ async loadCaseWorks() { try { const data = this.currentUser!.get('data') || {}; const caseProjectIds = data.caseWorks || []; if (caseProjectIds.length === 0) { this.caseWorks = []; return; } // 查询案例对应的项目 const query = new Parse.Query('Project'); query.containedIn('objectId', caseProjectIds); query.equalTo('currentStage', '售后归档'); query.notEqualTo('isDeleted', true); query.include('contact'); query.descending('updatedAt'); query.limit(20); const projects = await query.find(); this.caseWorks = projects.map(p => this.transformProjectToCase(p)); console.log(`✅ 加载了 ${this.caseWorks.length} 个案例作品`); } catch (err) { console.error('加载案例作品失败:', err); this.caseWorks = []; } } /** * 加载月度统计 */ async loadMonthlyStats() { try { // 查询最近6个月的项目 const sixMonthsAgo = new Date(); sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6); const query = new Parse.Query('Project'); query.equalTo('assignee', this.currentUser!.toPointer()); query.greaterThanOrEqualTo('createdAt', sixMonthsAgo); query.notEqualTo('isDeleted', true); query.limit(1000); const projects = await query.find(); // 按月分组统计 const monthlyMap = new Map(); projects.forEach(p => { const date = p.get('createdAt'); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; if (!monthlyMap.has(monthKey)) { monthlyMap.set(monthKey, { month: monthKey, totalProjects: 0, completedProjects: 0, revenue: 0, avgScore: 0 }); } const stats = monthlyMap.get(monthKey)!; stats.totalProjects++; if (p.get('currentStage') === '售后归档' || p.get('status') === '已完成') { stats.completedProjects++; // 计算收入 const pricing = p.get('data')?.pricing || {}; const totalPrice = pricing.totalAmount || pricing.total || pricing.finalPrice || 0; stats.revenue += totalPrice; } }); // 转换为数组并排序 this.monthlyStats = Array.from(monthlyMap.values()) .sort((a, b) => b.month.localeCompare(a.month)) .slice(0, 6); console.log(`✅ 加载了 ${this.monthlyStats.length} 个月的统计数据`); } catch (err) { console.error('加载月度统计失败:', err); this.monthlyStats = []; } } /** * 加载自我评价 */ async loadSelfEvaluation() { try { const data = this.currentUser!.get('data') || {}; const evaluation = data.selfEvaluation; if (evaluation) { this.selfEvaluation = { strengths: evaluation.strengths || [], improvements: evaluation.improvements || [], personalStatement: evaluation.personalStatement || '', lastUpdated: evaluation.lastUpdated ? new Date(evaluation.lastUpdated) : new Date() }; } else { // 默认值 this.selfEvaluation = { strengths: ['专业扎实', '责任心强'], improvements: ['沟通效率', '时间管理'], personalStatement: '我是一名热爱设计的专业人士,致力于为客户提供优质的服务。', lastUpdated: new Date() }; } } catch (err) { console.error('加载自我评价失败:', err); } } /** * 计算统计数据 */ async calculateStatistics() { try { const profilePointer = this.currentUser!.toPointer(); // 查询总项目数 const totalQuery = new Parse.Query('Project'); totalQuery.equalTo('assignee', profilePointer); totalQuery.notEqualTo('isDeleted', true); this.totalProjects = await totalQuery.count(); // 查询已完成项目数 const completedQuery = new Parse.Query('Project'); completedQuery.equalTo('assignee', profilePointer); completedQuery.equalTo('currentStage', '售后归档'); completedQuery.notEqualTo('isDeleted', true); this.completedProjects = await completedQuery.count(); // 查询本月项目数 const currentMonth = new Date(); currentMonth.setDate(1); currentMonth.setHours(0, 0, 0, 0); const monthQuery = new Parse.Query('Project'); monthQuery.equalTo('assignee', profilePointer); monthQuery.greaterThanOrEqualTo('createdAt', currentMonth); monthQuery.notEqualTo('isDeleted', true); this.currentMonthProjects = await monthQuery.count(); console.log(`✅ 统计数据:总项目=${this.totalProjects}, 已完成=${this.completedProjects}, 本月=${this.currentMonthProjects}`); } catch (err) { console.error('计算统计数据失败:', err); } } /** * 将项目转换为案例 */ transformProjectToCase(project: FmodeObject): CaseWork { const data = project.get('data') || {}; const pricing = data.pricing || {}; const contact = project.get('contact'); // 获取封面图片 let coverImage = '/assets/images/default-project.jpg'; if (data.referenceImages && data.referenceImages.length > 0) { coverImage = data.referenceImages[0]; } else if (data.deliverables && data.deliverables.length > 0) { const firstDeliverable = data.deliverables[0]; if (firstDeliverable.files && firstDeliverable.files.length > 0) { coverImage = firstDeliverable.files[0]; } } return { id: project.id, projectId: project.id, projectTitle: project.get('title') || '未命名项目', coverImage: coverImage, description: data.description || project.get('title') || '', tags: data.tags || data.stylePreferences || [], completionDate: project.get('updatedAt') || new Date(), customerName: contact?.get('name') || '客户', status: project.get('status') || '已完成', totalPrice: pricing.totalAmount || pricing.total || pricing.finalPrice, roomType: data.roomType || data.spaceType }; } /** * 获取默认技能评分 */ getDefaultSkillRatings(): SkillRating[] { const role = this.currentUser?.get('roleName') || '组员'; if (role === '组员' || role === '设计师') { return [ { name: '空间设计', currentScore: 70, targetScore: 90, category: '设计能力' }, { name: '色彩搭配', currentScore: 65, targetScore: 85, category: '设计能力' }, { name: '软装搭配', currentScore: 75, targetScore: 90, category: '设计能力' }, { name: '客户沟通', currentScore: 60, targetScore: 80, category: '沟通能力' }, { name: '需求分析', currentScore: 65, targetScore: 85, category: '沟通能力' }, { name: '3D建模', currentScore: 70, targetScore: 85, category: '技术能力' }, { name: '效果图渲染', currentScore: 75, targetScore: 90, category: '技术能力' }, { name: '项目管理', currentScore: 60, targetScore: 80, category: '项目管理' } ]; } else if (role === '客服') { return [ { name: '客户接待', currentScore: 80, targetScore: 95, category: '沟通能力' }, { name: '需求挖掘', currentScore: 75, targetScore: 90, category: '沟通能力' }, { name: '订单管理', currentScore: 70, targetScore: 85, category: '项目管理' }, { name: '售后服务', currentScore: 75, targetScore: 90, category: '沟通能力' }, { name: '问题解决', currentScore: 65, targetScore: 85, category: '项目管理' } ]; } return []; } // ==================== 编辑功能 ==================== /** * 打开编辑自我评价 */ openEditEvaluation() { this.editingEvaluation = JSON.parse(JSON.stringify(this.selfEvaluation)); this.showEditEvaluation = true; } /** * 保存自我评价 */ async saveEvaluation() { if (!this.editingEvaluation) return; try { this.editingEvaluation.lastUpdated = new Date(); const data = this.currentUser!.get('data') || {}; data.selfEvaluation = this.editingEvaluation; this.currentUser!.set('data', data); await this.currentUser!.save(); this.selfEvaluation = this.editingEvaluation; this.showEditEvaluation = false; this.editingEvaluation = null; window?.fmode?.alert('保存成功!'); } catch (err: any) { console.error('保存自我评价失败:', err); window?.fmode?.alert('保存失败: ' + (err.message || '未知错误')); } } /** * 打开案例选择器 */ async openCaseSelector() { try { this.loadingMessage = '加载可选项目...'; this.loading = true; // 查询已完成的项目 const query = new Parse.Query('Project'); query.equalTo('assignee', this.currentUser!.toPointer()); query.equalTo('currentStage', '售后归档'); query.notEqualTo('isDeleted', true); query.include('contact'); query.descending('updatedAt'); query.limit(100); this.availableProjects = await query.find(); this.selectedProjectIds = this.caseWorks.map(c => c.projectId); this.showCaseSelector = true; console.log(`✅ 找到 ${this.availableProjects.length} 个可选项目`); } catch (err) { console.error('加载可选项目失败:', err); window?.fmode?.alert('加载失败,请重试'); } finally { this.loading = false; } } /** * 切换项目选择 */ toggleProjectSelection(projectId: string) { const index = this.selectedProjectIds.indexOf(projectId); if (index > -1) { this.selectedProjectIds.splice(index, 1); } else { if (this.selectedProjectIds.length >= 12) { window?.fmode?.alert('最多选择12个案例'); return; } this.selectedProjectIds.push(projectId); } } /** * 保存案例选择 */ async saveCaseSelection() { try { const data = this.currentUser!.get('data') || {}; data.caseWorks = this.selectedProjectIds; this.currentUser!.set('data', data); await this.currentUser!.save(); // 重新加载案例 await this.loadCaseWorks(); this.showCaseSelector = false; window?.fmode?.alert('保存成功!'); } catch (err: any) { console.error('保存案例选择失败:', err); window?.fmode?.alert('保存失败: ' + (err.message || '未知错误')); } } /** * 保存技能评分 */ async saveSkillRatings() { try { const data = this.currentUser!.get('data') || {}; data.skillRatings = this.skillRatings; this.currentUser!.set('data', data); await this.currentUser!.save(); this.showSkillEditor = false; window?.fmode?.alert('保存成功!'); } catch (err: any) { console.error('保存技能评分失败:', err); window?.fmode?.alert('保存失败: ' + (err.message || '未知错误')); } } // ==================== 原有群聊/联系人场景功能(保留) ==================== async handleGroupChatScene() { this.loadingMessage = '查询项目信息...'; const projectPointer = this.groupChat!.get('project'); if (projectPointer) { let pid = projectPointer.id || projectPointer.objectId try { const query = new Parse.Query('Project'); query.include('contact', 'assignee'); this.project = await query.get(pid); await this.navigateToProjectDetail(); } catch (err) { console.error('加载项目失败:', err); this.error = '项目已删除或无权访问'; } } else { await this.loadHistoryProjects(); this.showCreateProjectGuide(); } } async handleContactScene() { await this.router.navigate(['/wxwork', this.cid, 'contact', this.contact!.id], { queryParams: { profileId: this.currentUser!.id } }); } async loadHistoryProjects() { try { 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: any) => pg.get('project')) .filter((p: any) => p && !p.get('isDeleted')); } catch (err) { console.error('加载历史项目失败:', err); } } showCreateProjectGuide() { this.showCreateGuide = true; this.defaultProjectName = this.groupChat!.get('name') || '新项目'; this.projectName = this.defaultProjectName; } async createProject() { if (!this.projectName.trim()) { window?.fmode?.alert('请输入项目名称'); return; } const role = this.currentUser!.get('roleName'); if (!['客服', '组长', '管理员'].includes(role)) { window?.fmode?.alert('您没有权限创建项目'); return; } try { this.creating = true; const Project = Parse.Object.extend('Project'); const project = new Project(); project.set('title', this.projectName.trim()); project.set('company', this.currentUser!.get('company')); project.set('status', '待分配'); project.set('currentStage', '订单分配'); project.set('data', { createdBy: this.currentUser!.id, createdFrom: 'wxwork_groupchat', groupChatId: this.groupChat!.id }); await project.save(); this.groupChat!.set('project', project.toPointer()); await this.groupChat!.save(); const ProjectGroup = Parse.Object.extend('ProjectGroup'); const pg = new ProjectGroup(); 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(); await this.activityLogService.logActivity({ actorId: this.currentUser!.id, actorName: this.currentUser!.get('name') || '系统', actorRole: role, actionType: 'create', module: 'project', entityType: 'Project', entityId: project.id, entityName: this.projectName.trim(), description: '创建了新项目', metadata: { createdFrom: 'wxwork_groupchat', groupChatId: this.groupChat!.id, status: '待分配', stage: '订单分配' } }); this.project = project; await this.navigateToProjectDetail(); } catch (err: any) { console.error('创建项目失败:', err); window?.fmode?.alert('创建失败: ' + (err.message || '未知错误')); } finally { this.creating = false; } } async selectHistoryProject(project: FmodeObject) { try { this.groupChat!.set('project', project.toPointer()); await this.groupChat!.save(); this.project = project; await this.navigateToProjectDetail(); } catch (err: any) { console.error('关联项目失败:', err); window?.fmode?.alert('关联失败: ' + (err.message || '未知错误')); } } async navigateToProjectDetail() { await this.router.navigate(['/wxwork', this.cid, 'project', this.project!.id], { queryParams: { groupId: this.groupChat?.id, profileId: this.currentUser!.id } }); } // ==================== 工具方法 ==================== async reload() { this.error = null; this.showCreateGuide = false; this.historyProjects = []; this.chatType = 'personal'; 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('roleName') || '未知'; } getProjectStatusClass(status: string): string { const classMap: any = { '待分配': 'status-pending', '进行中': 'status-active', '已完成': 'status-completed', '已暂停': 'status-paused', '已取消': 'status-cancelled' }; return classMap[status] || 'status-default'; } formatDate(date: Date | string): string { if (!date) return ''; const d = new Date(date); return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`; } formatMonth(monthStr: string): string { const [year, month] = monthStr.split('-'); return `${year}年${month}月`; } formatCurrency(amount: number): string { if (!amount) return '¥0'; return `¥${amount.toLocaleString()}`; } getScoreColor(score: number): string { if (score >= 80) return 'score-high'; if (score >= 60) return 'score-medium'; return 'score-low'; } getScoreProgress(current: number, target: number): number { if (target === 0) return 0; return Math.min((current / target) * 100, 100); } filterSkillsByCategory(category: string): SkillRating[] { return this.skillRatings.filter(s => s.category === category); } getMaxMonthlyProjects(): number { if (this.monthlyStats.length === 0) return 1; return Math.max(...this.monthlyStats.map(m => m.totalProjects)); } updateStrengths(value: string) { if (!this.editingEvaluation) return; this.editingEvaluation.strengths = value.split(',').map(s => s.trim()).filter(s => s.length > 0); } updateImprovements(value: string) { if (!this.editingEvaluation) return; this.editingEvaluation.improvements = value.split(',').map(s => s.trim()).filter(s => s.length > 0); } }