20251024-calendar-month-view-redesign.md 14 KB

设计师日历月视图重新设计

完成时间

2024-10-24

任务概述

重新设计设计师团队分配弹窗中的日历视图,改为月历模式,优化为单屏显示,无需横向滚动,清晰展示所有设计师在每一天的状态。


设计目标 ✅

  1. 月历显示 - 按照传统月历方式显示完整月份
  2. 单屏显示 - 优先在单屏内看完,不需要左右拖拽滑动
  3. 组长标识 - 组长可以被派单,有明显标识(👑图标 + 金黄色背景)
  4. 状态清晰 - 每个设计师在每一天的状态清晰标注
  5. 精美样式 - 现代化、美观的UI设计

实现方案

1. HTML结构重新设计

原有结构(横向滚动表格)

设计师列 | 日期1 | 日期2 | 日期3 | ... | 日期30+
需要横向滚动才能看到全部日期

新结构(标准月历)

<!-- 设计师列表区域 -->
<div class="designers-section">
  <!-- 显示所有设计师信息,组长特殊标记 -->
</div>

<!-- 月历网格 -->
<div class="month-calendar-grid">
  <!-- 星期标题:日 一 二 三 四 五 六 -->
  <div class="weekday-header">...</div>
  
  <!-- 7列 x 5/6行的日期网格 -->
  <div class="dates-grid">
    <div class="day-cell">
      <!-- 日期号 -->
      <div class="day-header">1</div>
      
      <!-- 该日期下所有设计师的状态 -->
      <div class="designers-status-list">
        <div class="designer-status-row">
          <span class="designer-initial">张</span>
          <div class="status-indicators-mini">
            <span class="indicator free">✓</span>
          </div>
        </div>
        <!-- 更多设计师... -->
      </div>
    </div>
    <!-- 更多日期... -->
  </div>
</div>

2. 核心功能实现

2.1 设计师列表区域

特性

  • ✅ 横向排列,自动换行
  • ✅ 显示设计师头像、姓名、状态、工作量
  • ✅ 组长特殊标识:
    • 👑 皇冠图标在头像上
    • "组长"标签
    • 金黄色渐变背景
    • 特殊边框颜色

代码

<div class="designer-item" [class.is-leader]="designer.isLeader">
  <div class="designer-avatar-small">
    <img [src]="designer.avatar" />
    @if (designer.isLeader) {
      <span class="leader-badge-icon" title="组长">👑</span>
    }
  </div>
  <div class="designer-info-compact">
    <div class="designer-name-row">
      <span class="name">{{ designer.name }}</span>
      @if (designer.isLeader) {
        <span class="leader-tag">组长</span>
      }
    </div>
    <div class="designer-status-row">
      <span class="status-dot" [class]="designer.status"></span>
      <span class="status-label">{{ getStatusText(designer.status) }}</span>
      <span class="workload-label">{{ designer.workload }}%</span>
    </div>
  </div>
</div>

2.2 月历网格布局

特性

  • ✅ 标准7列网格(周日-周六)
  • ✅ 自动行高,最小120px
  • ✅ 今天高亮显示(蓝色边框 + 蓝色背景)
  • ✅ 周末特殊背景色(浅黄色)
  • ✅ 非当前月份日期半透明显示

布局

.month-calendar-grid {
  .weekday-header {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    background: linear-gradient(135deg, #475569 0%, #334155 100%);
  }
  
  .dates-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    grid-auto-rows: minmax(120px, auto);
    gap: 1px;
    background: #e2e8f0; // 网格线颜色
  }
}

2.3 设计师状态显示

每个日期单元格内显示

  • ✅ 所有设计师的首字母(圆形图标)
  • ✅ 状态指示器:
    • 绿色 - 空闲可接单
    • 橙色 - 对图日
    • 红色 - 忙碌中
    • 数字 紫色 - 事件数量

状态颜色编码

.designer-status-row {
  &.available {
    background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
    border-left: 3px solid #10b981; // 绿色
  }
  
  &.review {
    background: linear-gradient(135deg, #fed7aa 0%, #fdba74 100%);
    border-left: 3px solid #f59e0b; // 橙色
  }
  
  &.busy {
    background: linear-gradient(135deg, #fecaca 0%, #fca5a5 100%);
    border-left: 3px solid #ef4444; // 红色
  }
}

3. TypeScript方法实现

新增方法:

// 判断是否是今天
isToday(date: Date): boolean {
  const today = new Date();
  return date.getDate() === today.getDate() &&
         date.getMonth() === today.getMonth() &&
         date.getFullYear() === today.getFullYear();
}

// 判断是否是当前月份
isCurrentMonth(date: Date): boolean {
  return date.getMonth() === this.currentDate.getMonth() &&
         date.getFullYear() === this.currentDate.getFullYear();
}

// 判断是否是周末
isWeekend(date: Date): boolean {
  const day = date.getDay();
  return day === 0 || day === 6; // 0=周日, 6=周六
}

// 获取设计师在指定日期的状态CSS类
getDesignerDayStatusClass(designer: Designer, date: Date): string {
  const classes: string[] = [];
  if (this.isDateAvailable(designer, date)) classes.push('available');
  if (this.isDateReview(designer, date)) classes.push('review');
  if (this.isDateBusy(designer, date)) classes.push('busy');
  return classes.join(' ');
}

// 获取设计师在指定日期的状态标题(hover提示)
getDesignerDayStatusTitle(designer: Designer, date: Date): string {
  const statuses: string[] = [designer.name];
  if (this.isDateAvailable(designer, date)) statuses.push('空闲可接单');
  if (this.isDateReview(designer, date)) statuses.push('对图日');
  if (this.isDateBusy(designer, date)) statuses.push('忙碌中');
  const events = this.getDateEvents(designer, date);
  if (events.length > 0) statuses.push(`${events.length}个事件`);
  return statuses.join(' · ');
}

// 判断设计师在指定日期是否空闲
isDateAvailable(designer: Designer, date: Date): boolean {
  const dateStr = this.formatDateString(date);
  return designer.availableDates?.includes(dateStr) ?? false;
}

// 判断设计师在指定日期是否对图
isDateReview(designer: Designer, date: Date): boolean {
  const dateStr = this.formatDateString(date);
  const events = designer.upcomingEvents || [];
  return events.some(e => e.type === 'review' && 
    this.formatDateString(e.date) === dateStr);
}

// 判断设计师在指定日期是否忙碌
isDateBusy(designer: Designer, date: Date): boolean {
  const events = this.getDateEvents(designer, date);
  return events.some(e => e.type === 'project');
}

// 格式化日期为字符串(用于比较)
formatDateString(date: Date): string {
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, '0');
  const day = date.getDate().toString().padStart(2, '0');
  return `${year}-${month}-${day}`;
}

// 获取工作量CSS类
getWorkloadClass(workload: number): string {
  if (workload >= 80) return 'high';
  if (workload >= 50) return 'medium';
  return 'low';
}

4. 样式设计特点

4.1 组长特殊样式

.designer-item {
  &.is-leader {
    background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
    border-color: #f59e0b;
    
    &:hover {
      border-color: #d97706;
      box-shadow: 0 2px 8px rgba(217, 119, 6, 0.25);
    }
  }
  
  .leader-badge-icon {
    position: absolute;
    top: -6px;
    right: -6px;
    background: #fbbf24;
    border-radius: 50%;
    font-size: 10px; // 👑 图标
  }
  
  .leader-tag {
    padding: 2px 8px;
    background: #fbbf24;
    color: #78350f;
    font-size: 11px;
    font-weight: 600;
    border-radius: 4px;
  }
}

4.2 日期单元格状态

今天

&.is-today {
  background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
  border: 2px solid #3b82f6;
  
  .day-number {
    background: #3b82f6;
    color: #ffffff;
  }
}

周末

&.is-weekend {
  background: #fef9f3; // 浅黄色
  
  .day-number {
    color: #dc2626; // 红色日期号
  }
}

非当前月份

&.not-current-month {
  background: #f8fafc;
  opacity: 0.5; // 半透明
  
  .day-number {
    color: #94a3b8; // 浅灰色
  }
}

4.3 响应式设计

1400px以下

.day-cell {
  min-height: 100px; // 从120px降至100px
  .designers-status-list {
    max-height: 65px; // 从80px降至65px
  }
}

1024px以下

.day-cell {
  min-height: 90px; // 进一步降低
  padding: 4px;
  
  .day-number {
    width: 24px;
    height: 24px;
    font-size: 12px;
  }
  
  .designer-initial {
    width: 16px;
    height: 16px;
    font-size: 9px;
  }
}

功能特性总结

✅ 已实现功能

  1. 月历标准布局

    • 7列(周日-周六)网格
    • 完整显示当前月份所有日期
    • 包含上月末和下月初的部分日期(标准月历)
  2. 组长标识

    • 👑 皇冠图标在头像右上角
    • "组长"文字标签
    • 金黄色渐变背景
    • 特殊边框和悬浮效果
    • 在日历中可正常派单
  3. 单屏显示优化

    • 无需横向滚动
    • 响应式布局适配不同屏幕
    • 自适应行高
    • 紧凑但清晰的信息展示
  4. 状态清晰标注

    • 空闲:绿色 ✓
    • 对图:橙色 ◆
    • 忙碌:红色 ●
    • 事件:紫色数字徽章
    • 渐变背景色区分
  5. 交互体验

    • Hover显示详细信息
    • 日期单元格hover高亮
    • 设计师状态行hover动画
    • 平滑过渡效果
  6. 视觉设计

    • 现代化渐变配色
    • 清晰的视觉层次
    • 圆角卡片设计
    • 柔和阴影效果
    • 色彩编码系统

视觉对比

旧设计(横向滚动表格)

❌ 需要横向滚动
❌ 设计师和日期混在一起
❌ 难以快速查看整月情况
❌ 组长标识不明显

新设计(月历视图)

✅ 单屏显示完整月份
✅ 设计师列表独立展示在上方
✅ 传统月历布局,直观易懂
✅ 组长有明显的👑标识和金色背景
✅ 每天的设计师状态一目了然
✅ 清晰的色彩编码系统

使用指南

查看设计师日历

  1. 识别组长

    • 查看设计师列表区域
    • 带👑图标的是组长
    • 金黄色背景卡片
    • "组长"标签
  2. 查看设计师状态

    • 绿色点 = 空闲可接单
    • 橙色点 = 忙碌
    • 红色点 = 满载
    • 工作量百分比显示
  3. 查看日期状态

    • 找到目标日期
    • 查看该日期下的设计师状态行
    • ✓ = 空闲,◆ = 对图,● = 忙碌
    • 数字徽章 = 事件数量
    • Hover查看详细信息
  4. 派单决策

    • 查看设计师工作量
    • 组长可以接单(标识明显)
    • 查看空闲日期
    • 避开对图日和忙碌日

技术要点

1. CSS Grid布局

.dates-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr); // 7列等宽
  grid-auto-rows: minmax(120px, auto); // 自适应行高
  gap: 1px; // 网格线
}

2. 条件样式类

<div class="day-cell" 
     [class.is-today]="isToday(date)"
     [class.is-weekend]="isWeekend(date)"
     [class.not-current-month]="!isCurrentMonth(date)">

3. 状态判断逻辑

// 综合判断设计师在指定日期的状态
getDesignerDayStatusClass(designer: Designer, date: Date): string {
  // 可能同时有多个状态(如:空闲 + 对图)
  // 返回空格分隔的类名字符串
}

4. 日期格式化

formatDateString(date: Date): string {
  // 统一格式:YYYY-MM-DD
  // 用于日期比较和查找
}

修改的文件

HTML

  • yss-project/src/app/pages/customer-service/consultation-order/components/designer-calendar/designer-calendar.component.html
    • 完全重构calendar-view区域
    • 从横向滚动表格改为7x5/6月历网格
    • 添加设计师列表区域
    • 优化状态显示逻辑

TypeScript

  • yss-project/src/app/pages/customer-service/consultation-order/components/designer-calendar/designer-calendar.component.ts
    • 添加10个新方法支持月历视图
    • isToday(), isCurrentMonth(), isWeekend()
    • getDesignerDayStatusClass(), getDesignerDayStatusTitle()
    • isDateAvailable(), isDateReview(), isDateBusy()
    • formatDateString(), getWorkloadClass()

SCSS

  • yss-project/src/app/pages/customer-service/consultation-order/components/designer-calendar/designer-calendar.component.scss
    • 新增500+行月历视图样式
    • .month-calendar-view 主容器
    • .designers-section 设计师列表样式
    • .month-calendar-grid 月历网格样式
    • 组长特殊样式
    • 状态颜色系统
    • 响应式媒体查询

编译状态

无编译错误无Linter警告TypeScript类型检查通过样式正确编译


测试检查清单

  • 月历正常显示7列(周日-周六)
  • 完整显示当前月份所有日期
  • 今天高亮显示(蓝色边框+背景)
  • 周末特殊背景色(浅黄色)
  • 非当前月份日期半透明
  • 设计师列表正常显示
  • 组长有👑图标标识
  • 组长有金黄色背景
  • 组长有"组长"文字标签
  • 设计师状态点正常显示
  • 工作量百分比正常显示
  • 每个日期下设计师状态列表显示
  • 空闲状态显示绿色✓
  • 对图状态显示橙色◆
  • 忙碌状态显示红色●
  • 事件数量显示紫色徽章
  • Hover显示详细信息
  • 单屏显示无需横向滚动
  • 响应式布局正常
  • 上一月/下一月导航正常

后续优化建议

1. 数据对接

  • 从Parse Server加载真实设计师数据
  • 实时更新设计师工作负载
  • 同步对图日期和事件

2. 功能增强

  • 点击日期单元格快速派单
  • 拖拽设计师到日期进行分配
  • 批量查看设计师空闲时段
  • 导出设计师排期表

3. 性能优化

  • 虚拟滚动(如果设计师很多)
  • 懒加载非当前月份数据
  • 缓存已加载的月份数据

4. 用户体验

  • 添加日期范围选择器
  • 快速跳转到特定月份
  • 设计师筛选和搜索
  • 状态图例悬浮提示

总结

本次重新设计成功将横向滚动的表格视图改造为标准的月历视图,实现了以下核心目标:

单屏显示 - 无需横向滚动即可查看完整月份
组长标识 - 👑图标 + 金色背景,一目了然
状态清晰 - 色彩编码 + 图标系统,快速识别
精美设计 - 现代化UI,渐变配色,流畅动画
响应式 - 适配不同屏幕尺寸

新设计大幅提升了设计师日历的可读性和易用性,为项目分配决策提供了更直观的视觉支持。

项目现已可以正常编译和运行