operations-schedule-status.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { ScheduleAppointment } from '../admin.models';
  2. export type ScheduleStatusTone =
  3. | 'status-upcoming'
  4. | 'status-live'
  5. | 'status-coach-ended'
  6. | 'status-confirmed'
  7. | 'status-completed'
  8. | 'status-archived'
  9. | 'status-overdue'
  10. | 'status-cancelled'
  11. | 'status-neutral';
  12. export type ScheduleStatusFilter =
  13. | 'all'
  14. | 'upcoming'
  15. | 'live'
  16. | 'coach-ended'
  17. | 'confirmed'
  18. | 'completed'
  19. | 'archived'
  20. | 'overdue'
  21. | 'cancelled'
  22. | 'unassigned';
  23. const scheduleStatusLabels: Partial<Record<number, string>> = {
  24. 0: '待开始',
  25. 10: '上课中',
  26. 11: '陪练已结束',
  27. 20: '学员已确认',
  28. 30: '已完课',
  29. 40: '已归档',
  30. };
  31. export function scheduleStatusTone(
  32. item: Pick<ScheduleAppointment, 'cancelled' | 'statusCode' | 'endsAt'>,
  33. now = Date.now(),
  34. ): ScheduleStatusTone {
  35. if (item.cancelled) return 'status-cancelled';
  36. const endTime = Date.parse(item.endsAt);
  37. if ((item.statusCode === 0 || item.statusCode === 10) && Number.isFinite(endTime) && endTime < now) return 'status-overdue';
  38. if (item.statusCode === 0) return 'status-upcoming';
  39. if (item.statusCode === 10) return 'status-live';
  40. if (item.statusCode === 11) return 'status-coach-ended';
  41. if (item.statusCode === 20) return 'status-confirmed';
  42. if (item.statusCode === 30) return 'status-completed';
  43. if (item.statusCode === 40) return 'status-archived';
  44. return 'status-neutral';
  45. }
  46. export function scheduleStatusLabel(
  47. item: Pick<ScheduleAppointment, 'cancelled' | 'statusCode' | 'statusLabel' | 'endsAt'>,
  48. now = Date.now(),
  49. ): string {
  50. const tone = scheduleStatusTone(item, now);
  51. if (tone === 'status-cancelled') return '已取消';
  52. if (tone === 'status-overdue') return '逾期未完成';
  53. return scheduleStatusLabels[item.statusCode] || item.statusLabel || `状态 ${item.statusCode}`;
  54. }
  55. export function scheduleMatchesStatus(
  56. item: Pick<ScheduleAppointment, 'cancelled' | 'statusCode' | 'endsAt' | 'coachId' | 'coachAccountMissing'>,
  57. filter: ScheduleStatusFilter,
  58. now = Date.now(),
  59. ): boolean {
  60. if (filter === 'all') return true;
  61. if (filter === 'unassigned') return !item.coachId || item.coachAccountMissing === true;
  62. return scheduleStatusTone(item, now) === `status-${filter}`;
  63. }