|
|
@@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms';
|
|
|
import { OperationsService } from '../operations.service';
|
|
|
interface Difference {id:string;dataset:string;legacyKey:string;student:string;course:string;date:string;fields:string[];current:unknown;fingerprint:string;cosmetic:boolean;resolved:boolean;}
|
|
|
interface Cutoff {month:string;version:string;runId:string;cutoffAt:string;complete:boolean;pending:number;conflicts:Difference[];approved:boolean;monthClosed:boolean;datasets:{model:number;source:number;matched:number}[];}
|
|
|
-@Component({selector:'app-payroll-cutoff',standalone:true,imports:[CommonModule,FormsModule],template:`
|
|
|
+@Component({selector:'app-payroll-cutoff',standalone:true,imports:[CommonModule,FormsModule],host:{'[class.expanded]':'data() !== null || error().length > 0'},template:`
|
|
|
<section class="cutoff" [class.is-collapsed]="!data()&&!error()"><button type="button" (click)="load()" [disabled]="busy()">{{busy()?'核验中…':'核验 '+month()+' 结算来源'}}</button>
|
|
|
@if(error()){<p role="alert">{{error()}}</p>}
|
|
|
@if(data();as d){<h3>{{d.month}} · 工资来源核验</h3><p>旧接口同步截止:{{d.cutoffAt|date:'yyyy-MM-dd HH:mm':'+0800'}}。{{d.monthClosed?'已覆盖整月':'当前仅可生成截止时间内的草稿,整月同步后才能审核'}}</p>
|
|
|
@@ -14,7 +14,7 @@ interface Cutoff {month:string;version:string;runId:string;cutoffAt:string;compl
|
|
|
<label>核验依据<textarea [(ngModel)]="reason" rows="2" placeholder="说明核对依据,至少 2 个字"></textarea></label>
|
|
|
<div class="differences">@for(row of d.conflicts;track row.id){<details><summary>{{row.student||row.legacyKey}} · {{row.course}} · {{row.cosmetic?'不影响计薪':row.resolved?'已确认':'待核对'}}</summary><p>{{sourceName(row.dataset)}}编号 {{row.legacyKey}} · {{row.date||'日期待核对'}}</p><p>以下为新系统当前记录,请与旧系统同编号记录核对后填写依据。</p><dl>@for(field of row.fields;track field){<dt>{{fieldName(field)}}</dt><dd>{{fieldValue(row,field)}}</dd>}@empty{<dt>差异说明</dt><dd>来源没有提供具体字段,请完整核对该记录。</dd>}</dl>@if(!row.resolved&&canConfirm()){<button type="button" [disabled]="busy()||reason.trim().length<2" (click)="confirm(row)">确认此记录保留新系统</button>}</details>}</div>
|
|
|
@if(canConfirm()){<button type="button" [disabled]="busy()||!d.complete||d.pending>0||reason.trim().length<2" (click)="confirm()">确认该月份截止来源</button>}@if(d.approved){<p role="status">来源已确认;数据变化后确认将自动失效。</p>}
|
|
|
-}</section>`,styles:[`.cutoff{padding:20px;border:1px solid #d8e5e1;border-radius:16px;background:#fff;margin:16px 0;color:#28443a}.cutoff.is-collapsed{display:flex;justify-content:flex-end;margin:12px 0 0;padding:0;border:0;background:transparent}button{min-height:44px;padding:8px 16px;border:1px solid #197664;background:#eff8f5;border-radius:8px;color:#155c4e;cursor:pointer}button:disabled{opacity:.5;cursor:default}label{display:grid;gap:8px;margin:12px 0}textarea{padding:10px;border:1px solid #b8ccc4;border-radius:8px}dl{display:grid;grid-template-columns:minmax(100px,1fr) 2fr;gap:8px;margin:12px 0}dt{color:#567168}dd{margin:0;overflow-wrap:anywhere}details{padding:10px 0;border-bottom:1px solid #edf2f0}.differences{max-height:480px;overflow:auto;margin:12px 0}.source{display:inline-block;margin-right:20px}`]})
|
|
|
+}</section>`,styles:[`:host{display:block;flex:0 0 auto;margin-left:auto}:host(.expanded){flex:1 0 100%;margin-left:0}.cutoff{padding:20px;border:1px solid #d8e5e1;border-radius:16px;background:#fff;margin:16px 0;color:#28443a}.cutoff.is-collapsed{display:flex;justify-content:flex-end;margin:0;padding:0;border:0;background:transparent}.cutoff.is-collapsed button{min-height:32px;padding:5px 11px;border-color:#c6d8d1;background:#fff;color:#4c675c;font-size:12px}button{min-height:44px;padding:8px 16px;border:1px solid #197664;background:#eff8f5;border-radius:8px;color:#155c4e;cursor:pointer}button:disabled{opacity:.5;cursor:default}label{display:grid;gap:8px;margin:12px 0}textarea{padding:10px;border:1px solid #b8ccc4;border-radius:8px}dl{display:grid;grid-template-columns:minmax(100px,1fr) 2fr;gap:8px;margin:12px 0}dt{color:#567168}dd{margin:0;overflow-wrap:anywhere}details{padding:10px 0;border-bottom:1px solid #edf2f0}.differences{max-height:480px;overflow:auto;margin:12px 0}.source{display:inline-block;margin-right:20px}`]})
|
|
|
export class PayrollCutoffComponent {
|
|
|
readonly month=input.required<string>();readonly canConfirm=input(false);readonly changed=output<void>();readonly busy=signal(false);readonly error=signal('');readonly data=signal<Cutoff|null>(null);reason='';private readonly ops=inject(OperationsService);
|
|
|
sourceName(value:string):string{return ({'54':'预约','59':'完课','60':'复习'} as Record<string,string>)[value]||'来源记录';}
|