import { Component, OnInit, Input } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Router, ActivatedRoute, RouterModule } from '@angular/router'; import { IonicModule } from '@ionic/angular'; import { WxworkSDK, WxworkCorp, WxworkAuth } from 'fmode-ng/core'; import { FmodeParse, FmodeObject } from 'fmode-ng/parse'; import { ProfileService } from '../../../../app/services/profile.service'; import { ProjectBottomCardComponent } from '../../components/project-bottom-card/project-bottom-card.component'; import { ProjectFilesModalComponent } from '../../components/project-files-modal/project-files-modal.component'; import { ProjectMembersModalComponent } from '../../components/project-members-modal/project-members-modal.component'; import { ProjectIssuesModalComponent } from '../../components/project-issues-modal/project-issues-modal.component'; import { ProjectIssueService } from '../../services/project-issue.service'; const Parse = FmodeParse.with('nova'); /** * 项目详情核心组件 * * 功能: * 1. 展示四阶段导航(订单分配、确认需求、交付执行、售后归档) * 2. 根据角色控制权限 * 3. 子路由切换阶段内容 * 4. 支持@Input和路由参数两种数据加载方式 * * 路由:/wxwork/:cid/project/:projectId */ @Component({ selector: 'app-project-detail', standalone: true, imports: [CommonModule, IonicModule, RouterModule, ProjectBottomCardComponent, ProjectFilesModalComponent, ProjectMembersModalComponent, ProjectIssuesModalComponent], templateUrl: './project-detail.component.html', styleUrls: ['./project-detail.component.scss'] }) export class ProjectDetailComponent implements OnInit { // 输入参数(支持组件复用) @Input() project: FmodeObject | null = null; @Input() groupChat: FmodeObject | null = null; @Input() currentUser: FmodeObject | null = null; // 问题统计 issueCount: number = 0; // 路由参数 cid: string = ''; projectId: string = ''; groupId: string = ''; profileId: string = ''; chatId: string = ''; // 从企微进入时的 chat_id // 企微SDK wxwork: WxworkSDK | null = null; wecorp: WxworkCorp | null = null; wxAuth: WxworkAuth | null = null; // WxworkAuth 实例 // 加载状态 loading: boolean = true; error: string | null = null; // 项目数据 contact: FmodeObject | null = null; assignee: FmodeObject | null = null; // 当前阶段 currentStage: string = 'order'; // order | requirements | delivery | aftercare stages = [ { id: 'order', name: '订单分配', icon: 'document-text-outline', number: 1 }, { id: 'requirements', name: '确认需求', icon: 'checkmark-circle-outline', number: 2 }, { id: 'delivery', name: '交付执行', icon: 'rocket-outline', number: 3 }, { id: 'aftercare', name: '售后归档', icon: 'archive-outline', number: 4 } ]; // 权限 canEdit: boolean = false; canViewCustomerPhone: boolean = false; role: string = ''; // 模态框状态 showFilesModal: boolean = false; showMembersModal: boolean = false; showIssuesModal: boolean = false; constructor( private router: Router, private route: ActivatedRoute, private profileService: ProfileService, private issueService: ProjectIssueService ) {} async ngOnInit() { // 获取路由参数 this.cid = this.route.snapshot.paramMap.get('cid') || ''; this.projectId = this.route.snapshot.paramMap.get('projectId') || ''; this.groupId = this.route.snapshot.queryParamMap.get('groupId') || ''; this.profileId = this.route.snapshot.queryParamMap.get('profileId') || ''; this.chatId = this.route.snapshot.queryParamMap.get('chatId') || ''; // 监听路由变化 this.route.firstChild?.url.subscribe((segments) => { if (segments.length > 0) { this.currentStage = segments[0].path; } }); // 初始化企微授权(不阻塞页面加载) await this.initWxworkAuth(); await this.loadData(); } /** * 初始化企微授权(不阻塞页面) */ async initWxworkAuth() { let cid = this.cid || localStorage.getItem("company") || ""; this.wxAuth = new WxworkAuth({ cid: cid}); this.wxwork = new WxworkSDK({ cid: cid, appId: 'crm' }); this.wecorp = new WxworkCorp(cid); } /** * 加载数据 */ async loadData() { try { this.loading = true; // 2. 获取当前用户(优先从全局服务获取) if (!this.currentUser?.id) { this.currentUser = await this.wxAuth?.currentProfile(); } console.log("777",this.currentUser) // 设置权限 console.log(this.currentUser) this.role = this.currentUser?.get('roleName') || ''; this.canEdit = ['客服', '组员', '组长', '管理员'].includes(this.role); this.canViewCustomerPhone = ['客服', '组长', '管理员'].includes(this.role); const companyId = this.currentUser?.get('company')?.id || localStorage?.getItem("company"); // 3. 加载项目 if (!this.project) { if (this.projectId) { // 通过 projectId 加载(从后台进入) const query = new Parse.Query('Project'); query.include('contact', 'assignee','department','department.leader'); this.project = await query.get(this.projectId); } else if (this.chatId) { // 通过 chat_id 查找项目(从企微群聊进入) if (companyId) { // 先查找 GroupChat const gcQuery = new Parse.Query('GroupChat'); gcQuery.equalTo('chat_id', this.chatId); gcQuery.equalTo('company', companyId); let groupChat = await gcQuery.first(); if (groupChat) { this.groupChat = groupChat; const projectPointer = groupChat.get('project'); if (projectPointer) { const pQuery = new Parse.Query('Project'); pQuery.include('contact', 'assignee','department','department.leader'); this.project = await pQuery.get(projectPointer.id); } } if (!this.project) { throw new Error('该群聊尚未关联项目,请先在后台创建项目'); } } } } if(!this.groupChat?.id){ const gcQuery2 = new Parse.Query('GroupChat'); gcQuery2.equalTo('project', this.projectId); gcQuery2.equalTo('company', companyId); this.groupChat = await gcQuery2.first(); } if (!this.project) { throw new Error('无法加载项目信息'); } this.contact = this.project.get('contact'); this.assignee = this.project.get('assignee'); // 更新问题计数 try { if (this.project?.id) { this.issueService.seed(this.project.id!); const counts = this.issueService.getCounts(this.project.id!); this.issueCount = counts.total; } } catch (e) { console.warn('统计问题数量失败:', e); } // 4. 加载群聊(如果没有传入且有groupId) if (!this.groupChat && this.groupId) { try { const gcQuery = new Parse.Query('GroupChat'); this.groupChat = await gcQuery.get(this.groupId); } catch (err) { console.warn('加载群聊失败:', err); } } // 5. 根据项目当前阶段设置默认路由 const projectStage = this.project.get('currentStage'); const stageMap: any = { '订单分配': 'order', '确认需求': 'requirements', '方案确认': 'requirements', '方案深化': 'requirements', '交付执行': 'delivery', '建模': 'delivery', '软装': 'delivery', '渲染': 'delivery', '后期': 'delivery', '尾款结算': 'aftercare', '客户评价': 'aftercare', '投诉处理': 'aftercare' }; const targetStage = stageMap[projectStage] || 'order'; // 如果当前没有子路由,跳转到对应阶段 if (!this.route.firstChild) { this.router.navigate([targetStage], { relativeTo: this.route, replaceUrl: true }); } } catch (err: any) { console.error('加载失败:', err); this.error = err.message || '加载失败'; } finally { this.loading = false; } } /** * 切换阶段 */ switchStage(stageId: string) { this.currentStage = stageId; this.router.navigate([stageId], { relativeTo: this.route }); } /** * 获取阶段状态 */ getStageStatus(stageId: string): 'completed' | 'active' | 'pending' { const projectStage = this.project?.get('currentStage') || ''; const stageOrder = ['订单分配', '确认需求', '建模', '软装', '渲染', '后期', '尾款结算', '客户评价']; const currentIndex = stageOrder.indexOf(projectStage); const stageIndexMap: any = { 'order': 0, 'requirements': 1, 'delivery': 3, 'aftercare': 6 }; const targetIndex = stageIndexMap[stageId]; if (currentIndex > targetIndex) { return 'completed'; } else if (this.currentStage === stageId) { return 'active'; } else { return 'pending'; } } /** * 返回 */ goBack() { let ua = navigator.userAgent.toLowerCase(); let isWeixin = ua.indexOf("micromessenger") != -1; if(isWeixin){ this.router.navigate(['/wxwork', this.cid, 'project-loader']); }else{ history.back(); } } /** * 更新项目阶段 */ async updateProjectStage(stage: string) { if (!this.project || !this.canEdit) return; try { this.project.set('currentStage', stage); await this.project.save(); // 添加阶段历史 const data = this.project.get('data') || {}; const stageHistory = data.stageHistory || []; stageHistory.push({ stage, startTime: new Date(), status: 'current', operator: { id: this.currentUser!.id, name: this.currentUser!.get('name'), role: this.role } }); this.project.set('data', { ...data, stageHistory }); await this.project.save(); } catch (err) { console.error('更新阶段失败:', err); alert('更新失败'); } } /** * 发送企微消息 */ async sendWxMessage(message: string) { if (!this.groupChat || !this.wecorp) return; try { const chatId = this.groupChat.get('chat_id'); await this.wecorp.appchat.sendText(chatId, message); } catch (err) { console.error('发送消息失败:', err); } } /** * 选择客户(从群聊成员中选择外部联系人) */ async selectCustomer() { console.log(this.canEdit, this.groupChat) if (!this.groupChat) return; try { const memberList = this.groupChat.get('member_list') || []; const externalMembers = memberList.filter((m: any) => m.type === 2); if (externalMembers.length === 0) { alert('当前群聊中没有外部联系人'); return; } console.log(externalMembers) // 简单实现:选择第一个外部联系人 // TODO: 实现选择器UI const selectedMember = externalMembers[0]; await this.setCustomerFromMember(selectedMember); } catch (err) { console.error('选择客户失败:', err); alert('选择客户失败'); } } /** * 从群成员设置客户 */ async setCustomerFromMember(member: any) { if (!this.wecorp) return; try { const companyId = this.currentUser?.get('company')?.id || localStorage.getItem("company"); if (!companyId) throw new Error('无法获取企业信息'); // 1. 查询是否已存在 ContactInfo const query = new Parse.Query('ContactInfo'); query.equalTo('external_userid', member.userid); query.equalTo('company', companyId); let contactInfo = await query.first(); // 2. 如果不存在,通过企微API获取并创建 if (!contactInfo) { contactInfo = new Parse.Object("ContactInfo"); } const externalContactData = await this.wecorp.externalContact.get(member.userid); console.log("externalContactData",externalContactData) const ContactInfo = Parse.Object.extend('ContactInfo'); contactInfo.set('name', externalContactData.name); contactInfo.set('external_userid', member.userid); const company = new Parse.Object('Company'); company.id = companyId; const companyPointer = company.toPointer(); contactInfo.set('company', companyPointer); contactInfo.set('data', externalContactData); await contactInfo.save(); // 3. 设置为项目客户 if (this.project) { this.project.set('contact', contactInfo.toPointer()); await this.project.save(); this.contact = contactInfo; alert('客户设置成功'); } } catch (err) { console.error('设置客户失败:', err); throw err; } } /** * 显示文件模态框 */ showFiles() { this.showFilesModal = true; } /** * 显示成员模态框 */ showMembers() { this.showMembersModal = true; } /** 显示问题模态框 */ showIssues() { this.showIssuesModal = true; } /** * 关闭文件模态框 */ closeFilesModal() { this.showFilesModal = false; } /** * 关闭成员模态框 */ closeMembersModal() { this.showMembersModal = false; } /** 关闭问题模态框 */ closeIssuesModal() { this.showIssuesModal = false; // 关闭后更新计数(避免列表操作后的计数不一致) if (this.project?.id) { const counts = this.issueService.getCounts(this.project.id!); this.issueCount = counts.total; } } }