import { Component, Input, computed, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; export interface ComplaintItem { id: string; type: string; description: string; submitTime: any; submittedAt?: Date; resolvedAt?: Date; status: string; response?: string; handlerComment?: string; handlerName?: string; solution?: string; customerName?: string; priority?: string; images?: string[]; // 新增字段 source?: 'manual' | 'wechat_auto' | 'keyword_monitor'; // 投诉来源 wechatGroupName?: string; // 企业微信群名称 wechatMessageId?: string; // 微信消息ID keywordMatched?: string[]; // 匹配的关键词 autoTagged?: boolean; // 是否自动标注 urgencyLevel?: 'normal' | 'urgent' | 'critical'; // 紧急程度 assignedTo?: string; // 分配给谁处理 estimatedResolutionTime?: Date; // 预计解决时间 actualResolutionTime?: Date; // 实际解决时间 customerSatisfaction?: number; // 客户满意度评分 followUpRequired?: boolean; // 是否需要后续跟进 relatedProjectId?: string; // 关联项目ID escalationLevel?: number; // 升级级别 (0-3) tags?: string[]; // 标签 } interface ComplaintStats { totalCount: number; pendingCount: number; processingCount: number; resolvedCount: number; highPriorityCount: number; averageResolutionTime: number; priorityStats: { [key: string]: number }; typeStats: { [key: string]: number }; // 新增统计字段 sourceStats: { [key: string]: number }; urgencyStats: { [key: string]: number }; escalationStats: { [key: string]: number }; autoTaggedCount: number; wechatComplaintCount: number; keywordMonitorCount: number; overdueCount: number; followUpRequiredCount: number; } @Component({ selector: 'app-complaint-card', standalone: true, imports: [CommonModule, FormsModule], templateUrl: './complaint-card.html', styleUrls: ['./complaint-card.scss'] }) export class ComplaintCardComponent { @Input() complaints: ComplaintItem[] = []; // 筛选条件 statusFilter = signal('all'); priorityFilter = signal('all'); typeFilter = signal('all'); searchKeyword = signal(''); sourceFilter = signal('all'); urgencyFilter = signal('all'); // 新增缺失的属性 monitoredGroups = 5; // 监控群组数量 todayDetections = 3; // 今日检测数量 // 详情弹窗状态 showDetailModal = signal(false); selectedComplaint = signal(null); // 添加标签弹窗状态 showTagModal = signal(false); tagModalComplaint = signal(null); newTagInput = signal(''); // 预设标签列表 presetTags = [ '质量问题', '服务态度', '延期交付', '沟通不畅', '设计不满', '价格争议', '技术问题', '售后服务', '紧急处理', '重要客户', '需要跟进', '已协商' ]; // 紧急投诉计算属性 urgentComplaints = computed(() => { return this.complaints.filter(complaint => complaint.urgencyLevel === 'urgent' || complaint.urgencyLevel === 'critical' || complaint.priority === 'urgent' || complaint.priority === 'high' ); }); // 投诉来源选项 complaintSources = [ { value: 'manual', label: '人工创建', icon: '👥' }, { value: 'wechat_auto', label: '微信自动', icon: '🤖' }, { value: 'keyword_monitor', label: '关键词监控', icon: '🔍' } ]; // 紧急程度选项 urgencyLevels = [ { value: 'normal', label: '普通', color: '#52c41a' }, { value: 'urgent', label: '紧急', color: '#faad14' }, { value: 'critical', label: '严重', color: '#f5222d' } ]; // 关键词监控配置 monitorKeywords = ['不满意', '投诉', '退款', '差评', '问题', '延期', '质量差']; // 实时代办项状态 realTimeTaskStatus = signal<'idle' | 'processing' | 'completed'>('idle'); processingTaskId = signal(''); // 投诉类型 complaintTypes = [ { value: 'quality', label: '质量问题' }, { value: 'service', label: '服务态度' }, { value: 'delivery', label: '交付延期' }, { value: 'communication', label: '沟通问题' }, { value: 'design', label: '设计不满意' }, { value: 'other', label: '其他问题' } ]; // 优先级选项 priorities = [ { value: 'low', label: '低', color: '#52c41a' }, { value: 'medium', label: '中', color: '#faad14' }, { value: 'high', label: '高', color: '#f5222d' }, { value: 'urgent', label: '紧急', color: '#722ed1' } ]; // 计算统计数据 stats = computed(() => { const complaints = this.complaints || []; const totalCount = complaints.length; const pendingCount = complaints.filter(c => c.status === '待处理').length; const processingCount = complaints.filter(c => c.status === '处理中').length; const resolvedCount = complaints.filter(c => c.status === '已解决').length; const highPriorityCount = complaints.filter(c => (c as any).priority === 'high' || (c as any).priority === 'urgent').length; // 计算平均解决时间(天) const resolvedComplaints = complaints.filter(c => c.status === '已解决' && (c as any).resolvedAt && (c as any).createdAt); const averageResolutionTime = resolvedComplaints.length > 0 ? resolvedComplaints.reduce((sum, c) => { const days = Math.ceil((new Date((c as any).resolvedAt).getTime() - new Date((c as any).createdAt).getTime()) / (1000 * 60 * 60 * 24)); return sum + days; }, 0) / resolvedComplaints.length : 0; // 优先级统计 const priorityStats: { [key: string]: number } = {}; this.priorities.forEach(p => { priorityStats[p.value] = complaints.filter(c => (c as any).priority === p.value).length; }); // 类型统计 const typeStats: { [key: string]: number } = {}; this.complaintTypes.forEach(t => { typeStats[t.value] = complaints.filter(c => this.getComplaintType(c) === t.value).length; }); // 来源统计 const sourceStats: { [key: string]: number } = {}; this.complaintSources.forEach(s => { sourceStats[s.value] = complaints.filter(c => c.source === s.value).length; }); // 紧急程度统计 const urgencyStats: { [key: string]: number } = {}; this.urgencyLevels.forEach(u => { urgencyStats[u.value] = complaints.filter(c => c.urgencyLevel === u.value).length; }); // 升级级别统计 const escalationStats: { [key: string]: number } = {}; for (let i = 0; i <= 3; i++) { escalationStats[i.toString()] = complaints.filter(c => c.escalationLevel === i).length; } // 其他统计 const autoTaggedCount = complaints.filter(c => c.autoTagged === true).length; const wechatComplaintCount = complaints.filter(c => c.source === 'wechat_auto').length; const keywordMonitorCount = complaints.filter(c => c.source === 'keyword_monitor').length; const overdueCount = complaints.filter(c => this.isOverdue(c)).length; const followUpRequiredCount = complaints.filter(c => c.followUpRequired === true).length; return { totalCount, pendingCount, processingCount, resolvedCount, highPriorityCount, averageResolutionTime: Math.round(averageResolutionTime * 10) / 10, priorityStats, typeStats, sourceStats, urgencyStats, escalationStats, autoTaggedCount, wechatComplaintCount, keywordMonitorCount, overdueCount, followUpRequiredCount }; }); // 筛选后的投诉列表 filteredComplaints = computed(() => { let filtered = this.complaints || []; // 状态筛选 if (this.statusFilter() !== 'all') { const statusMap: { [key: string]: string } = { 'pending': '待处理', 'processing': '处理中', 'resolved': '已解决' }; const targetStatus = statusMap[this.statusFilter()] || this.statusFilter(); filtered = filtered.filter(complaint => complaint.status === targetStatus); } // 优先级筛选 if (this.priorityFilter() !== 'all') { filtered = filtered.filter(complaint => (complaint as any).priority === this.priorityFilter()); } // 类型筛选 if (this.typeFilter() !== 'all') { filtered = filtered.filter(complaint => this.getComplaintType(complaint) === this.typeFilter()); } // 来源筛选 if (this.sourceFilter() !== 'all') { filtered = filtered.filter(complaint => complaint.source === this.sourceFilter()); } // 紧急程度筛选 if (this.urgencyFilter() !== 'all') { filtered = filtered.filter(complaint => complaint.urgencyLevel === this.urgencyFilter()); } // 关键词搜索 if (this.searchKeyword()) { const keyword = this.searchKeyword().toLowerCase(); filtered = filtered.filter(complaint => complaint.description?.toLowerCase().includes(keyword) || (complaint as any).customerName?.toLowerCase().includes(keyword) || complaint.type?.toLowerCase().includes(keyword) || complaint.wechatGroupName?.toLowerCase().includes(keyword) || complaint.tags?.some(tag => tag.toLowerCase().includes(keyword)) || complaint.keywordMatched?.some(kw => kw.toLowerCase().includes(keyword)) ); } return filtered; }); // 获取投诉类型 getComplaintType(complaint: any): string { if (complaint.type) return complaint.type; // 根据描述内容推断类型 const description = complaint.description?.toLowerCase() || ''; if (description.includes('质量') || description.includes('缺陷')) return 'quality'; if (description.includes('服务') || description.includes('态度')) return 'service'; if (description.includes('延期') || description.includes('时间')) return 'delivery'; if (description.includes('沟通') || description.includes('联系')) return 'communication'; if (description.includes('设计') || description.includes('效果')) return 'design'; return 'other'; } // 获取类型标签 getTypeLabel(type: string): string { const typeObj = this.complaintTypes.find(t => t.value === type); return typeObj ? typeObj.label : '其他'; } // 获取优先级信息 getPriorityInfo(priority: string) { return this.priorities.find(p => p.value === priority) || this.priorities[0]; } // 获取状态样式类 getStatusClass(complaint: any): string { switch (complaint.status) { case '待处理': return 'pending'; case '处理中': return 'processing'; case '已解决': return 'resolved'; default: return 'pending'; } } // 获取优先级样式类 getPriorityClass(priority: string): string { switch (priority) { case 'low': return 'priority-low'; case 'medium': return 'priority-medium'; case 'high': return 'priority-high'; case 'urgent': return 'priority-urgent'; default: return 'priority-low'; } } // 获取处理天数(修复NaN问题) getDaysInProgress(complaint: ComplaintItem): number { if (!complaint.submittedAt) { return 0; } const submittedDate = new Date(complaint.submittedAt); const currentDate = complaint.resolvedAt ? new Date(complaint.resolvedAt) : new Date(); // 检查日期是否有效 if (isNaN(submittedDate.getTime()) || isNaN(currentDate.getTime())) { return 0; } const diffTime = Math.abs(currentDate.getTime() - submittedDate.getTime()); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays; } // 开始处理投诉 startProcessing(complaint: ComplaintItem): void { // 更新投诉状态为处理中 complaint.status = '处理中'; // 可以在这里添加更多逻辑,比如分配处理人员等 console.log('开始处理投诉:', complaint.id); // 这里可以调用服务来更新后端数据 // this.complaintService.updateComplaintStatus(complaint.id, '处理中'); } // 完成处理投诉 completeProcessing(complaint: ComplaintItem): void { // 更新投诉状态为已解决 complaint.status = '已解决'; complaint.resolvedAt = new Date(); // 可以在这里添加处理结果的逻辑 console.log('完成处理投诉:', complaint.id); // 这里可以调用服务来更新后端数据 // this.complaintService.completeComplaint(complaint.id, resolvedData); } // 查看投诉详情 viewDetails(complaint: ComplaintItem): void { console.log('查看投诉详情:', complaint.id); this.selectedComplaint.set(complaint); this.showDetailModal.set(true); } // 关闭详情弹窗 closeDetailModal(): void { this.showDetailModal.set(false); this.selectedComplaint.set(null); } // 判断是否超时 isOverdue(complaint: any): boolean { if (complaint.status === '已解决') return false; const daysInProgress = this.getDaysInProgress(complaint); const maxDays = complaint.priority === 'urgent' ? 1 : complaint.priority === 'high' ? 3 : complaint.priority === 'medium' ? 7 : 14; return daysInProgress > maxDays; } // 更新筛选条件 updateStatusFilter(status: string | any) { if (typeof status === 'string') { this.statusFilter.set(status); } else { this.statusFilter.set(status.target.value); } } updatePriorityFilter(event: any) { this.priorityFilter.set(event.target.value); } updateTypeFilter(event: any) { this.typeFilter.set(event.target.value); } updateSearchKeyword(keyword: string) { this.searchKeyword.set(keyword); } updateSourceFilter(event: any) { this.sourceFilter.set(event.target.value); } updateUrgencyFilter(event: any) { this.urgencyFilter.set(event.target.value); } // ==================== 新增投诉管理功能 ==================== // 创建实时代办项 createRealTimeTask(complaint: ComplaintItem): void { this.realTimeTaskStatus.set('processing'); this.processingTaskId.set(complaint.id); console.log('创建实时代办项:', complaint.id); // 模拟异步处理 setTimeout(() => { complaint.status = '处理中'; complaint.assignedTo = '当前用户'; // 实际应该从用户服务获取 this.realTimeTaskStatus.set('completed'); // 这里可以调用服务来创建实时代办项 // this.complaintService.createRealTimeTask(complaint); }, 2000); } // 企业微信自动获取投诉 startWeChatMonitoring(): void { console.log('开始企业微信投诉监控'); // 这里可以调用企业微信API服务 // this.wechatService.startComplaintMonitoring(this.monitorKeywords); // 模拟自动获取到的投诉 const autoComplaint: ComplaintItem = { id: 'wechat_' + Date.now(), type: 'service', description: '客户在微信群中表达不满意,提到"服务态度不好"', status: '待处理', source: 'wechat_auto', wechatGroupName: '项目交流群', wechatMessageId: 'msg_' + Date.now(), keywordMatched: ['不满意', '服务态度'], autoTagged: true, urgencyLevel: 'urgent', priority: 'high', submitTime: new Date(), submittedAt: new Date(), customerName: '张先生', followUpRequired: true, escalationLevel: 1 }; // 添加到投诉列表 this.complaints.push(autoComplaint); alert('检测到新的微信投诉,已自动创建投诉记录'); } // 设置关键词监控 setupKeywordMonitoring(): void { console.log('设置关键词监控:', this.monitorKeywords); // 这里可以打开关键词设置弹窗 const newKeywords = prompt('请输入监控关键词(用逗号分隔):', this.monitorKeywords.join(',')); if (newKeywords) { this.monitorKeywords = newKeywords.split(',').map(k => k.trim()).filter(k => k); console.log('更新关键词监控:', this.monitorKeywords); // 这里可以调用服务更新关键词配置 // this.complaintService.updateMonitorKeywords(this.monitorKeywords); } } // 分配投诉处理人 assignComplaint(complaint: ComplaintItem, assignee: string): void { complaint.assignedTo = assignee; complaint.status = '处理中'; console.log('分配投诉处理人:', complaint.id, '→', assignee); // 这里可以调用服务更新分配信息 // this.complaintService.assignComplaint(complaint.id, assignee); } // 升级投诉 escalateComplaint(complaint: ComplaintItem): void { if (!complaint.escalationLevel) { complaint.escalationLevel = 0; } if (complaint.escalationLevel < 3) { complaint.escalationLevel++; complaint.urgencyLevel = complaint.escalationLevel >= 2 ? 'critical' : 'urgent'; complaint.priority = 'urgent'; console.log('投诉升级:', complaint.id, '升级到级别', complaint.escalationLevel); // 这里可以调用服务处理升级逻辑 // this.complaintService.escalateComplaint(complaint.id, complaint.escalationLevel); } } // 模拟从企业微信获取新投诉 simulateNewWeChatComplaint(): void { const newComplaint: ComplaintItem = { id: 'wechat_' + Date.now(), type: 'quality', description: '客户在微信群中反映图纸质量问题,要求重新制作', submitTime: new Date(), submittedAt: new Date(), status: '待处理', customerName: '张先生', priority: 'urgent', source: 'wechat_auto', wechatGroupName: '项目交付群-张先生', wechatMessageId: 'msg_' + Date.now(), keywordMatched: ['质量差', '重新制作'], autoTagged: true, urgencyLevel: 'urgent', followUpRequired: true, relatedProjectId: 'proj_001', escalationLevel: 1, tags: ['质量问题', '微信投诉', '紧急处理'] }; // 添加到投诉列表 this.complaints.push(newComplaint); console.log('检测到新的微信投诉:', newComplaint); } // 实时处理代办项 processRealTimeTask(taskId: string): void { this.processingTaskId.set(taskId); this.realTimeTaskStatus.set('processing'); console.log('开始处理实时代办项:', taskId); // 模拟处理过程 setTimeout(() => { this.realTimeTaskStatus.set('completed'); this.processingTaskId.set(''); console.log('实时代办项处理完成'); }, 3000); } // 通知升级 notifyEscalation(complaint: ComplaintItem): void { console.log('发送升级通知:', complaint.id); // 这里可以实现邮件、短信或企业微信通知 } // 设置预计解决时间 setEstimatedResolutionTime(complaint: ComplaintItem, hours: number): void { const estimatedTime = new Date(); estimatedTime.setHours(estimatedTime.getHours() + hours); complaint.estimatedResolutionTime = estimatedTime; console.log('设置预计解决时间:', complaint.id, estimatedTime); } // 客户满意度评分 setCustomerSatisfaction(complaint: ComplaintItem, score: number): void { complaint.customerSatisfaction = Math.max(1, Math.min(5, score)); console.log('客户满意度评分:', complaint.id, score); } // 批量处理投诉 batchProcessComplaints(complaintIds: string[], action: string): void { console.log('批量处理投诉:', complaintIds, action); complaintIds.forEach(id => { const complaint = this.complaints.find(c => c.id === id); if (complaint) { switch (action) { case 'assign': // 批量分配 break; case 'tag': // 批量添加标签 break; case 'escalate': // 批量升级 this.escalateComplaint(complaint); break; } } }); } // 设置后续跟进 setFollowUpRequired(complaint: ComplaintItem, required: boolean): void { complaint.followUpRequired = required; console.log('设置后续跟进:', complaint.id, '→', required); // 这里可以调用服务更新跟进状态 // this.complaintService.setFollowUpRequired(complaint.id, required); } // 获取投诉来源信息 getComplaintSourceInfo(complaint: ComplaintItem) { const source = this.complaintSources.find(s => s.value === complaint.source); return source || { value: 'manual', label: '人工创建', icon: '👥' }; } // 获取紧急程度信息 getUrgencyInfo(complaint: ComplaintItem) { const urgency = this.urgencyLevels.find(u => u.value === complaint.urgencyLevel); return urgency || { value: 'normal', label: '普通', color: '#52c41a' }; } // 检查是否为自动标注 isAutoTagged(complaint: ComplaintItem): boolean { return complaint.autoTagged === true; } // 获取升级级别显示 getEscalationDisplay(level: number): string { const levels = ['普通', '一级', '二级', '三级']; return levels[level] || '普通'; } // 确认客户评价完成 confirmCustomerReviewComplete(complaint: ComplaintItem): void { // 更新投诉状态 complaint.status = '已解决'; complaint.resolvedAt = new Date(); console.log('确认客户评价完成:', complaint.id); // 这里可以调用服务来更新后端数据 // this.complaintService.confirmCustomerReview(complaint.id); } // 确认投诉解决完成 confirmComplaintResolutionComplete(complaint: ComplaintItem): void { console.log('确认投诉解决完成:', complaint.id); // 这里可以添加确认投诉解决完成的逻辑 // 例如:更新状态、发送通知等 complaint.status = 'resolved'; complaint.actualResolutionTime = new Date(); } // 打开添加标签弹窗 addComplaintTag(complaint: ComplaintItem, tag?: string): void { if (tag) { // 如果直接传入标签,直接添加 if (!complaint.tags) { complaint.tags = []; } if (!complaint.tags.includes(tag)) { complaint.tags.push(tag); console.log(`为投诉 ${complaint.id} 添加标签: ${tag}`); } } else { // 否则打开标签弹窗 this.tagModalComplaint.set(complaint); this.showTagModal.set(true); this.newTagInput.set(''); } } // 关闭标签弹窗 closeTagModal(): void { this.showTagModal.set(false); this.tagModalComplaint.set(null); this.newTagInput.set(''); } // 选择预设标签 selectPresetTag(tag: string): void { const complaint = this.tagModalComplaint(); if (!complaint) return; if (!complaint.tags) { complaint.tags = []; } if (!complaint.tags.includes(tag)) { complaint.tags.push(tag); console.log(`为投诉 ${complaint.id} 添加预设标签: ${tag}`); } } // 添加自定义标签 addCustomTag(): void { const complaint = this.tagModalComplaint(); const newTag = this.newTagInput().trim(); if (!complaint || !newTag) return; if (!complaint.tags) { complaint.tags = []; } if (!complaint.tags.includes(newTag)) { complaint.tags.push(newTag); console.log(`为投诉 ${complaint.id} 添加自定义标签: ${newTag}`); this.newTagInput.set(''); } } // 移除标签 removeTag(complaint: ComplaintItem, tag: string): void { if (!complaint.tags) return; const index = complaint.tags.indexOf(tag); if (index > -1) { complaint.tags.splice(index, 1); console.log(`从投诉 ${complaint.id} 移除标签: ${tag}`); } } }