|
@@ -8,6 +8,22 @@ import { OperationsService } from '../operations.service';
|
|
|
const localDateKey=(value:Date):string=>`${value.getFullYear()}-${String(value.getMonth()+1).padStart(2,'0')}-${String(value.getDate()).padStart(2,'0')}`;
|
|
const localDateKey=(value:Date):string=>`${value.getFullYear()}-${String(value.getMonth()+1).padStart(2,'0')}-${String(value.getDate()).padStart(2,'0')}`;
|
|
|
|
|
|
|
|
type SearchField='member'|'course'|'coach';
|
|
type SearchField='member'|'course'|'coach';
|
|
|
|
|
+export type ScheduleStatusTone='status-upcoming'|'status-live'|'status-completed'|'status-archived'|'status-overdue'|'status-cancelled'|'status-neutral';
|
|
|
|
|
+
|
|
|
|
|
+export function scheduleStatusTone(item:Pick<ScheduleAppointment,'cancelled'|'statusCode'|'endsAt'>,now=Date.now()):ScheduleStatusTone{
|
|
|
|
|
+ if(item.cancelled)return 'status-cancelled';
|
|
|
|
|
+ const endTime=Date.parse(item.endsAt);
|
|
|
|
|
+ if(item.statusCode===0&&Number.isFinite(endTime)&&endTime<now)return 'status-overdue';
|
|
|
|
|
+ if(item.statusCode===0)return 'status-upcoming';
|
|
|
|
|
+ if(item.statusCode===10)return 'status-live';
|
|
|
|
|
+ if(item.statusCode===11||item.statusCode===20)return 'status-completed';
|
|
|
|
|
+ if(item.statusCode>=30)return 'status-archived';
|
|
|
|
|
+ return 'status-neutral';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export function scheduleStatusLabel(item:Pick<ScheduleAppointment,'cancelled'|'statusCode'|'statusLabel'|'endsAt'>,now=Date.now()):string{
|
|
|
|
|
+ return scheduleStatusTone(item,now)==='status-overdue'?'逾期未上课':item.statusLabel;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
export function matchesScheduleSearch(query:string,values:Array<string|number|undefined|null>):boolean{
|
|
export function matchesScheduleSearch(query:string,values:Array<string|number|undefined|null>):boolean{
|
|
|
const normalizedQuery=query.trim().toLocaleLowerCase();
|
|
const normalizedQuery=query.trim().toLocaleLowerCase();
|
|
@@ -35,6 +51,8 @@ export class OperationsScheduleComponent implements OnInit {
|
|
|
readonly filteredCoaches=computed(()=>(this.data()?.coaches||[]).filter(coach=>matchesScheduleSearch(this.coachSearch(),[coach.displayName,coach.mobile,coach.mobileMasked,coach.userId])).slice(0,12));
|
|
readonly filteredCoaches=computed(()=>(this.data()?.coaches||[]).filter(coach=>matchesScheduleSearch(this.coachSearch(),[coach.displayName,coach.mobile,coach.mobileMasked,coach.userId])).slice(0,12));
|
|
|
private initialRange=true;
|
|
private initialRange=true;
|
|
|
previewConflictCount():number{return Number(this.preview()?.['conflictCount']||0);}
|
|
previewConflictCount():number{return Number(this.preview()?.['conflictCount']||0);}
|
|
|
|
|
+ statusTone(item:ScheduleAppointment):ScheduleStatusTone{return scheduleStatusTone(item);}
|
|
|
|
|
+ statusDisplayLabel(item:ScheduleAppointment):string{return scheduleStatusLabel(item);}
|
|
|
ngOnInit():void{void this.load();}
|
|
ngOnInit():void{void this.load();}
|
|
|
range():{from:string;to:string}{const from=new Date(`${this.date()}T00:00:00`);const to=new Date(from);if(this.view()==='week')to.setDate(to.getDate()+6);return{from:localDateKey(from),to:localDateKey(to)};}
|
|
range():{from:string;to:string}{const from=new Date(`${this.date()}T00:00:00`);const to=new Date(from);if(this.view()==='week')to.setDate(to.getDate()+6);return{from:localDateKey(from),to:localDateKey(to)};}
|
|
|
async load():Promise<void>{this.loading.set(true);this.error.set('');try{const range=this.range();const result=await this.operations.schedule(range.from,range.to,this.coachFilter());if(this.initialRange&&!result.appointments.length&&result.dataDateTo&&result.dataDateTo!==this.date()){this.initialRange=false;this.date.set(result.dataDateTo);const latestRange=this.range();this.data.set(await this.operations.schedule(latestRange.from,latestRange.to,this.coachFilter()));this.message.set(`当前日期没有排课,已自动定位到最近有数据的 ${result.dataDateTo}`);}else{this.initialRange=false;this.data.set(result);}}catch(error){this.error.set(error instanceof Error?error.message:'排课数据加载失败');}finally{this.loading.set(false);}}
|
|
async load():Promise<void>{this.loading.set(true);this.error.set('');try{const range=this.range();const result=await this.operations.schedule(range.from,range.to,this.coachFilter());if(this.initialRange&&!result.appointments.length&&result.dataDateTo&&result.dataDateTo!==this.date()){this.initialRange=false;this.date.set(result.dataDateTo);const latestRange=this.range();this.data.set(await this.operations.schedule(latestRange.from,latestRange.to,this.coachFilter()));this.message.set(`当前日期没有排课,已自动定位到最近有数据的 ${result.dataDateTo}`);}else{this.initialRange=false;this.data.set(result);}}catch(error){this.error.set(error instanceof Error?error.message:'排课数据加载失败');}finally{this.loading.set(false);}}
|