listing-ai-workbench.component.ts 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637
  1. import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. import { ActivatedRoute, Router, RouterLink } from '@angular/router';
  5. import { RUNTIME_CONFIG } from '../../../../app/core/config/runtime-config';
  6. import { PageHeaderComponent } from '../../../shared/components/page-header/page-header.component';
  7. import { SummaryMetricCardComponent } from '../../../shared/components/summary-metric-card/summary-metric-card.component';
  8. import { ContentCardComponent } from '../../../shared/components/content-card/content-card.component';
  9. import { ListingAiApiService } from '../../services/listing-ai-api.service';
  10. import { ListingAiWorkbenchStore } from '../../services/listing-ai-workbench.store';
  11. @Component({selector:'app-listing-ai-workbench',standalone:true,imports:[CommonModule,FormsModule,RouterLink,PageHeaderComponent,SummaryMetricCardComponent,ContentCardComponent],providers:[ListingAiWorkbenchStore],changeDetection:ChangeDetectionStrategy.OnPush,templateUrl:'./listing-ai-workbench.component.html',styleUrl:'../../listing-ai.shared.scss'})
  12. export class ListingAiWorkbenchComponent implements OnInit{
  13. readonly store=inject(ListingAiWorkbenchStore);private readonly api=inject(ListingAiApiService);private readonly route=inject(ActivatedRoute);private readonly router=inject(Router);readonly workspaceId=RUNTIME_CONFIG.domesticWorkspaceId;
  14. search='';scoreStatus='';coverageStatus='';sort='productId';readonly creating=signal(false);readonly actionError=signal('');readonly selected=new Set<string>();
  15. ngOnInit():void{const query=this.route.snapshot.queryParamMap;this.search=query.get('search')??'';this.scoreStatus=query.get('scoreStatus')??'';this.coverageStatus=query.get('coverageStatus')??'';this.sort=query.get('sort')??'productId';this.apply(false);}
  16. apply(updateUrl=true):void{this.store.patchFilters({search:this.search,scoreStatus:this.scoreStatus||undefined,coverageStatus:this.coverageStatus||undefined,sort:this.sort});if(updateUrl)void this.router.navigate([],{relativeTo:this.route,queryParams:{search:this.search||null,scoreStatus:this.scoreStatus||null,coverageStatus:this.coverageStatus||null,sort:this.sort==='productId'?null:this.sort},queryParamsHandling:'merge',replaceUrl:true});this.store.load(this.workspaceId);}
  17. toggle(id:string,checked:boolean):void{checked?this.selected.add(id):this.selected.delete(id);}
  18. runAiScore():void{
  19. if(!this.selected.size)return;
  20. if(this.selected.size>10){this.actionError.set('本轮 Listing AI 评分最多支持 10 个商品,请减少选择数量。');return;}
  21. this.creating.set(true);
  22. this.actionError.set('');
  23. const scope={mode:'selected' as const,productIds:[...this.selected]};
  24. this.api.createScoreJob(this.workspaceId,scope,'ai').subscribe({
  25. next:({job})=>void this.router.navigate(['/listing-ai/tasks',job.id]),
  26. error:(error)=>{
  27. this.creating.set(false);
  28. const code=error?.error?.error;
  29. this.actionError.set(code==='listing_ai_budget_exceeded'?'本轮 Listing AI 评分最多支持 10 个商品。':code||'任务创建失败');
  30. },
  31. complete:()=>this.creating.set(false),
  32. });
  33. }
  34. score(value:number|null):string{return value===null?'—':String(value);}
  35. scoreSource(score:{scoreKind?:string;rubricVersion:string}|null):string{return score?.scoreKind==='hybrid_ai'||score?.rubricVersion.startsWith('listing-jd-ai-')?'AI评分':'规则评分';}
  36. }