complaint-card.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. import { Component, Input, computed, signal } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. export interface ComplaintItem {
  5. id: string;
  6. type: string;
  7. description: string;
  8. submitTime: any;
  9. submittedAt?: Date;
  10. resolvedAt?: Date;
  11. status: string;
  12. response?: string;
  13. handlerComment?: string;
  14. handlerName?: string;
  15. solution?: string;
  16. customerName?: string;
  17. priority?: string;
  18. images?: string[];
  19. // 新增字段
  20. source?: 'manual' | 'wechat_auto' | 'keyword_monitor'; // 投诉来源
  21. wechatGroupName?: string; // 企业微信群名称
  22. wechatMessageId?: string; // 微信消息ID
  23. keywordMatched?: string[]; // 匹配的关键词
  24. autoTagged?: boolean; // 是否自动标注
  25. urgencyLevel?: 'normal' | 'urgent' | 'critical'; // 紧急程度
  26. assignedTo?: string; // 分配给谁处理
  27. estimatedResolutionTime?: Date; // 预计解决时间
  28. actualResolutionTime?: Date; // 实际解决时间
  29. customerSatisfaction?: number; // 客户满意度评分
  30. followUpRequired?: boolean; // 是否需要后续跟进
  31. relatedProjectId?: string; // 关联项目ID
  32. escalationLevel?: number; // 升级级别 (0-3)
  33. tags?: string[]; // 标签
  34. }
  35. interface ComplaintStats {
  36. totalCount: number;
  37. pendingCount: number;
  38. processingCount: number;
  39. resolvedCount: number;
  40. highPriorityCount: number;
  41. averageResolutionTime: number;
  42. priorityStats: { [key: string]: number };
  43. typeStats: { [key: string]: number };
  44. // 新增统计字段
  45. sourceStats: { [key: string]: number };
  46. urgencyStats: { [key: string]: number };
  47. escalationStats: { [key: string]: number };
  48. autoTaggedCount: number;
  49. wechatComplaintCount: number;
  50. keywordMonitorCount: number;
  51. overdueCount: number;
  52. followUpRequiredCount: number;
  53. }
  54. @Component({
  55. selector: 'app-complaint-card',
  56. standalone: true,
  57. imports: [CommonModule, FormsModule],
  58. templateUrl: './complaint-card.html',
  59. styleUrls: ['./complaint-card.scss']
  60. })
  61. export class ComplaintCardComponent {
  62. @Input() complaints: ComplaintItem[] = [];
  63. // 筛选条件
  64. statusFilter = signal<string>('all');
  65. priorityFilter = signal<string>('all');
  66. typeFilter = signal<string>('all');
  67. searchKeyword = signal<string>('');
  68. sourceFilter = signal<string>('all');
  69. urgencyFilter = signal<string>('all');
  70. // 新增缺失的属性
  71. monitoredGroups = 5; // 监控群组数量
  72. todayDetections = 3; // 今日检测数量
  73. // 详情弹窗状态
  74. showDetailModal = signal<boolean>(false);
  75. selectedComplaint = signal<ComplaintItem | null>(null);
  76. // 添加标签弹窗状态
  77. showTagModal = signal<boolean>(false);
  78. tagModalComplaint = signal<ComplaintItem | null>(null);
  79. newTagInput = signal<string>('');
  80. // 预设标签列表
  81. presetTags = [
  82. '质量问题', '服务态度', '延期交付', '沟通不畅',
  83. '设计不满', '价格争议', '技术问题', '售后服务',
  84. '紧急处理', '重要客户', '需要跟进', '已协商'
  85. ];
  86. // 紧急投诉计算属性
  87. urgentComplaints = computed(() => {
  88. return this.complaints.filter(complaint =>
  89. complaint.urgencyLevel === 'urgent' ||
  90. complaint.urgencyLevel === 'critical' ||
  91. complaint.priority === 'urgent' ||
  92. complaint.priority === 'high'
  93. );
  94. });
  95. // 投诉来源选项
  96. complaintSources = [
  97. { value: 'manual', label: '人工创建', icon: '👥' },
  98. { value: 'wechat_auto', label: '微信自动', icon: '🤖' },
  99. { value: 'keyword_monitor', label: '关键词监控', icon: '🔍' }
  100. ];
  101. // 紧急程度选项
  102. urgencyLevels = [
  103. { value: 'normal', label: '普通', color: '#52c41a' },
  104. { value: 'urgent', label: '紧急', color: '#faad14' },
  105. { value: 'critical', label: '严重', color: '#f5222d' }
  106. ];
  107. // 关键词监控配置
  108. monitorKeywords = ['不满意', '投诉', '退款', '差评', '问题', '延期', '质量差'];
  109. // 实时代办项状态
  110. realTimeTaskStatus = signal<'idle' | 'processing' | 'completed'>('idle');
  111. processingTaskId = signal<string>('');
  112. // 投诉类型
  113. complaintTypes = [
  114. { value: 'quality', label: '质量问题' },
  115. { value: 'service', label: '服务态度' },
  116. { value: 'delivery', label: '交付延期' },
  117. { value: 'communication', label: '沟通问题' },
  118. { value: 'design', label: '设计不满意' },
  119. { value: 'other', label: '其他问题' }
  120. ];
  121. // 优先级选项
  122. priorities = [
  123. { value: 'low', label: '低', color: '#52c41a' },
  124. { value: 'medium', label: '中', color: '#faad14' },
  125. { value: 'high', label: '高', color: '#f5222d' },
  126. { value: 'urgent', label: '紧急', color: '#722ed1' }
  127. ];
  128. // 计算统计数据
  129. stats = computed<ComplaintStats>(() => {
  130. const complaints = this.complaints || [];
  131. const totalCount = complaints.length;
  132. const pendingCount = complaints.filter(c => c.status === '待处理').length;
  133. const processingCount = complaints.filter(c => c.status === '处理中').length;
  134. const resolvedCount = complaints.filter(c => c.status === '已解决').length;
  135. const highPriorityCount = complaints.filter(c => (c as any).priority === 'high' || (c as any).priority === 'urgent').length;
  136. // 计算平均解决时间(天)
  137. const resolvedComplaints = complaints.filter(c => c.status === '已解决' && (c as any).resolvedAt && (c as any).createdAt);
  138. const averageResolutionTime = resolvedComplaints.length > 0
  139. ? resolvedComplaints.reduce((sum, c) => {
  140. const days = Math.ceil((new Date((c as any).resolvedAt).getTime() - new Date((c as any).createdAt).getTime()) / (1000 * 60 * 60 * 24));
  141. return sum + days;
  142. }, 0) / resolvedComplaints.length
  143. : 0;
  144. // 优先级统计
  145. const priorityStats: { [key: string]: number } = {};
  146. this.priorities.forEach(p => {
  147. priorityStats[p.value] = complaints.filter(c => (c as any).priority === p.value).length;
  148. });
  149. // 类型统计
  150. const typeStats: { [key: string]: number } = {};
  151. this.complaintTypes.forEach(t => {
  152. typeStats[t.value] = complaints.filter(c => this.getComplaintType(c) === t.value).length;
  153. });
  154. // 来源统计
  155. const sourceStats: { [key: string]: number } = {};
  156. this.complaintSources.forEach(s => {
  157. sourceStats[s.value] = complaints.filter(c => c.source === s.value).length;
  158. });
  159. // 紧急程度统计
  160. const urgencyStats: { [key: string]: number } = {};
  161. this.urgencyLevels.forEach(u => {
  162. urgencyStats[u.value] = complaints.filter(c => c.urgencyLevel === u.value).length;
  163. });
  164. // 升级级别统计
  165. const escalationStats: { [key: string]: number } = {};
  166. for (let i = 0; i <= 3; i++) {
  167. escalationStats[i.toString()] = complaints.filter(c => c.escalationLevel === i).length;
  168. }
  169. // 其他统计
  170. const autoTaggedCount = complaints.filter(c => c.autoTagged === true).length;
  171. const wechatComplaintCount = complaints.filter(c => c.source === 'wechat_auto').length;
  172. const keywordMonitorCount = complaints.filter(c => c.source === 'keyword_monitor').length;
  173. const overdueCount = complaints.filter(c => this.isOverdue(c)).length;
  174. const followUpRequiredCount = complaints.filter(c => c.followUpRequired === true).length;
  175. return {
  176. totalCount,
  177. pendingCount,
  178. processingCount,
  179. resolvedCount,
  180. highPriorityCount,
  181. averageResolutionTime: Math.round(averageResolutionTime * 10) / 10,
  182. priorityStats,
  183. typeStats,
  184. sourceStats,
  185. urgencyStats,
  186. escalationStats,
  187. autoTaggedCount,
  188. wechatComplaintCount,
  189. keywordMonitorCount,
  190. overdueCount,
  191. followUpRequiredCount
  192. };
  193. });
  194. // 筛选后的投诉列表
  195. filteredComplaints = computed(() => {
  196. let filtered = this.complaints || [];
  197. // 状态筛选
  198. if (this.statusFilter() !== 'all') {
  199. const statusMap: { [key: string]: string } = {
  200. 'pending': '待处理',
  201. 'processing': '处理中',
  202. 'resolved': '已解决'
  203. };
  204. const targetStatus = statusMap[this.statusFilter()] || this.statusFilter();
  205. filtered = filtered.filter(complaint => complaint.status === targetStatus);
  206. }
  207. // 优先级筛选
  208. if (this.priorityFilter() !== 'all') {
  209. filtered = filtered.filter(complaint => (complaint as any).priority === this.priorityFilter());
  210. }
  211. // 类型筛选
  212. if (this.typeFilter() !== 'all') {
  213. filtered = filtered.filter(complaint => this.getComplaintType(complaint) === this.typeFilter());
  214. }
  215. // 来源筛选
  216. if (this.sourceFilter() !== 'all') {
  217. filtered = filtered.filter(complaint => complaint.source === this.sourceFilter());
  218. }
  219. // 紧急程度筛选
  220. if (this.urgencyFilter() !== 'all') {
  221. filtered = filtered.filter(complaint => complaint.urgencyLevel === this.urgencyFilter());
  222. }
  223. // 关键词搜索
  224. if (this.searchKeyword()) {
  225. const keyword = this.searchKeyword().toLowerCase();
  226. filtered = filtered.filter(complaint =>
  227. complaint.description?.toLowerCase().includes(keyword) ||
  228. (complaint as any).customerName?.toLowerCase().includes(keyword) ||
  229. complaint.type?.toLowerCase().includes(keyword) ||
  230. complaint.wechatGroupName?.toLowerCase().includes(keyword) ||
  231. complaint.tags?.some(tag => tag.toLowerCase().includes(keyword)) ||
  232. complaint.keywordMatched?.some(kw => kw.toLowerCase().includes(keyword))
  233. );
  234. }
  235. return filtered;
  236. });
  237. // 获取投诉类型
  238. getComplaintType(complaint: any): string {
  239. if (complaint.type) return complaint.type;
  240. // 根据描述内容推断类型
  241. const description = complaint.description?.toLowerCase() || '';
  242. if (description.includes('质量') || description.includes('缺陷')) return 'quality';
  243. if (description.includes('服务') || description.includes('态度')) return 'service';
  244. if (description.includes('延期') || description.includes('时间')) return 'delivery';
  245. if (description.includes('沟通') || description.includes('联系')) return 'communication';
  246. if (description.includes('设计') || description.includes('效果')) return 'design';
  247. return 'other';
  248. }
  249. // 获取类型标签
  250. getTypeLabel(type: string): string {
  251. const typeObj = this.complaintTypes.find(t => t.value === type);
  252. return typeObj ? typeObj.label : '其他';
  253. }
  254. // 获取优先级信息
  255. getPriorityInfo(priority: string) {
  256. return this.priorities.find(p => p.value === priority) || this.priorities[0];
  257. }
  258. // 获取状态样式类
  259. getStatusClass(complaint: any): string {
  260. switch (complaint.status) {
  261. case '待处理': return 'pending';
  262. case '处理中': return 'processing';
  263. case '已解决': return 'resolved';
  264. default: return 'pending';
  265. }
  266. }
  267. // 获取优先级样式类
  268. getPriorityClass(priority: string): string {
  269. switch (priority) {
  270. case 'low': return 'priority-low';
  271. case 'medium': return 'priority-medium';
  272. case 'high': return 'priority-high';
  273. case 'urgent': return 'priority-urgent';
  274. default: return 'priority-low';
  275. }
  276. }
  277. // 获取处理天数(修复NaN问题)
  278. getDaysInProgress(complaint: ComplaintItem): number {
  279. if (!complaint.submittedAt) {
  280. return 0;
  281. }
  282. const submittedDate = new Date(complaint.submittedAt);
  283. const currentDate = complaint.resolvedAt ? new Date(complaint.resolvedAt) : new Date();
  284. // 检查日期是否有效
  285. if (isNaN(submittedDate.getTime()) || isNaN(currentDate.getTime())) {
  286. return 0;
  287. }
  288. const diffTime = Math.abs(currentDate.getTime() - submittedDate.getTime());
  289. const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
  290. return diffDays;
  291. }
  292. // 开始处理投诉
  293. startProcessing(complaint: ComplaintItem): void {
  294. // 更新投诉状态为处理中
  295. complaint.status = '处理中';
  296. // 可以在这里添加更多逻辑,比如分配处理人员等
  297. console.log('开始处理投诉:', complaint.id);
  298. // 这里可以调用服务来更新后端数据
  299. // this.complaintService.updateComplaintStatus(complaint.id, '处理中');
  300. }
  301. // 完成处理投诉
  302. completeProcessing(complaint: ComplaintItem): void {
  303. // 更新投诉状态为已解决
  304. complaint.status = '已解决';
  305. complaint.resolvedAt = new Date();
  306. // 可以在这里添加处理结果的逻辑
  307. console.log('完成处理投诉:', complaint.id);
  308. // 这里可以调用服务来更新后端数据
  309. // this.complaintService.completeComplaint(complaint.id, resolvedData);
  310. }
  311. // 查看投诉详情
  312. viewDetails(complaint: ComplaintItem): void {
  313. console.log('查看投诉详情:', complaint.id);
  314. this.selectedComplaint.set(complaint);
  315. this.showDetailModal.set(true);
  316. }
  317. // 关闭详情弹窗
  318. closeDetailModal(): void {
  319. this.showDetailModal.set(false);
  320. this.selectedComplaint.set(null);
  321. }
  322. // 判断是否超时
  323. isOverdue(complaint: any): boolean {
  324. if (complaint.status === '已解决') return false;
  325. const daysInProgress = this.getDaysInProgress(complaint);
  326. const maxDays = complaint.priority === 'urgent' ? 1 :
  327. complaint.priority === 'high' ? 3 :
  328. complaint.priority === 'medium' ? 7 : 14;
  329. return daysInProgress > maxDays;
  330. }
  331. // 更新筛选条件
  332. updateStatusFilter(status: string | any) {
  333. if (typeof status === 'string') {
  334. this.statusFilter.set(status);
  335. } else {
  336. this.statusFilter.set(status.target.value);
  337. }
  338. }
  339. updatePriorityFilter(event: any) {
  340. this.priorityFilter.set(event.target.value);
  341. }
  342. updateTypeFilter(event: any) {
  343. this.typeFilter.set(event.target.value);
  344. }
  345. updateSearchKeyword(keyword: string) {
  346. this.searchKeyword.set(keyword);
  347. }
  348. updateSourceFilter(event: any) {
  349. this.sourceFilter.set(event.target.value);
  350. }
  351. updateUrgencyFilter(event: any) {
  352. this.urgencyFilter.set(event.target.value);
  353. }
  354. // ==================== 新增投诉管理功能 ====================
  355. // 创建实时代办项
  356. createRealTimeTask(complaint: ComplaintItem): void {
  357. this.realTimeTaskStatus.set('processing');
  358. this.processingTaskId.set(complaint.id);
  359. console.log('创建实时代办项:', complaint.id);
  360. // 模拟异步处理
  361. setTimeout(() => {
  362. complaint.status = '处理中';
  363. complaint.assignedTo = '当前用户'; // 实际应该从用户服务获取
  364. this.realTimeTaskStatus.set('completed');
  365. // 这里可以调用服务来创建实时代办项
  366. // this.complaintService.createRealTimeTask(complaint);
  367. }, 2000);
  368. }
  369. // 企业微信自动获取投诉
  370. startWeChatMonitoring(): void {
  371. console.log('开始企业微信投诉监控');
  372. // 这里可以调用企业微信API服务
  373. // this.wechatService.startComplaintMonitoring(this.monitorKeywords);
  374. // 模拟自动获取到的投诉
  375. const autoComplaint: ComplaintItem = {
  376. id: 'wechat_' + Date.now(),
  377. type: 'service',
  378. description: '客户在微信群中表达不满意,提到"服务态度不好"',
  379. status: '待处理',
  380. source: 'wechat_auto',
  381. wechatGroupName: '项目交流群',
  382. wechatMessageId: 'msg_' + Date.now(),
  383. keywordMatched: ['不满意', '服务态度'],
  384. autoTagged: true,
  385. urgencyLevel: 'urgent',
  386. priority: 'high',
  387. submitTime: new Date(),
  388. submittedAt: new Date(),
  389. customerName: '张先生',
  390. followUpRequired: true,
  391. escalationLevel: 1
  392. };
  393. // 添加到投诉列表
  394. this.complaints.push(autoComplaint);
  395. alert('检测到新的微信投诉,已自动创建投诉记录');
  396. }
  397. // 设置关键词监控
  398. setupKeywordMonitoring(): void {
  399. console.log('设置关键词监控:', this.monitorKeywords);
  400. // 这里可以打开关键词设置弹窗
  401. const newKeywords = prompt('请输入监控关键词(用逗号分隔):', this.monitorKeywords.join(','));
  402. if (newKeywords) {
  403. this.monitorKeywords = newKeywords.split(',').map(k => k.trim()).filter(k => k);
  404. console.log('更新关键词监控:', this.monitorKeywords);
  405. // 这里可以调用服务更新关键词配置
  406. // this.complaintService.updateMonitorKeywords(this.monitorKeywords);
  407. }
  408. }
  409. // 分配投诉处理人
  410. assignComplaint(complaint: ComplaintItem, assignee: string): void {
  411. complaint.assignedTo = assignee;
  412. complaint.status = '处理中';
  413. console.log('分配投诉处理人:', complaint.id, '→', assignee);
  414. // 这里可以调用服务更新分配信息
  415. // this.complaintService.assignComplaint(complaint.id, assignee);
  416. }
  417. // 升级投诉
  418. escalateComplaint(complaint: ComplaintItem): void {
  419. if (!complaint.escalationLevel) {
  420. complaint.escalationLevel = 0;
  421. }
  422. if (complaint.escalationLevel < 3) {
  423. complaint.escalationLevel++;
  424. complaint.urgencyLevel = complaint.escalationLevel >= 2 ? 'critical' : 'urgent';
  425. complaint.priority = 'urgent';
  426. console.log('投诉升级:', complaint.id, '升级到级别', complaint.escalationLevel);
  427. // 这里可以调用服务处理升级逻辑
  428. // this.complaintService.escalateComplaint(complaint.id, complaint.escalationLevel);
  429. }
  430. }
  431. // 模拟从企业微信获取新投诉
  432. simulateNewWeChatComplaint(): void {
  433. const newComplaint: ComplaintItem = {
  434. id: 'wechat_' + Date.now(),
  435. type: 'quality',
  436. description: '客户在微信群中反映图纸质量问题,要求重新制作',
  437. submitTime: new Date(),
  438. submittedAt: new Date(),
  439. status: '待处理',
  440. customerName: '张先生',
  441. priority: 'urgent',
  442. source: 'wechat_auto',
  443. wechatGroupName: '项目交付群-张先生',
  444. wechatMessageId: 'msg_' + Date.now(),
  445. keywordMatched: ['质量差', '重新制作'],
  446. autoTagged: true,
  447. urgencyLevel: 'urgent',
  448. followUpRequired: true,
  449. relatedProjectId: 'proj_001',
  450. escalationLevel: 1,
  451. tags: ['质量问题', '微信投诉', '紧急处理']
  452. };
  453. // 添加到投诉列表
  454. this.complaints.push(newComplaint);
  455. console.log('检测到新的微信投诉:', newComplaint);
  456. }
  457. // 实时处理代办项
  458. processRealTimeTask(taskId: string): void {
  459. this.processingTaskId.set(taskId);
  460. this.realTimeTaskStatus.set('processing');
  461. console.log('开始处理实时代办项:', taskId);
  462. // 模拟处理过程
  463. setTimeout(() => {
  464. this.realTimeTaskStatus.set('completed');
  465. this.processingTaskId.set('');
  466. console.log('实时代办项处理完成');
  467. }, 3000);
  468. }
  469. // 通知升级
  470. notifyEscalation(complaint: ComplaintItem): void {
  471. console.log('发送升级通知:', complaint.id);
  472. // 这里可以实现邮件、短信或企业微信通知
  473. }
  474. // 设置预计解决时间
  475. setEstimatedResolutionTime(complaint: ComplaintItem, hours: number): void {
  476. const estimatedTime = new Date();
  477. estimatedTime.setHours(estimatedTime.getHours() + hours);
  478. complaint.estimatedResolutionTime = estimatedTime;
  479. console.log('设置预计解决时间:', complaint.id, estimatedTime);
  480. }
  481. // 客户满意度评分
  482. setCustomerSatisfaction(complaint: ComplaintItem, score: number): void {
  483. complaint.customerSatisfaction = Math.max(1, Math.min(5, score));
  484. console.log('客户满意度评分:', complaint.id, score);
  485. }
  486. // 批量处理投诉
  487. batchProcessComplaints(complaintIds: string[], action: string): void {
  488. console.log('批量处理投诉:', complaintIds, action);
  489. complaintIds.forEach(id => {
  490. const complaint = this.complaints.find(c => c.id === id);
  491. if (complaint) {
  492. switch (action) {
  493. case 'assign':
  494. // 批量分配
  495. break;
  496. case 'tag':
  497. // 批量添加标签
  498. break;
  499. case 'escalate':
  500. // 批量升级
  501. this.escalateComplaint(complaint);
  502. break;
  503. }
  504. }
  505. });
  506. }
  507. // 设置后续跟进
  508. setFollowUpRequired(complaint: ComplaintItem, required: boolean): void {
  509. complaint.followUpRequired = required;
  510. console.log('设置后续跟进:', complaint.id, '→', required);
  511. // 这里可以调用服务更新跟进状态
  512. // this.complaintService.setFollowUpRequired(complaint.id, required);
  513. }
  514. // 获取投诉来源信息
  515. getComplaintSourceInfo(complaint: ComplaintItem) {
  516. const source = this.complaintSources.find(s => s.value === complaint.source);
  517. return source || { value: 'manual', label: '人工创建', icon: '👥' };
  518. }
  519. // 获取紧急程度信息
  520. getUrgencyInfo(complaint: ComplaintItem) {
  521. const urgency = this.urgencyLevels.find(u => u.value === complaint.urgencyLevel);
  522. return urgency || { value: 'normal', label: '普通', color: '#52c41a' };
  523. }
  524. // 检查是否为自动标注
  525. isAutoTagged(complaint: ComplaintItem): boolean {
  526. return complaint.autoTagged === true;
  527. }
  528. // 获取升级级别显示
  529. getEscalationDisplay(level: number): string {
  530. const levels = ['普通', '一级', '二级', '三级'];
  531. return levels[level] || '普通';
  532. }
  533. // 确认客户评价完成
  534. confirmCustomerReviewComplete(complaint: ComplaintItem): void {
  535. // 更新投诉状态
  536. complaint.status = '已解决';
  537. complaint.resolvedAt = new Date();
  538. console.log('确认客户评价完成:', complaint.id);
  539. // 这里可以调用服务来更新后端数据
  540. // this.complaintService.confirmCustomerReview(complaint.id);
  541. }
  542. // 确认投诉解决完成
  543. confirmComplaintResolutionComplete(complaint: ComplaintItem): void {
  544. console.log('确认投诉解决完成:', complaint.id);
  545. // 这里可以添加确认投诉解决完成的逻辑
  546. // 例如:更新状态、发送通知等
  547. complaint.status = 'resolved';
  548. complaint.actualResolutionTime = new Date();
  549. }
  550. // 打开添加标签弹窗
  551. addComplaintTag(complaint: ComplaintItem, tag?: string): void {
  552. if (tag) {
  553. // 如果直接传入标签,直接添加
  554. if (!complaint.tags) {
  555. complaint.tags = [];
  556. }
  557. if (!complaint.tags.includes(tag)) {
  558. complaint.tags.push(tag);
  559. console.log(`为投诉 ${complaint.id} 添加标签: ${tag}`);
  560. }
  561. } else {
  562. // 否则打开标签弹窗
  563. this.tagModalComplaint.set(complaint);
  564. this.showTagModal.set(true);
  565. this.newTagInput.set('');
  566. }
  567. }
  568. // 关闭标签弹窗
  569. closeTagModal(): void {
  570. this.showTagModal.set(false);
  571. this.tagModalComplaint.set(null);
  572. this.newTagInput.set('');
  573. }
  574. // 选择预设标签
  575. selectPresetTag(tag: string): void {
  576. const complaint = this.tagModalComplaint();
  577. if (!complaint) return;
  578. if (!complaint.tags) {
  579. complaint.tags = [];
  580. }
  581. if (!complaint.tags.includes(tag)) {
  582. complaint.tags.push(tag);
  583. console.log(`为投诉 ${complaint.id} 添加预设标签: ${tag}`);
  584. }
  585. }
  586. // 添加自定义标签
  587. addCustomTag(): void {
  588. const complaint = this.tagModalComplaint();
  589. const newTag = this.newTagInput().trim();
  590. if (!complaint || !newTag) return;
  591. if (!complaint.tags) {
  592. complaint.tags = [];
  593. }
  594. if (!complaint.tags.includes(newTag)) {
  595. complaint.tags.push(newTag);
  596. console.log(`为投诉 ${complaint.id} 添加自定义标签: ${newTag}`);
  597. this.newTagInput.set('');
  598. }
  599. }
  600. // 移除标签
  601. removeTag(complaint: ComplaintItem, tag: string): void {
  602. if (!complaint.tags) return;
  603. const index = complaint.tags.indexOf(tag);
  604. if (index > -1) {
  605. complaint.tags.splice(index, 1);
  606. console.log(`从投诉 ${complaint.id} 移除标签: ${tag}`);
  607. }
  608. }
  609. }