support-center.component.ts 4.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. import { CommonModule } from '@angular/common';
  2. import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit, signal } from '@angular/core';
  3. import { FormsModule } from '@angular/forms';
  4. import { ActivatedRoute } from '@angular/router';
  5. import { firstValueFrom } from 'rxjs';
  6. import { ApiService } from '../../core/api.service';
  7. import { SessionService } from '../../core/session.service';
  8. import { buildFormSubmission } from '../../core/form-payload';
  9. import { PAGE_BY_PATH } from '../../core/page-catalog';
  10. import { DropdownDirective } from '../../shared/dropdown.directive';
  11. interface Reply {id:string;title:string;content:string;kind:string;submittedAt:string;status:string;response:string;handledAt:string;unread:boolean;readAt:string;}
  12. interface Inbox {items:Reply[];total:number;unread:number;}
  13. @Component({selector:'app-support-center',standalone:true,imports:[CommonModule,FormsModule,DropdownDirective],templateUrl:'./support-center.component.html',styleUrl:'./support-center.component.scss',changeDetection:ChangeDetectionStrategy.OnPush})
  14. export class SupportCenterComponent implements OnInit,OnDestroy {
  15. private readonly api=inject(ApiService);
  16. private readonly session=inject(SessionService);
  17. readonly history=signal(inject(ActivatedRoute).snapshot.queryParamMap.get('view')==='history');
  18. readonly inbox=signal<Inbox|null>(null);
  19. readonly loading=signal(false);readonly sending=signal(false);readonly error=signal('');readonly submitError=signal('');readonly message=signal('');readonly readError=signal('');readonly reading=signal('');readonly expanded=signal('');
  20. name='';content='';requestType='feedback';page=1;readonly pageSize=10;readonly Math=Math;
  21. private revision=0;private timer?: ReturnType<typeof setInterval>;
  22. private readonly refresh=()=>{if(document.visibilityState==='visible'&&!this.reading()&&!this.sending())void this.load(false);};
  23. ngOnInit():void {this.name=this.session.user()?.honeyName||this.session.user()?.userName||'';void this.load();this.timer=setInterval(this.refresh,30000);window.addEventListener('focus',this.refresh);document.addEventListener('visibilitychange',this.refresh);}
  24. ngOnDestroy():void {this.revision++;clearInterval(this.timer);window.removeEventListener('focus',this.refresh);document.removeEventListener('visibilitychange',this.refresh);}
  25. statusLabel(status:string):string{return ({pending:'待处理',processed:'已处理',approved:'已同意休假',rejected:'未同意休假'} as Record<string,string>)[status]||'待处理';}
  26. async load(foreground=true):Promise<void>{
  27. const revision=++this.revision,token=this.session.token();
  28. if(foreground)this.loading.set(true);
  29. try{const {result}=await firstValueFrom(this.api.post<Inbox>('app_support_requests_list',{page:this.page,pageSize:this.pageSize}));if(revision!==this.revision||token!==this.session.token())return;this.inbox.set(result);this.error.set('');}
  30. catch(e){if(revision===this.revision&&token===this.session.token())this.error.set(e instanceof Error?e.message:'回复暂时无法加载,请重试');}
  31. finally{if(revision===this.revision)this.loading.set(false);}
  32. }
  33. showHistory():void{this.history.set(true);void this.load();}
  34. changePage(page:number):void{this.page=page;this.expanded.set('');void this.load();}
  35. async submit():Promise<void>{
  36. if(this.sending())return;
  37. if(!this.name.trim()||!this.content.trim()){this.submitError.set('请填写姓名和内容');return;}
  38. const payload=buildFormSubmission(PAGE_BY_PATH.get('pages/member/feedback')!,{name:this.name,content:this.content.trim(),requestType:this.requestType},{uid:this.session.user()?.userId})!;
  39. this.sending.set(true);this.submitError.set('');this.message.set('');
  40. try{await firstValueFrom(this.api.post(payload.action,payload.payload));this.content='';this.message.set('提交成功,管理员处理后会在这里回复。');this.history.set(true);this.page=1;await this.load();}
  41. catch(e){this.submitError.set(e instanceof Error?e.message:'提交失败,请重试');}finally{this.sending.set(false);}
  42. }
  43. async viewReply(row:Reply):Promise<void>{
  44. this.expanded.set(row.id);this.readError.set('');if(!row.unread||this.reading())return;
  45. this.revision++;this.loading.set(false);this.reading.set(row.id);const token=this.session.token();
  46. try{await firstValueFrom(this.api.post('app_support_request_read',{id:row.id}));if(token!==this.session.token())return;this.inbox.update(d=>d?{...d,unread:Math.max(0,d.unread-(d.items.some(r=>r.id===row.id&&r.unread)?1:0)),items:d.items.map(r=>r.id===row.id?{...r,unread:false}:r)}:d);}
  47. catch(e){this.readError.set(e instanceof Error?e.message:'已读状态未保存,可重试');}finally{this.reading.set('');}
  48. }
  49. }