import { Component, Input, computed, signal, OnInit } from '@angular/core'; import { CommonModule, DatePipe } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatTooltipModule } from '@angular/material/tooltip'; import { MatDialogModule, MatDialog } from '@angular/material/dialog'; import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar'; import { Settlement } from '../../../models/project.model'; import { AutoSettlementService } from '../../../services/auto-settlement.service'; import { ProjectService } from '../../../services/project.service'; export interface SettlementStats { totalAmount: number; pendingAmount: number; overdueAmount: number; completedAmount: number; totalCount: number; pendingCount: number; overdueCount: number; completedCount: number; } // 项目验收确认接口 export interface ProjectAcceptance { id: string; projectId: string; projectName: string; technicianId: string; technicianName: string; acceptanceStatus: 'pending' | 'confirmed' | 'rejected'; acceptanceDate?: Date; acceptanceNotes?: string; customerNotified: boolean; } // 客服跟进提醒接口 export interface CustomerServiceReminder { id: string; projectId: string; projectName: string; customerName: string; reminderType: 'payment_follow_up' | 'acceptance_notification' | 'delivery_confirmation'; reminderStatus: 'pending' | 'in_progress' | 'completed'; assignedTo: string; dueDate: Date; notes?: string; createdAt: Date; } // 企业微信群发图接口 export interface WeChatImageDelivery { id: string; projectId: string; groupId: string; groupName: string; imageUrls: string[]; deliveryStatus: 'pending' | 'sending' | 'completed' | 'failed'; deliveryDate?: Date; errorMessage?: string; } @Component({ selector: 'app-settlement-card', standalone: true, imports: [ CommonModule, DatePipe, FormsModule, MatButtonModule, MatIconModule, MatProgressSpinnerModule, MatTooltipModule, MatDialogModule, MatSnackBarModule ], templateUrl: './settlement-card.html', styleUrls: ['./settlement-card.scss'] }) export class SettlementCardComponent implements OnInit { @Input() settlements: Settlement[] = []; @Input() showAutomationControls = false; @Input() projectAcceptances: ProjectAcceptance[] = []; @Input() customerServiceReminders: CustomerServiceReminder[] = []; // 筛选条件 statusFilter = signal('all'); searchKeyword = signal(''); // 自动化处理状态 isProcessing = signal(false); processingSettlementId = signal(null); // 新增功能状态 acceptanceStatus = signal<{[key: string]: 'pending' | 'confirming' | 'confirmed'}>({}); reminderStatus = signal<{[key: string]: 'pending' | 'creating' | 'created'}>({}); isSendingImages = signal(false); sendingProjectId = signal(null); constructor( private autoSettlementService: AutoSettlementService, private dialog: MatDialog, private snackBar: MatSnackBar, private projectService: ProjectService ) { } ngOnInit() { // 启动定时自动化处理 this.autoSettlementService.startScheduledProcessing(); // 初始化项目验收状态 this.initializeAcceptanceStatus(); // 初始化客服提醒状态 this.initializeReminderStatus(); } // 初始化项目验收状态 private initializeAcceptanceStatus(): void { const status: {[key: string]: 'pending' | 'confirming' | 'confirmed'} = {}; this.settlements.forEach(settlement => { const acceptance = this.projectAcceptances.find(a => a.projectId === settlement.projectId); if (acceptance) { status[settlement.projectId] = acceptance.acceptanceStatus === 'confirmed' ? 'confirmed' : 'pending'; } else { status[settlement.projectId] = 'pending'; } }); this.acceptanceStatus.set(status); } // 初始化客服提醒状态 private initializeReminderStatus(): void { const status: {[key: string]: 'pending' | 'creating' | 'created'} = {}; this.settlements.forEach(settlement => { const reminder = this.customerServiceReminders.find(r => r.projectId === settlement.projectId && r.reminderType === 'payment_follow_up' ); if (reminder) { status[settlement.projectId] = reminder.reminderStatus === 'completed' ? 'created' : 'pending'; } else { status[settlement.projectId] = 'pending'; } }); this.reminderStatus.set(status); } // 技术确认项目验收 confirmProjectAcceptance(settlement: Settlement): void { const currentStatus = this.acceptanceStatus(); currentStatus[settlement.projectId] = 'confirming'; this.acceptanceStatus.set({...currentStatus}); // 模拟API调用 setTimeout(() => { const updatedStatus = this.acceptanceStatus(); updatedStatus[settlement.projectId] = 'confirmed'; this.acceptanceStatus.set({...updatedStatus}); this.snackBar.open(`项目 ${settlement.projectName} 验收确认成功`, '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top' }); // 自动创建客服跟进提醒 this.createCustomerServiceReminder(settlement, 'acceptance_notification'); // 验收完成后,自动发起尾款结算申请 this.initiateFinalSettlement(settlement); }, 1500); } // 创建客服跟进提醒 createCustomerServiceReminder(settlement: Settlement, type: 'payment_follow_up' | 'acceptance_notification' | 'delivery_confirmation'): void { const currentStatus = this.reminderStatus(); currentStatus[settlement.projectId] = 'creating'; this.reminderStatus.set({...currentStatus}); // 模拟API调用创建提醒 setTimeout(() => { const updatedStatus = this.reminderStatus(); updatedStatus[settlement.projectId] = 'created'; this.reminderStatus.set({...updatedStatus}); let message = ''; switch(type) { case 'payment_follow_up': message = `已创建尾款跟进提醒`; break; case 'acceptance_notification': message = `已创建验收通知提醒`; break; case 'delivery_confirmation': message = `已创建交付确认提醒`; break; } this.snackBar.open(`${settlement.projectName} ${message}`, '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top' }); }, 1000); } // 一键发图到企业微信群 sendImagesToWeChatGroup(settlement: Settlement): void { this.isSendingImages.set(true); this.sendingProjectId.set(settlement.projectId); // 模拟获取项目大图和发送到企业微信群 setTimeout(() => { this.isSendingImages.set(false); this.sendingProjectId.set(null); this.snackBar.open(`项目 ${settlement.projectName} 大图已发送到企业微信群`, '关闭', { duration: 4000, horizontalPosition: 'center', verticalPosition: 'top' }); // 自动创建交付确认提醒 this.createCustomerServiceReminder(settlement, 'delivery_confirmation'); }, 3000); } // 获取项目验收状态 getAcceptanceStatus(projectId: string): 'pending' | 'confirming' | 'confirmed' { return this.acceptanceStatus()[projectId] || 'pending'; } // 获取客服提醒状态 getReminderStatus(projectId: string): 'pending' | 'creating' | 'created' { return this.reminderStatus()[projectId] || 'pending'; } // 检查是否可以发送图片 canSendImages(settlement: Settlement): boolean { return settlement.status === '已结算' && this.getAcceptanceStatus(settlement.projectId) === 'confirmed'; } // 检查是否正在发送图片 isSendingImagesForProject(projectId: string): boolean { return this.isSendingImages() && this.sendingProjectId() === projectId; } // 处理单个结算 processSettlement(settlementId: string): void { const settlement = this.settlements.find(s => s.id === settlementId); if (!settlement) return; // 如果是逾期状态,显示逾期处理弹窗 if (settlement.status === '逾期' || this.isOverdue(settlement)) { this.handleOverdueSettlement(settlement); } else { this.processSettlementAutomation(settlement); } } // 处理逾期结算 async handleOverdueSettlement(settlement: Settlement): Promise { const daysOverdue = this.getDaysOverdue(settlement); const overdueMessage = ` 项目:${settlement.projectName} 阶段:${settlement.stage} 金额:¥${settlement.amount} 逾期天数:${daysOverdue}天 请选择处理方式: 1. 立即催款 2. 延期处理 3. 标记为已结算 `.trim(); if (await window?.fmode?.confirm(overdueMessage + '\n\n点击"确定"发送催款提醒,点击"取消"查看更多选项')) { // 发送催款提醒 this.sendOverdueReminder(settlement); } else { // 显示更多处理选项 await this.showOverdueOptions(settlement); } } // 发送逾期催款提醒 private sendOverdueReminder(settlement: Settlement): void { this.snackBar.open('正在发送催款提醒...', '', { duration: 2000, horizontalPosition: 'center', verticalPosition: 'top' }); setTimeout(() => { // 创建客服跟进提醒 this.createCustomerServiceReminder(settlement, 'payment_follow_up'); // 模拟发送多渠道催款通知 const channels = ['短信', '微信', '电话']; const channelText = channels.join('、'); this.snackBar.open( `✅ 催款提醒已通过${channelText}发送给客户\n项目:${settlement.projectName}\n金额:¥${settlement.amount}`, '关闭', { duration: 5000, horizontalPosition: 'center', verticalPosition: 'top' } ); // 记录催款历史 console.log('催款记录:', { settlementId: settlement.id, projectName: settlement.projectName, amount: settlement.amount, daysOverdue: this.getDaysOverdue(settlement), reminderDate: new Date(), channels: channels }); }, 2000); } // 显示逾期处理选项 private async showOverdueOptions(settlement: Settlement): Promise { const option = await window?.fmode?.input(` 逾期处理选项: 1 - 延期7天 2 - 延期15天 3 - 延期30天 4 - 标记为已结算 5 - 取消结算 请输入选项编号(1-5): `.trim()); switch(option) { case '1': this.extendSettlementDeadline(settlement, 7); break; case '2': this.extendSettlementDeadline(settlement, 15); break; case '3': this.extendSettlementDeadline(settlement, 30); break; case '4': this.markSettlementAsCompleted(settlement); break; case '5': this.cancelSettlement(settlement); break; default: if (option !== null) { this.snackBar.open('无效的选项', '关闭', { duration: 2000, horizontalPosition: 'center', verticalPosition: 'top' }); } } } // 延长结算期限 private extendSettlementDeadline(settlement: Settlement, days: number): void { const currentDueDate = settlement.dueDate || new Date(); const newDeadline = new Date(currentDueDate); newDeadline.setDate(newDeadline.getDate() + days); settlement.dueDate = newDeadline; settlement.status = '待结算'; this.snackBar.open( `✅ 已将结算期限延长${days}天\n新期限:${newDeadline.toLocaleDateString()}`, '关闭', { duration: 4000, horizontalPosition: 'center', verticalPosition: 'top' } ); console.log('延期记录:', { settlementId: settlement.id, projectName: settlement.projectName, extendedDays: days, newDeadline: newDeadline }); } // 标记为已结算 private async markSettlementAsCompleted(settlement: Settlement): Promise { const reason = await window?.fmode?.input('请输入标记为已结算的原因:'); if (reason && reason.trim()) { settlement.status = '已结算'; settlement.paidDate = new Date(); this.snackBar.open( `✅ 已标记为已结算\n项目:${settlement.projectName}\n原因:${reason}`, '关闭', { duration: 4000, horizontalPosition: 'center', verticalPosition: 'top' } ); console.log('手动结算记录:', { settlementId: settlement.id, projectName: settlement.projectName, reason: reason, completedDate: new Date() }); } } // 取消结算 private async cancelSettlement(settlement: Settlement): Promise { const reason = await window?.fmode?.input('请输入取消结算的原因:'); if (reason && reason.trim()) { if (await window?.fmode?.confirm(`确定要取消此结算吗?\n项目:${settlement.projectName}\n金额:¥${settlement.amount}`)) { settlement.status = '已取消'; this.snackBar.open( `✅ 已取消结算\n项目:${settlement.projectName}\n原因:${reason}`, '关闭', { duration: 4000, horizontalPosition: 'center', verticalPosition: 'top' } ); console.log('取消结算记录:', { settlementId: settlement.id, projectName: settlement.projectName, reason: reason, cancelledDate: new Date() }); } } } // 发送提醒 sendReminder(settlementId: string): void { const settlement = this.settlements.find(s => s.id === settlementId); if (settlement) { this.createCustomerServiceReminder(settlement, 'payment_follow_up'); } } // 处理单个结算自动化 processSettlementAutomation(settlement: Settlement): void { this.processingSettlementId.set(settlement.id); this.isProcessing.set(true); this.autoSettlementService.processSettlementAutomation(settlement).subscribe({ next: (processed) => { if (processed) { console.log(`结算 ${settlement.id} 已自动处理`); } this.processingSettlementId.set(null); this.isProcessing.set(false); }, error: (error) => { console.error('自动化处理失败:', error); this.processingSettlementId.set(null); this.isProcessing.set(false); } }); } // 项目验收确认相关方法 isProjectAccepted(projectId: string): boolean { return this.projectAcceptances.some(acceptance => acceptance.projectId === projectId && acceptance.acceptanceStatus === 'confirmed' ); } // 客服跟进提醒相关方法 hasCustomerServiceReminder(projectId: string): boolean { return this.customerServiceReminders.some(reminder => reminder.projectId === projectId && reminder.reminderStatus === 'completed' ); } // 企业微信发图相关方法 canSendToWeChat(projectId: string): boolean { return this.isProjectAccepted(projectId) && !this.isSendingImagesForProject(projectId); } // 批量处理自动化 processAllAutomation(): void { const pendingSettlements = this.settlements.filter( s => s.status === '待结算' && !this.isOverdue(s) ); this.isProcessing.set(true); // 模拟批量处理 let processedCount = 0; const processNext = () => { if (processedCount >= pendingSettlements.length) { this.isProcessing.set(false); return; } const settlement = pendingSettlements[processedCount]; this.processingSettlementId.set(settlement.id); this.autoSettlementService.processSettlementAutomation(settlement).subscribe({ next: () => { processedCount++; this.processingSettlementId.set(null); setTimeout(processNext, 500); // 添加延迟以避免同时处理过多 }, error: () => { processedCount++; this.processingSettlementId.set(null); setTimeout(processNext, 500); } }); }; processNext(); } // 计算统计数据 stats = computed(() => { const settlements = this.settlements || []; return { totalAmount: settlements.reduce((sum, s) => sum + (s.amount || 0), 0), pendingAmount: settlements.filter(s => s.status === '待结算').reduce((sum, s) => sum + (s.amount || 0), 0), overdueAmount: settlements.filter(s => this.isOverdue(s)).reduce((sum, s) => sum + (s.amount || 0), 0), completedAmount: settlements.filter(s => s.status === '已结算').reduce((sum, s) => sum + (s.amount || 0), 0), totalCount: settlements.length, pendingCount: settlements.filter(s => s.status === '待结算').length, overdueCount: settlements.filter(s => this.isOverdue(s)).length, completedCount: settlements.filter(s => s.status === '已结算').length }; }); // 筛选后的结算列表 filteredSettlements = computed(() => { let filtered = this.settlements || []; // 状态筛选 const status = this.statusFilter(); if (status !== 'all') { if (status === 'overdue') { filtered = filtered.filter(s => this.isOverdue(s)); } else { filtered = filtered.filter(s => s.status === status); } } // 关键词搜索 const keyword = this.searchKeyword().toLowerCase(); if (keyword) { filtered = filtered.filter(s => (s.projectName || '').toLowerCase().includes(keyword) || (s.stage || '').toLowerCase().includes(keyword) ); } return filtered; }); // 判断是否逾期 isOverdue(settlement: Settlement): boolean { if (settlement.status === '已结算') return false; // 简化逾期判断:如果是待结算状态且创建时间超过30天,则认为逾期 const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); return settlement.createdAt < thirtyDaysAgo; } // 获取状态样式类 getStatusClass(settlement: Settlement): string { if (this.isOverdue(settlement)) return 'overdue'; return settlement.status === '已结算' ? 'completed' : 'pending'; } // 格式化金额 formatAmount(amount: number): string { return new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(amount); } // 更新筛选条件 updateStatusFilter(status: string): void { this.statusFilter.set(status); } updateSearchKeyword(keyword: string): void { this.searchKeyword.set(keyword); } onSearchInput(event: Event): void { const target = event.target as HTMLInputElement; if (target) { this.updateSearchKeyword(target.value); } } // 计算逾期天数 getDaysOverdue(settlement: Settlement): number { if (settlement.status === '已结算') return 0; const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); if (settlement.createdAt >= thirtyDaysAgo) return 0; const today = new Date(); const diffTime = today.getTime() - thirtyDaysAgo.getTime(); return Math.max(0, Math.ceil(diffTime / (1000 * 60 * 60 * 24))); } // 验收完成后自动发起尾款结算申请 private initiateFinalSettlement(contextSettlement: Settlement): void { // 查找当前项目的“尾款结算”记录 this.projectService.getSettlements().subscribe(settlements => { const finalSettlement = settlements.find(s => s.projectId === contextSettlement.projectId && s.stage === '尾款结算'); if (finalSettlement) { // 标记为已发起(更新时间)并触发自动化处理 finalSettlement.createdAt = new Date(); this.snackBar.open(`已自动发起尾款结算申请:${finalSettlement.projectName}`, '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top' }); // 触发自动化处理(提醒/折扣/自动确认等) this.processSettlementAutomation(finalSettlement); } else { // 若不存在尾款结算记录,则创建一条默认记录并发起 const newSettlement: Settlement = { id: `s${Date.now()}`, projectId: contextSettlement.projectId, projectName: contextSettlement.projectName, stage: '尾款结算', amount: 0, percentage: 100, status: '待结算', createdAt: new Date() }; this.projectService.addSettlement(newSettlement).subscribe(() => { this.snackBar.open(`已创建并自动发起尾款结算申请:${newSettlement.projectName}`, '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top' }); this.processSettlementAutomation(newSettlement); }); } }); } }