workload-calendar.html 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <div class="workload-calendar-container">
  2. <header class="page-header" role="navigation" aria-label="工作量日历控制">
  3. <h1>负载日历</h1>
  4. <div class="controls">
  5. <div class="view-switch" role="tablist" aria-label="视图切换">
  6. <button [class.active]="view==='day'" (click)="switchView('day')" role="tab" [attr.aria-selected]="view==='day'" title="日视图">日</button>
  7. <button [class.active]="view==='week'" (click)="switchView('week')" role="tab" [attr.aria-selected]="view==='week'" title="周视图">周</button>
  8. <button [class.active]="view==='month'" (click)="switchView('month')" role="tab" [attr.aria-selected]="view==='month'" title="月视图">月</button>
  9. </div>
  10. <div class="date-nav">
  11. <button (click)="navigateDate('prev')" aria-label="上一周期">‹</button>
  12. <span class="month-label" aria-live="polite">{{ monthLabel }}</span>
  13. <button (click)="navigateDate('next')" aria-label="下一周期">›</button>
  14. <button class="today" (click)="setToday()" title="返回今天">今天</button>
  15. </div>
  16. <div class="filters">
  17. <label for="designerSelect">设计师:</label>
  18. <select id="designerSelect" [(ngModel)]="selectedDesigner" (change)="onDesignerChange()">
  19. <option value="all">全部</option>
  20. @for (d of designers; track d) {
  21. <option [value]="d">{{ d }}</option>
  22. }
  23. </select>
  24. <label class="overdue-toggle" for="overdueOnly">
  25. <input id="overdueOnly" type="checkbox" [(ngModel)]="showOverdueOnly" (change)="onOverdueOnlyChange()" />
  26. 仅看超期
  27. </label>
  28. <!-- 新增:快捷范围按钮 -->
  29. <div class="quick-range" role="group" aria-label="快捷范围">
  30. <button [class.active]="quickRange==='all'" (click)="onQuickRangeSelect('all')">全部</button>
  31. <button [class.active]="quickRange==='today'" (click)="onQuickRangeSelect('today')">今天</button>
  32. <button [class.active]="quickRange==='3d'" (click)="onQuickRangeSelect('3d')">3天内</button>
  33. <button [class.active]="quickRange==='7d'" (click)="onQuickRangeSelect('7d')">7天内</button>
  34. </div>
  35. <!-- 新增:阶段筛选(按5大阶段归并) -->
  36. <label for="phaseSelect">阶段:</label>
  37. <select id="phaseSelect" [(ngModel)]="selectedPhase" (change)="onPhaseChange()">
  38. <option value="all">全部</option>
  39. @for (p of phases; track p) {
  40. <option [value]="p">{{ p }}</option>
  41. }
  42. </select>
  43. </div>
  44. </div>
  45. </header>
  46. @if (view==='month') {
  47. <section class="calendar-section" aria-label="月视图">
  48. <div class="weekdays">
  49. <div class="weekday">一</div>
  50. <div class="weekday">二</div>
  51. <div class="weekday">三</div>
  52. <div class="weekday">四</div>
  53. <div class="weekday">五</div>
  54. <div class="weekday weekend">六</div>
  55. <div class="weekday weekend">日</div>
  56. </div>
  57. <div class="calendar-grid">
  58. @for (d of monthDays; track d.date) {
  59. <div class="day"
  60. [class.other-month]="!d.currentMonth"
  61. [class.today]="isSameDay(d.date, today)"
  62. [class.selected]="isSameDay(d.date, selectedDate)"
  63. [class.weekend]="isWeekend(d.date)"
  64. (click)="selectDate(d.date)"
  65. title="查看 {{ d.date | date:'yyyy-MM-dd' }} 的任务">
  66. <div class="date-label" [title]="d.date | date:'fullDate'" (click)="selectDate(d.date)">{{ d.date | date:'MM/dd' }}</div>
  67. <div class="tasks">
  68. @if (!isExpanded(d.date)) {
  69. @for (t of d.tasks.slice(0,3); track t.id) {
  70. <button class="task-chip" type="button" (click)="navigateToProject(t, $event)" [class.overdue]="t.isOverdue" [class.high]="t.priority==='high'" [class.due-soon]="isDueSoon(t.deadline)" title="进入项目:{{t.projectName}}">
  71. <span class="task-title">{{ t.title }}</span>
  72. <span class="assignee" title="按设计师筛选" (click)="filterByDesigner(t.assignee, $event)">{{ t.assignee }}</span>
  73. </button>
  74. }
  75. @if (d.tasks.length > 3) {
  76. <button class="more-btn" (click)="toggleExpand(d.date, $event)" aria-label="展开更多">+{{ d.tasks.length - 3 }}</button>
  77. }
  78. } @else {
  79. @for (t of d.tasks; track t.id) {
  80. <button class="task-chip" type="button" (click)="navigateToProject(t, $event)" [class.overdue]="t.isOverdue" [class.high]="t.priority==='high'" [class.due-soon]="isDueSoon(t.deadline)" title="进入项目:{{t.projectName}}">
  81. <span class="task-title">{{ t.title }}</span>
  82. <span class="assignee" title="按设计师筛选" (click)="filterByDesigner(t.assignee, $event)">{{ t.assignee }}</span>
  83. </button>
  84. }
  85. <button class="more-btn" (click)="toggleExpand(d.date, $event)" aria-label="收起">收起</button>
  86. }
  87. @if (d.tasks.length === 0) {
  88. <div class="no-task" aria-hidden="true">—</div>
  89. }
  90. </div>
  91. </div>
  92. }
  93. </div>
  94. </section>
  95. }
  96. @if (view==='week') {
  97. <section class="calendar-section" aria-label="周视图">
  98. <div class="week-grid">
  99. @for (d of weekDays; track d.date) {
  100. <div class="week-day" [class.today]="isSameDay(d.date, today)" [class.selected]="isSameDay(d.date, selectedDate)" [class.weekend]="isWeekend(d.date)" (click)="selectDate(d.date)">
  101. <div class="date-label" (click)="selectDate(d.date)">{{ d.date | date:'EEE MM/dd' }}</div>
  102. <div class="tasks">
  103. @for (t of d.tasks; track t.id) {
  104. <button class="task-chip" type="button" (click)="navigateToProject(t, $event)" [class.overdue]="t.isOverdue" [class.high]="t.priority==='high'" [class.due-soon]="isDueSoon(t.deadline)" title="进入项目:{{t.projectName}}">
  105. <span class="task-title">{{ t.title }}</span>
  106. <span class="assignee" title="按设计师筛选" (click)="filterByDesigner(t.assignee, $event)">{{ t.assignee }}</span>
  107. </button>
  108. }
  109. @if (d.tasks.length === 0) {
  110. <div class="no-task" aria-hidden="true">—</div>
  111. }
  112. </div>
  113. </div>
  114. }
  115. </div>
  116. </section>
  117. }
  118. @if (view==='day') {
  119. <section class="calendar-section" aria-label="日视图">
  120. <div class="day-panel">
  121. <div class="date-label large">{{ selectedDate | date:'yyyy-MM-dd EEEE' }}</div>
  122. <div class="tasks">
  123. @for (t of dayTasks; track t.id) {
  124. <div class="task-row" [class.overdue]="t.isOverdue" [class.due-soon]="isDueSoon(t.deadline)" title="{{t.title}} - {{t.projectName}} / {{t.assignee}}" (click)="navigateToProject(t, $event)">
  125. <div class="title">{{ t.title }}</div>
  126. <div class="project">{{ t.projectName }}</div>
  127. <div class="assignee"><button type="button" class="linklike" (click)="filterByDesigner(t.assignee, $event)" title="按设计师筛选">{{ t.assignee }}</button></div>
  128. <div class="priority" [class.high]="t.priority==='high'">{{ t.priority }}</div>
  129. </div>
  130. }
  131. @if (dayTasks.length === 0) {
  132. <div class="no-task">今日暂无任务</div>
  133. }
  134. </div>
  135. </div>
  136. </section>
  137. }
  138. <section class="status-board" aria-label="设计师工作状态">
  139. <h2>设计师工作状态</h2>
  140. <div class="status-list">
  141. @for (s of designerStatuses; track s.name) {
  142. <div class="status-item" [class]="s.cls">
  143. <div class="name">{{ s.name }}</div>
  144. <div class="meta">任务数:{{ s.tasksCount }};超期:{{ s.overdue }}</div>
  145. <div class="label">{{ s.label }}</div>
  146. </div>
  147. }
  148. </div>
  149. </section>
  150. </div>