// 客服工作台 - 对接Parse Server真实数据 import { Component, OnInit, OnDestroy, signal, computed } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { RouterModule, Router, ActivatedRoute } from '@angular/router'; import { ProfileService } from '../../../services/profile.service'; import { UrgentTaskService } from '../../../services/urgent-task.service'; import { ActivityLogService } from '../../../services/activity-log.service'; import { FmodeParse, FmodeObject } from 'fmode-ng/parse'; const Parse = FmodeParse.with('nova'); // 项目数据接口 interface ProjectData { id: string; title: string; customerName: string; customerPhone?: string; status: string; stage: string; assigneeName?: string; createdAt: Date; updatedAt: Date; deadline?: Date; priority?: string; description?: string; } // 任务数据接口 interface Task { id: string; projectId: string; projectName: string; title: string; stage: string; deadline: Date; isOverdue: boolean; isCompleted: boolean; priority: 'high' | 'medium' | 'low'; assignee: string; description?: string; status: string; } // 项目更新联合类型 interface ProjectUpdate { id: string; name?: string; customerName: string; status: string; updatedAt?: Date; createdAt?: Date; } interface FeedbackUpdate { id: string; customerName: string; content: string; status: string; createdAt: Date; feedbackType: string; } // 项目类型(用于项目动态) interface Project { id: string; name: string; customerName: string; status: string; updatedAt?: Date; createdAt?: Date; deadline?: Date; } // 客户反馈类型 interface CustomerFeedback { id: string; projectId: string; customerName: string; content: string; status: string; createdAt: Date; } @Component({ selector: 'app-dashboard', standalone: true, imports: [CommonModule, FormsModule, RouterModule], templateUrl: './dashboard.html', styleUrls: ['./dashboard.scss', './dashboard-urgent-tasks-enhanced.scss', '../customer-service-styles.scss'] }) export class Dashboard implements OnInit, OnDestroy { // 数据看板统计 stats = { totalProjects: signal(0), // 项目总数 newConsultations: signal(0), // 新咨询数 pendingAssignments: signal(0), // 待分配项目数(原待派单数) exceptionProjects: signal(0), // 异常项目数 afterSalesCount: signal(0) // 售后服务数量 }; // 紧急任务列表 urgentTasks = signal([]); // 任务处理状态 taskProcessingState = signal>>({}); // 新增:待跟进尾款项目列表(真实数据) pendingFinalPaymentProjects = signal>([]); // 项目动态流 projectUpdates = signal<(Project | CustomerFeedback)[]>([]); // 搜索关键词 searchTerm = signal(''); // 筛选后的项目更新 filteredUpdates = computed(() => { if (!this.searchTerm()) return this.projectUpdates(); return this.projectUpdates().filter(item => { if ('name' in item) { // 项目 return item.name.toLowerCase().includes(this.searchTerm().toLowerCase()) || item.customerName.toLowerCase().includes(this.searchTerm().toLowerCase()) || item.status.toLowerCase().includes(this.searchTerm().toLowerCase()); } else { // 反馈 return 'content' in item && item.content.toLowerCase().includes(this.searchTerm().toLowerCase()) || 'status' in item && item.status.toLowerCase().includes(this.searchTerm().toLowerCase()); } }); }); currentDate = new Date(); // 回到顶部按钮可见性信号 showBackToTopSignal = signal(false); // 任务表单可见性 isTaskFormVisible = signal(false); // 项目列表(用于下拉选择) projectList = signal([]); // 空间列表(用于下拉选择) spaceList = signal([]); // 团队成员列表(用于指派) teamMembers = signal([]); // 新任务数据 newTask: any = { title: '', description: '', projectId: '', spaceId: '', stage: '订单分配', region: '', priority: 'high', assigneeId: '', deadline: new Date() }; // 用于日期时间输入的属性 deadlineInput = ''; // 预设快捷时长选项 timePresets = [ { label: '1小时内', hours: 1 }, { label: '3小时内', hours: 3 }, { label: '6小时内', hours: 6 }, { label: '12小时内', hours: 12 }, { label: '24小时内', hours: 24 } ]; // 选中的预设时长 selectedPreset = ''; // 自定义时间弹窗可见性 isCustomTimeVisible = false; // 自定义选择的日期和时间 customDate = new Date(); customTime = ''; // 错误提示信息 deadlineError = ''; // 提交按钮是否禁用 isSubmitDisabled = false; // 下拉框可见性 deadlineDropdownVisible = false; // 日期范围限制 get todayDate(): string { return new Date().toISOString().split('T')[0]; } get sevenDaysLaterDate(): string { const date = new Date(); date.setDate(date.getDate() + 7); return date.toISOString().split('T')[0]; } constructor( private router: Router, private route: ActivatedRoute, private profileService: ProfileService, private urgentTaskService: UrgentTaskService, private activityLogService: ActivityLogService ) {} // 当前用户和公司信息 currentUser = signal(null); company = signal(null); // 初始化用户和公司信息 private async initializeUserAndCompany(): Promise { try { const profile = await this.profileService.getCurrentProfile(); this.currentUser.set(profile); // 获取公司信息 - 映三色帐套 const companyQuery = new Parse.Query('Company'); companyQuery.equalTo('objectId', 'cDL6R1hgSi'); const company = await companyQuery.first(); if (!company) { throw new Error('未找到公司信息'); } this.company.set(company); console.log('✅ 用户和公司信息初始化完成'); } catch (error) { console.error('❌ 用户和公司信息初始化失败:', error); throw error; } } // 获取公司指针 private getCompanyPointer(): any { if (!this.company()) { throw new Error('公司信息未加载'); } return { __type: 'Pointer', className: 'Company', objectId: this.company().id }; } // 创建带公司过滤的查询 private createQuery(className: string): any { const query = new Parse.Query(className); query.equalTo('company', this.getCompanyPointer()); query.notEqualTo('isDeleted', true); return query; } async ngOnInit(): Promise { try { await this.initializeUserAndCompany(); await this.loadDashboardData(); // 添加滚动事件监听 window.addEventListener('scroll', this.onScroll.bind(this)); } catch (error) { console.error('❌ 客服工作台初始化失败:', error); } } // 加载仪表板数据 private async loadDashboardData(): Promise { try { await Promise.all([ this.loadConsultationStats(), this.loadUrgentTasks(), this.loadProjectUpdates(), this.loadCRMQueues(), this.loadPendingFinalPaymentProjects() ]); console.log('✅ 客服仪表板数据加载完成'); } catch (error) { console.error('❌ 客服仪表板数据加载失败:', error); throw error; } } // 加载咨询统计数据 private async loadConsultationStats(): Promise { try { const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0); // 项目总数 const totalProjectQuery = this.createQuery('Project'); const totalProjects = await totalProjectQuery.count(); this.stats.totalProjects.set(totalProjects); // 新咨询数(今日新增的项目) const consultationQuery = this.createQuery('Project'); consultationQuery.greaterThanOrEqualTo('createdAt', todayStart); const newConsultations = await consultationQuery.count(); this.stats.newConsultations.set(newConsultations); // 待分配项目数(阶段处于"订单分配"的项目) // 参考组长工作台的筛选逻辑:根据四大板块筛选 // 订单分配阶段包括:order, pendingApproval, pendingAssignment, 订单分配, 待审批, 待分配 // 查询所有项目,然后在客户端筛选(与组长工作台保持一致) const allProjectsQuery = this.createQuery('Project'); allProjectsQuery.limit(1000); // 限制最多1000个项目 const allProjects = await allProjectsQuery.find(); // 使用与组长工作台相同的筛选逻辑 const orderPhaseProjects = allProjects.filter(p => { const currentStage = p.get('currentStage') || ''; const stage = p.get('stage') || ''; const stageValue = (currentStage || stage).toString().trim().toLowerCase(); // 订单分配阶段的所有变体(与组长工作台mapStageToCorePhase保持一致) const isOrderPhase = stageValue === 'order' || stageValue === 'pendingapproval' || stageValue === 'pendingassignment' || stageValue === '订单分配' || stageValue === '待审批' || stageValue === '待分配'; // 调试日志:输出每个项目的阶段信息 if (isOrderPhase) { console.log(`📋 订单分配项目: ${p.get('title')}, currentStage="${currentStage}", stage="${stage}", 匹配值="${stageValue}"`); } return isOrderPhase; }); const pendingAssignments = orderPhaseProjects.length; this.stats.pendingAssignments.set(pendingAssignments); console.log(`✅ 待分配项目统计: 总项目数=${allProjects.length}, 订单分配阶段项目数=${pendingAssignments}`); // 异常项目数(使用ProjectIssue表) const issueQuery = this.createQuery('ProjectIssue'); issueQuery.equalTo('priority', 'high'); issueQuery.equalTo('status', 'open'); const exceptionProjects = await issueQuery.count(); this.stats.exceptionProjects.set(exceptionProjects); // 售后服务数量(使用ProjectFeedback表,类型为投诉的待处理反馈) const feedbackQuery = this.createQuery('ProjectFeedback'); feedbackQuery.equalTo('status', 'pending'); feedbackQuery.equalTo('feedbackType', 'complaint'); const afterSalesCount = await feedbackQuery.count(); this.stats.afterSalesCount.set(afterSalesCount); console.log(`✅ 咨询统计: 项目总数${totalProjects}, 新咨询${newConsultations}, 待分配${pendingAssignments}, 异常${exceptionProjects}, 售后${afterSalesCount}`); } catch (error) { console.error('❌ 咨询统计加载失败:', error); // 不抛出错误,允许其他数据继续加载 } } // 降级到模拟数据 private loadMockData(): void { console.warn('⚠️ 使用模拟数据'); this.loadUrgentTasks(); this.loadProjectUpdates(); this.loadCRMQueues(); // loadPendingFinalPaymentProjects 已改为异步真实数据查询 } // 添加滚动事件处理方法 private onScroll(): void { this.showBackToTopSignal.set(window.scrollY > 300); } // 添加显示回到顶部按钮的计算属性 showBackToTop = computed(() => this.showBackToTopSignal()); // 清理事件监听器 ngOnDestroy(): void { window.removeEventListener('scroll', this.onScroll.bind(this)); } // 添加scrollToTop方法 scrollToTop(): void { window.scrollTo({ top: 0, behavior: 'smooth' }); } // 查看人员考勤 viewAttendance(): void { this.router.navigate(['/hr/attendance']); } // 加载紧急任务 private async loadUrgentTasks(): Promise { try { // 使用UrgentTaskService加载紧急事项 const result = await this.urgentTaskService.findUrgentTasks({ isCompleted: false }, 1, 20); // 转换数据格式以兼容现有UI const formattedTasks: Task[] = result.tasks.map(task => ({ id: task.id, projectId: task.projectId, projectName: task.projectName, title: task.title, stage: task.stage, deadline: task.deadline, isOverdue: task.isOverdue, isCompleted: task.isCompleted, priority: task.priority as 'high' | 'medium' | 'low', assignee: task.assigneeName, description: task.description || '', status: task.status })); this.urgentTasks.set(formattedTasks); console.log(`✅ 紧急任务加载完成: ${formattedTasks.length} 个任务`); } catch (error) { console.error('❌ 紧急任务加载失败:', error); this.urgentTasks.set([]); } } // 加载CRM队列数据(已隐藏,暂不使用真实数据) private loadCRMQueues(): void { // CRM功能暂时隐藏,后续开发时再从Parse查询真实数据 // 可以从ProjectFeedback表查询客户反馈和咨询记录 console.log('⏸️ CRM队列功能暂时隐藏'); } // 查看全部咨询列表 goToConsultationList(): void { this.router.navigate(['/customer-service/consultation-list']); } // 加载项目动态 private async loadProjectUpdates(): Promise { try { const updates: (Project | CustomerFeedback)[] = []; // 1. 查询最新更新的项目 const projectQuery = this.createQuery('Project'); projectQuery.include(['contact', 'assignee']); projectQuery.descending('updatedAt'); projectQuery.limit(10); const projects = await projectQuery.find(); for (const project of projects) { const contact = project.get('contact'); updates.push({ id: project.id, name: project.get('title') || '未命名项目', customerName: contact?.get('name') || '未知客户', status: project.get('status') || '进行中', updatedAt: project.get('updatedAt'), createdAt: project.get('createdAt') }); } // 2. 查询最新客户反馈 const feedbackQuery = this.createQuery('ProjectFeedback'); feedbackQuery.include(['contact', 'project']); feedbackQuery.descending('createdAt'); feedbackQuery.limit(10); const feedbacks = await feedbackQuery.find(); for (const feedback of feedbacks) { const contact = feedback.get('contact'); updates.push({ id: feedback.id, projectId: feedback.get('project')?.id || '', customerName: contact?.get('name') || '未知客户', content: feedback.get('content') || '无内容', status: feedback.get('status') || 'pending', createdAt: feedback.get('createdAt') }); } // 按时间排序 updates.sort((a, b) => { const aTime = ('updatedAt' in a && a.updatedAt) ? a.updatedAt.getTime() : (a.createdAt?.getTime() || 0); const bTime = ('updatedAt' in b && b.updatedAt) ? b.updatedAt.getTime() : (b.createdAt?.getTime() || 0); return bTime - aTime; }); this.projectUpdates.set(updates); console.log(`✅ 项目动态加载完成: ${updates.length} 条动态`); } catch (error) { console.error('❌ 项目动态加载失败:', error); // 不抛出错误,允许其他数据继续加载 } } // 处理任务完成 async markTaskAsCompleted(taskId: string): Promise { try { const task = this.urgentTasks().find(t => t.id === taskId); await this.urgentTaskService.markAsCompleted(taskId); // 记录活动日志 if (task) { try { const user = this.currentUser(); await this.activityLogService.logActivity({ actorId: user?.id || 'unknown', actorName: user?.get('name') || '客服', actorRole: user?.get('roleName') || 'customer_service', actionType: 'complete', module: 'urgent_task', entityType: 'UrgentTask', entityId: taskId, entityName: task.title, description: '完成了紧急事项', metadata: { priority: task.priority, projectName: task.projectName } }); } catch (logError) { console.error('记录活动日志失败:', logError); } } // 重新加载任务列表 await this.loadUrgentTasks(); console.log('✅ 任务标记为已完成'); } catch (error) { console.error('❌ 标记任务完成失败:', error); alert('操作失败,请稍后重试'); } } // 删除任务 async deleteTask(taskId: string): Promise { if (!await window?.fmode?.confirm('确定要删除这个紧急事项吗?')) { return; } try { await this.urgentTaskService.deleteUrgentTask(taskId); // 重新加载任务列表 await this.loadUrgentTasks(); console.log('✅ 任务删除成功'); } catch (error) { console.error('❌ 删除任务失败:', error); alert('删除失败,请稍后重试'); } } // 处理派单操作 handleAssignment(taskId: string): void { // 标记任务为处理中 const task = this.urgentTasks().find(t => t.id === taskId); if (task) { // 初始化处理状态 this.taskProcessingState.update(state => ({ ...state, [task.id]: { inProgress: true, progress: 0 } })); // 模拟处理进度 let progress = 0; const interval = setInterval(() => { progress += 10; this.taskProcessingState.update(state => ({ ...state, [task.id]: { inProgress: progress < 100, progress } })); if (progress >= 100) { clearInterval(interval); // 处理完成后从列表中移除该任务 this.urgentTasks.set( this.urgentTasks().filter(t => t.id !== task.id) ); // 清除处理状态 this.taskProcessingState.update(state => { const newState = { ...state }; delete newState[task.id]; return newState; }); } }, 300); } // 更新统计数据 this.stats.pendingAssignments.set(this.stats.pendingAssignments() - 1); } // 显示任务表单 async showTaskForm(): Promise { // 重置表单数据 this.newTask = { title: '', description: '', projectId: '', spaceId: '', stage: '订单分配', region: '', priority: 'high', assigneeId: '', deadline: new Date() }; // 重置相关状态 this.deadlineError = ''; this.isSubmitDisabled = false; // 计算并设置默认预设时长 this.setDefaultPreset(); // 加载下拉列表数据 try { const [projects, members] = await Promise.all([ this.urgentTaskService.getProjects(), this.urgentTaskService.getTeamMembers() ]); this.projectList.set(projects); this.teamMembers.set(members); this.spaceList.set([]); // 初始为空,等待选择项目后加载 } catch (error) { console.error('加载下拉列表数据失败:', error); } // 显示表单 this.isTaskFormVisible.set(true); // 添加iOS风格的面板显示动画 setTimeout(() => { document.querySelector('.ios-panel')?.classList.add('ios-panel-visible'); }, 10); } // 项目选择变化时加载空间列表 async onProjectChange(projectId: string): Promise { if (!projectId) { this.spaceList.set([]); return; } try { const spaces = await this.urgentTaskService.getProjectSpaces(projectId); this.spaceList.set(spaces); } catch (error) { console.error('加载空间列表失败:', error); this.spaceList.set([]); } } // 设置默认预设时长 private setDefaultPreset(): void { const now = new Date(); const todayEnd = new Date(now); todayEnd.setHours(23, 59, 59, 999); // 检查3小时后是否超过当天24:00 const threeHoursLater = new Date(now.getTime() + 3 * 60 * 60 * 1000); if (threeHoursLater <= todayEnd) { // 3小时后未超过当天24:00,默认选中3小时内 this.selectedPreset = '3'; this.updatePresetDeadline(3); } else { // 3小时后超过当天24:00,默认选中当天24:00前 this.selectedPreset = 'today'; this.deadlineInput = todayEnd.toISOString().slice(0, 16); this.newTask.deadline = todayEnd; } } // 处理预设时长选择 handlePresetSelection(preset: string): void { this.selectedPreset = preset; this.deadlineError = ''; if (preset === 'custom') { // 打开自定义时间选择器 this.openCustomTimePicker(); } else if (preset === 'today') { // 设置为当天24:00前 const now = new Date(); const todayEnd = new Date(now); todayEnd.setHours(23, 59, 59, 999); this.deadlineInput = todayEnd.toISOString().slice(0, 16); this.newTask.deadline = todayEnd; } else { // 计算预设时长的截止时间 const hours = parseInt(preset); this.updatePresetDeadline(hours); } } // 更新预设时长的截止时间 private updatePresetDeadline(hours: number): void { const now = new Date(); const deadline = new Date(now.getTime() + hours * 60 * 60 * 1000); this.deadlineInput = deadline.toISOString().slice(0, 16); this.newTask.deadline = deadline; } // 打开自定义时间选择器 openCustomTimePicker(): void { // 重置自定义时间 this.customDate = new Date(); const now = new Date(); this.customTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`; // 显示自定义时间弹窗 this.isCustomTimeVisible = true; // 添加iOS风格的弹窗动画 setTimeout(() => { document.querySelector('.custom-time-modal')?.classList.add('modal-visible'); }, 10); } // 关闭自定义时间选择器 closeCustomTimePicker(): void { // 添加iOS风格的弹窗关闭动画 const modal = document.querySelector('.custom-time-modal'); if (modal) { modal.classList.remove('modal-visible'); setTimeout(() => { this.isCustomTimeVisible = false; }, 300); } else { this.isCustomTimeVisible = false; } } // 处理自定义时间选择 handleCustomTimeSelection(): void { const [hours, minutes] = this.customTime.split(':').map(Number); const selectedDateTime = new Date(this.customDate); selectedDateTime.setHours(hours, minutes, 0, 0); // 验证选择的时间是否有效 if (this.validateDeadline(selectedDateTime)) { this.deadlineInput = selectedDateTime.toISOString().slice(0, 16); this.newTask.deadline = selectedDateTime; this.closeCustomTimePicker(); } } // 验证截止时间是否有效 validateDeadline(deadline: Date): boolean { const now = new Date(); if (deadline < now) { this.deadlineError = '截止时间不能早于当前时间,请重新选择'; this.isSubmitDisabled = true; return false; } this.deadlineError = ''; this.isSubmitDisabled = false; return true; } // 获取显示的截止时间文本 getDisplayDeadline(): string { if (!this.deadlineInput) return ''; try { const date = new Date(this.deadlineInput); return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch (error) { return ''; } } // 隐藏任务表单 hideTaskForm(): void { // 添加iOS风格的面板隐藏动画 const panel = document.querySelector('.ios-panel'); if (panel) { panel.classList.remove('ios-panel-visible'); setTimeout(() => { this.isTaskFormVisible.set(false); }, 300); } else { this.isTaskFormVisible.set(false); } } // 处理添加任务表单提交 async handleAddTaskSubmit(): Promise { // 验证表单数据 if (!this.newTask.title.trim() || !this.newTask.projectName.trim() || !this.deadlineInput || this.isSubmitDisabled) { // 在实际应用中,这里应该显示错误提示 window?.fmode?.alert('请填写必填字段(任务标题、项目名称、截止时间)'); return; } try { // 创建紧急事项 const task = await this.urgentTaskService.createUrgentTask({ title: this.newTask.title, description: this.newTask.description, projectId: this.newTask.projectId, spaceId: this.newTask.spaceId || undefined, stage: this.newTask.stage, region: this.newTask.region, priority: this.newTask.priority, assigneeId: this.newTask.assigneeId || undefined, deadline: new Date(this.deadlineInput) }); // 记录活动日志 try { const user = this.currentUser(); const projectName = this.projectList().find(p => p.id === this.newTask.projectId)?.get('title') || '未知项目'; await this.activityLogService.logActivity({ actorId: user?.id || 'unknown', actorName: user?.get('name') || '客服', actorRole: user?.get('roleName') || 'customer_service', actionType: 'create', module: 'urgent_task', entityType: 'UrgentTask', entityId: task.id, entityName: this.newTask.title, description: '创建了紧急事项', metadata: { priority: this.newTask.priority, projectName: projectName, stage: this.newTask.stage, region: this.newTask.region, deadline: this.deadlineInput } }); } catch (logError) { console.error('记录活动日志失败:', logError); } // 重新加载任务列表 await this.loadUrgentTasks(); console.log('✅ 紧急事项创建成功'); // 隐藏表单 this.hideTaskForm(); } catch (error) { console.error('❌ 创建紧急事项失败:', error); alert('创建失败,请稍后重试'); } } // 添加新的紧急事项 addUrgentTask(): void { // 调用显示表单方法 this.showTaskForm(); } // 项目总数图标点击处理 handleTotalProjectsClick(): void { console.log('导航到项目列表 - 显示所有项目'); this.router.navigate(['/customer-service/project-list'], { queryParams: { filter: 'all' } }); } // 新咨询数图标点击处理 handleNewConsultationsClick(): void { this.navigateToDetail('consultations'); } // 待分配数图标点击处理 handlePendingAssignmentsClick(): void { console.log('导航到项目列表 - 显示待分配项目'); this.router.navigate(['/customer-service/project-list'], { queryParams: { filter: 'pending' } }); } // 异常项目图标点击处理 handleExceptionProjectsClick(): void { this.navigateToDetail('exceptions'); } handleAfterSalesClick(): void { this.router.navigate(['/customer-service/after-sales']); } // 导航到详情页 private navigateToDetail(type: 'consultations' | 'assignments' | 'exceptions'): void { const routeMap = { consultations: '/customer-service/consultation-list', assignments: '/customer-service/assignment-list', exceptions: '/customer-service/exception-list' }; console.log('导航到:', routeMap[type]); console.log('当前路由:', this.router.url); // 添加iOS风格页面过渡动画 document.body.classList.add('ios-page-transition'); setTimeout(() => { this.router.navigateByUrl(routeMap[type]) .then(navResult => { console.log('导航结果:', navResult); if (!navResult) { console.error('导航失败,检查路由配置'); } }) .catch(err => { console.error('导航错误:', err); }); setTimeout(() => { document.body.classList.remove('ios-page-transition'); }, 300); }, 100); } // 格式化日期 formatDate(date: Date | string): string { if (!date) return ''; try { return new Date(date).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch (error) { console.error('日期格式化错误:', error); return ''; } } // 添加安全获取客户名称的方法 getCustomerName(update: Project | CustomerFeedback): string { if ('customerName' in update && update.customerName) { return update.customerName; } else if ('projectId' in update) { // 查找相关项目获取客户名称 return '客户反馈'; } return '未知客户'; } // 优化的日期格式化方法 getFormattedDate(update: Project | CustomerFeedback): string { if (!update) return ''; if ('createdAt' in update && update.createdAt) { return this.formatDate(update.createdAt); } else if ('updatedAt' in update && update.updatedAt) { return this.formatDate(update.updatedAt); } else if ('deadline' in update && update.deadline) { return this.formatDate(update.deadline); } return ''; } // 添加获取状态的安全方法 getUpdateStatus(update: Project | CustomerFeedback): string { if ('status' in update && update.status) { return update.status; } return '已更新'; } // 检查是否是项目更新 isProjectUpdate(update: Project | CustomerFeedback): update is Project { return 'name' in update && 'status' in update; } // 检查是否有内容字段 hasContent(update: Project | CustomerFeedback): boolean { return 'content' in update; } // 获取更新内容 getUpdateContent(update: Project | CustomerFeedback): string { if ('content' in update) { return (update as CustomerFeedback).content; } return ''; } // 处理搜索输入事件 onSearchInput(event: Event): void { const target = event.target as HTMLInputElement; if (target) { this.searchTerm.set(target.value); } } // 添加getTaskStatus方法的正确实现 getTaskStatus(task: Task): string { if (!task) return '未知状态'; if (task.isCompleted) return '已完成'; if (task.isOverdue) return '已逾期'; return '进行中'; } // 添加getUpdateStatusClass方法的正确实现 getUpdateStatusClass(update: Project | CustomerFeedback): string { if ('name' in update) { // 项目 switch (update.status) { case '进行中': return 'status-active'; case '已完成': return 'status-completed'; case '已暂停': return 'status-paused'; default: return 'status-pending'; } } else { // 反馈 switch (update.status) { case '已解决': return 'status-completed'; case '处理中': return 'status-active'; default: return 'status-pending'; } } } // 新增:加载待跟进尾款项目(从Parse真实数据) private async loadPendingFinalPaymentProjects(): Promise { try { const now = new Date(); const pendingProjects: Array<{ id: string; projectId: string; projectName: string; customerName: string; customerPhone: string; finalPaymentAmount: number; dueDate: Date; status: string; overdueDay: number; }> = []; // 查询所有待付款的尾款记录 const paymentQuery = this.createQuery('ProjectPayment'); paymentQuery.equalTo('type', 'final'); // 尾款类型 paymentQuery.containedIn('status', ['pending', 'overdue']); // 待付款或逾期状态 paymentQuery.include(['project', 'paidBy']); // 关联项目和付款人信息 paymentQuery.descending('dueDate'); // 按应付时间倒序 paymentQuery.limit(20); const payments = await paymentQuery.find(); for (const payment of payments) { const project = payment.get('project'); const paidBy = payment.get('paidBy'); const dueDate = payment.get('dueDate'); const amount = payment.get('amount'); const status = payment.get('status'); if (project && paidBy) { // 计算逾期天数 const overdueDays = status === 'overdue' ? Math.floor((now.getTime() - dueDate.getTime()) / (1000 * 60 * 60 * 24)) : 0; pendingProjects.push({ id: payment.id, projectId: project.id, projectName: project.get('title') || '未命名项目', customerName: paidBy.get('name') || '未知客户', customerPhone: paidBy.get('mobile') || '无电话', finalPaymentAmount: amount || 0, dueDate: dueDate || new Date(), status: status === 'overdue' ? '已逾期' : '待付款', overdueDay: overdueDays }); } } this.pendingFinalPaymentProjects.set(pendingProjects); console.log(`✅ 待跟进尾款项目加载完成: ${pendingProjects.length} 个项目`); } catch (error) { console.error('❌ 待跟进尾款项目加载失败:', error); // 不抛出错误,允许其他数据继续加载 } } // 新增:格式化日期时间 formatDateTime(date: Date): string { const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffMinutes = Math.floor(diffMs / (1000 * 60)); if (diffMinutes < 60) { return `${diffMinutes}分钟前`; } else if (diffHours < 24) { return `${diffHours}小时前`; } else { return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } } // 新增:获取支付状态文本 getPaymentStatusText(status: string): string { switch (status) { case 'pending_followup': return '待跟进'; case 'following_up': return '跟进中'; case 'payment_completed': return '已支付'; default: return '未知状态'; } } // 新增:开始跟进尾款 followUpFinalPayment(projectId: string): void { console.log(`开始跟进项目 ${projectId} 的尾款`); // 这里可以添加实际的跟进逻辑,比如发送消息、创建任务等 // 导航到项目详情页或打开跟进对话框 this.router.navigate(['/customer-service/project-detail', projectId]); } // 新增:查看项目详情 viewProjectDetail(projectId: string): void { this.router.navigate(['/customer-service/project-detail', projectId]); } // 新增:一键发送大图 sendLargeImages(projectId: string): void { const projects = this.pendingFinalPaymentProjects(); const project = projects.find(p => p.projectId === projectId); if (!project) return; console.log(`正在为项目 ${projectId} 发送大图到企业微信...`); // 模拟发送过程 setTimeout(() => { const updatedProjects = projects.map(p => { if (p.projectId === projectId) { return { ...p, largeImagesSent: true }; } return p; }); this.pendingFinalPaymentProjects.set(updatedProjects); console.log(`✅ 项目 ${projectId} 大图已成功发送到企业微信服务群`); console.log(`📱 已同步发送支付成功与大图交付通知`); window?.fmode?.alert(`🎉 大图发送成功! ✅ 已完成操作: • 大图已发送至企业微信服务群 • 已通知客户支付成功 • 已确认大图交付完成 项目:${project.projectName} 客户:${project.customerName}`); }, 2000); } }