Browse Source

feat:date.002

0235889 22 hours ago
parent
commit
ca2ba0cb26

+ 1 - 1
manager-web/src/modules/manager/mobile/mobile.routes.ts

@@ -16,7 +16,7 @@ export const MOBILE_ROUTES: Routes = [
       },
       },
       {
       {
         path: 'mine',
         path: 'mine',
-        loadComponent: () => import('./page-mine/page-mine').then(m => m.PageMineComponent)
+        loadComponent: () => import('./page-mine/page-mine').then(m => m.PageMine)
       },
       },
       {
       {
         path: '',
         path: '',

+ 148 - 110
manager-web/src/modules/manager/mobile/page-mine/page-mine.html

@@ -1,127 +1,165 @@
-
-<!-- page-mine.component.html -->
-<div class="container">
-  <header>
-    <div class="logo">
-      <i class="fas fa-heart"></i>
-      <h1>婚庆行业客户全生命周期AI管理系统</h1>
+<div class="calendar-container">
+  <!-- 头部区域 -->
+  <div class="header">
+    <h1><i class="fas fa-calendar-alt"></i> 婚庆AI全周期管理系统 - 场馆日历</h1>
+    <div class="user-info">
+      <button class="btn"><i class="fas fa-user"></i> 管理员</button>
     </div>
     </div>
-    <div class="nav-links">
-      <a href="#" class="active">日历</a>
-      <a href="#">每日工作</a>
-      <a href="#">订单总览</a>
-      <a href="#">访客视图</a>
-      <a href="#">房间安排</a>
+  </div>
+
+  <!-- 统计卡片 -->
+  <div class="stats">
+    <div class="stat-card">
+      <div class="stat-icon">
+        <i class="fas fa-calendar-day fa-2x"></i>
+      </div>
+      <div class="stat-value">{{ stats.totalEvents }}</div>
+      <div class="stat-title">本月预订</div>
     </div>
     </div>
-  </header>
-  
-  <div class="main-content">
-    <div class="calendar-section">
-      <div class="section-header">
-        <h2>场地档期日历</h2>
-        <div class="controls">
-          <div class="date-controls">
-            <button class="btn btn-outline" (click)="changeMonth(-1)"><i class="fas fa-chevron-left"></i></button>
-            <div class="date-display">{{ currentYear }}年{{ currentMonth + 1 }}月</div>
-            <button class="btn btn-outline" (click)="changeMonth(1)"><i class="fas fa-chevron-right"></i></button>
-          </div>
-          <button class="btn btn-primary"><i class="fas fa-search"></i> 查询日期</button>
-        </div>
+    <div class="stat-card">
+      <div class="stat-icon">
+        <i class="fas fa-heart fa-2x"></i>
       </div>
       </div>
-      
-      <div class="weekdays">
-        <div *ngFor="let day of weekdays">{{ day }}</div>
+      <div class="stat-value">{{ stats.weddings }}</div>
+      <div class="stat-title">婚礼预订</div>
+    </div>
+    <div class="stat-card">
+      <div class="stat-icon">
+        <i class="fas fa-utensils fa-2x"></i>
       </div>
       </div>
-      
-      <div class="calendar">
-        <div *ngFor="let day of calendarDays" 
-             class="calendar-day" 
-             [class.today]="day.isToday" 
-             [class.empty]="day.isEmpty">
-          <div *ngIf="!day.isEmpty" class="day-header">
-            <div class="day-num">{{ day.date }}
-              <span *ngIf="day.isToday" class="today-label">今日</span>
-            </div>
-            <div class="lunar-date">{{ day.lunarDate }} {{ day.weekday }}</div>
-          </div>
-          <div *ngIf="!day.isEmpty" class="day-events">
-            <div *ngFor="let event of day.events" 
-                 class="event-tag"
-                 [class.event-booked]="event.type === 'booked'"
-                 [class.event-available]="event.type === 'available'"
-                 [class.event-consult]="event.type === 'consult'">
-              {{ event.venue }}: {{ event.title }}
-            </div>
-          </div>
-        </div>
+      <div class="stat-value">{{ stats.banquets }}</div>
+      <div class="stat-title">宴会预订</div>
+    </div>
+    <div class="stat-card">
+      <div class="stat-icon">
+        <i class="fas fa-users fa-2x"></i>
       </div>
       </div>
+      <div class="stat-value">{{ stats.meetings }}</div>
+      <div class="stat-title">会议预订</div>
+    </div>
+  </div>
+
+  <!-- 控制区域 -->
+  <div class="controls">
+    <div class="date-controls">
+      <button class="btn" (click)="changeMonth(-1)">
+        <i class="fas fa-chevron-left"></i>
+      </button>
+      <div class="date-display">{{ currentYear }}年{{ currentMonth + 1 }}月</div>
+      <button class="btn" (click)="changeMonth(1)">
+        <i class="fas fa-chevron-right"></i>
+      </button>
+      <button class="btn btn-outline" (click)="goToToday()">今天</button>
+    </div>
+    
+    <div class="view-controls">
+      <button 
+        class="view-btn" 
+        [class.active]="viewMode === 'month'"
+        (click)="viewMode = 'month'"
+      >
+        月视图
+      </button>
+      <button 
+        class="view-btn" 
+        [class.active]="viewMode === 'week'"
+        (click)="viewMode = 'week'"
+      >
+        周视图
+      </button>
+    </div>
+  </div>
+
+  <!-- 筛选器 -->
+  <div class="filters">
+    <div 
+      class="filter-item" 
+      [class.active]="activeFilter === 'all'"
+      (click)="setFilter('all')"
+    >
+      <div class="filter-color wedding"></div>
+      <span>全部活动</span>
+    </div>
+    <div 
+      class="filter-item" 
+      [class.active]="activeFilter === 'wedding'"
+      (click)="setFilter('wedding')"
+    >
+      <div class="filter-color wedding"></div>
+      <span>婚礼</span>
+    </div>
+    <div 
+      class="filter-item" 
+      [class.active]="activeFilter === 'banquet'"
+      (click)="setFilter('banquet')"
+    >
+      <div class="filter-color banquet"></div>
+      <span>宴会</span>
+    </div>
+    <div 
+      class="filter-item" 
+      [class.active]="activeFilter === 'meeting'"
+      (click)="setFilter('meeting')"
+    >
+      <div class="filter-color meeting"></div>
+      <span>会议</span>
+    </div>
+    <div 
+      class="filter-item" 
+      [class.active]="activeFilter === 'other'"
+      (click)="setFilter('other')"
+    >
+      <div class="filter-color other"></div>
+      <span>其他</span>
+    </div>
+  </div>
+
+  <!-- 日历区域 -->
+  <div class="calendar">
+    <!-- 月视图 -->
+    <div class="month-view" *ngIf="viewMode === 'month'">
+      <div class="day-header" *ngFor="let day of weekDays">{{ day }}</div>
       
       
-      <div class="ai-section">
-        <div class="ai-header">
-          <i class="fas fa-robot"></i>
-          <h3>AI智能档期助手</h3>
-        </div>
-        
-        <div class="ai-recommendation">
-          <div class="ai-title">
-            <i class="fas fa-lightbulb"></i>
-            <span>今日智能建议</span>
-          </div>
-          <div class="ai-content">
-            <p>根据历史数据分析,本周末(7月5日-6日)为婚礼高峰期,建议:</p>
-            <ul>
-              <li>优先安排资深策划师处理九州厅、月光礼堂的咨询客户</li>
-              <li>星空之镜、海洋之心场地有较高意向客户,可主动跟进</li>
-              <li>塞纳花园连续3个周末空闲,建议推出限时优惠活动</li>
-            </ul>
-            <p><strong>预测:</strong>7月婚礼预订量预计增长25%,建议增加临时服务人员</p>
-          </div>
-        </div>
-        
-        <div class="stats-grid">
-          <div class="stat-card">
-            <div class="stat-value">78%</div>
-            <div class="stat-label">本月场地利用率</div>
-          </div>
-          <div class="stat-card">
-            <div class="stat-value">42</div>
-            <div class="stat-label">待处理咨询</div>
-          </div>
-          <div class="stat-card">
-            <div class="stat-value">15</div>
-            <div class="stat-label">本周婚礼活动</div>
+      <div 
+        class="calendar-day" 
+        [class.other-month]="!day.isCurrentMonth"
+        [class.today]="day.isToday"
+        *ngFor="let day of monthDays"
+      >
+        <div class="day-number">{{ day.date }}</div>
+        <div class="events">
+          <div 
+            class="event" 
+            [class]="event.type" 
+            *ngFor="let event of day.events"
+          >
+            <div>{{ event.title }}</div>
+            <small>{{ event.time }} | {{ event.venue }}</small>
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
     
     
-    <div class="venue-section">
-      <div class="section-header">
-        <h2>场地状态</h2>
-        <button class="btn btn-outline"><i class="fas fa-filter"></i> 筛选</button>
-      </div>
-      
-      <div class="venue-list">
-        <div *ngFor="let venue of venues" class="venue-item">
-          <div class="venue-info">
-            <div class="venue-name">{{ venue.name }}</div>
-            <div class="venue-status" 
-                 [class.status-available]="venue.status === 'available'"
-                 [class.status-booked]="venue.status === 'booked'"
-                 [class.status-hold]="venue.status === 'hold'">
-              {{ getStatusText(venue.status, venue.date) }}
+    <!-- 周视图 -->
+    <div class="week-view" *ngIf="viewMode === 'week'">
+      <div class="week-grid">
+        <div class="time-slot" *ngFor="let hour of hours">
+          <div class="time-label">{{ hour }}:00</div>
+        </div>
+        
+        <div class="day-column" *ngFor="let day of weekDays; let i = index">
+          <div class="day-header">{{ day }}</div>
+          <div class="time-slot" *ngFor="let hour of hours">
+            <div 
+              class="week-event {{ event.type }}" 
+              *ngFor="let event of getEventsForDayAndHour(i, hour)"
+            >
+              <strong>{{ event.title }}</strong>
+              <div>{{ event.time }} | {{ event.venue }}</div>
             </div>
             </div>
           </div>
           </div>
-          <div class="venue-actions">
-            <div class="action-btn"><i class="fas fa-eye"></i></div>
-            <div class="action-btn"><i class="fas fa-edit"></i></div>
-          </div>
         </div>
         </div>
-        
-        <a href="#" class="view-more">
-          <i class="fas fa-chevron-down"></i> 查看42个场地/日期未定的客户
-        </a>
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
-</div>
+</div>

+ 308 - 357
manager-web/src/modules/manager/mobile/page-mine/page-mine.scss

@@ -1,487 +1,438 @@
-/* page-mine.component.scss */
-:host {
-  display: block;
-  background: linear-gradient(135deg, #fdf2f8 0%, #f0f9ff 100%);
-  color: #333;
-  min-height: 100vh;
-  padding: 20px;
-}
-
-.container {
-  max-width: 1600px;
+@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap');
+
+$primary: #9c27b0;
+$primary-light: #e1bee7;
+$primary-dark: #7b1fa2;
+$secondary: #ff9800;
+$accent: #4caf50;
+$text: #333;
+$text-light: #666;
+$bg: #f9f9f9;
+$bg-card: #fff;
+$border: #e0e0e0;
+$shadow: rgba(0, 0, 0, 0.1);
+
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  font-family: 'Noto Sans SC', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+.calendar-container {
+  max-width: 1400px;
   margin: 0 auto;
   margin: 0 auto;
-  display: flex;
-  flex-direction: column;
-  gap: 25px;
+  padding: 20px;
+  background-color: $bg;
+  min-height: 100vh;
 }
 }
 
 
-header {
+/* 顶部标题和功能区 */
+.header {
   display: flex;
   display: flex;
   justify-content: space-between;
   justify-content: space-between;
   align-items: center;
   align-items: center;
-  padding: 15px 25px;
-  background: rgba(255, 255, 255, 0.95);
-  border-radius: 16px;
-  box-shadow: 0 8px 20px rgba(186, 47, 142, 0.1);
-}
+  margin-bottom: 30px;
+  padding-bottom: 20px;
+  border-bottom: 1px solid $border;
 
 
-.logo {
-  display: flex;
-  align-items: center;
-  gap: 15px;
-  
   h1 {
   h1 {
-    font-size: 26px;
-    font-weight: 700;
-    background: linear-gradient(135deg, #d6336c, #a61e4d);
-    -webkit-background-clip: text;
-    background-clip: text;
-    color: transparent;
-  }
-  
-  i {
+    color: $primary;
     font-size: 28px;
     font-size: 28px;
-    color: #d6336c;
-  }
-}
-
-.nav-links {
-  display: flex;
-  gap: 25px;
-  
-  a {
-    text-decoration: none;
-    font-weight: 500;
-    color: #555;
-    padding: 10px 15px;
-    border-radius: 12px;
-    transition: all 0.3s ease;
-    position: relative;
-    
-    &:hover, &.active {
-      color: #d6336c;
-      background: rgba(214, 51, 108, 0.08);
-    }
+    display: flex;
+    align-items: center;
+    gap: 12px;
     
     
-    &.active:after {
-      content: "";
-      position: absolute;
-      bottom: 0;
-      left: 15px;
-      right: 15px;
-      height: 3px;
-      background: #d6336c;
-      border-radius: 3px;
+    i {
+      background: linear-gradient(135deg, $primary, $primary-dark);
+      -webkit-background-clip: text;
+      background-clip: text;
+      -webkit-text-fill-color: transparent;
     }
     }
   }
   }
 }
 }
 
 
-.main-content {
-  display: flex;
-  gap: 25px;
-}
-
-.calendar-section {
-  flex: 1;
-  background: rgba(255, 255, 255, 0.95);
-  border-radius: 16px;
-  padding: 25px;
-  box-shadow: 0 8px 20px rgba(186, 47, 142, 0.08);
-}
-
-.section-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 25px;
-  
-  h2 {
-    font-size: 22px;
-    font-weight: 600;
-    color: #d6336c;
-  }
-}
-
+/* 控制栏 */
 .controls {
 .controls {
   display: flex;
   display: flex;
-  gap: 15px;
+  justify-content: space-between;
   align-items: center;
   align-items: center;
+  margin-bottom: 20px;
+  background: $bg-card;
+  padding: 15px 20px;
+  border-radius: 12px;
+  box-shadow: 0 4px 12px $shadow;
 }
 }
 
 
 .date-controls {
 .date-controls {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  gap: 10px;
+  gap: 15px;
 }
 }
 
 
 .date-display {
 .date-display {
-  font-size: 18px;
-  font-weight: 500;
-  color: #555;
+  font-size: 22px;
+  font-weight: 600;
   min-width: 220px;
   min-width: 220px;
+  text-align: center;
 }
 }
 
 
 .btn {
 .btn {
-  padding: 8px 18px;
-  border-radius: 12px;
+  background: $primary;
+  color: white;
   border: none;
   border: none;
-  font-weight: 500;
+  padding: 10px 18px;
+  border-radius: 8px;
   cursor: pointer;
   cursor: pointer;
-  transition: all 0.3s ease;
+  font-weight: 600;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   gap: 8px;
   gap: 8px;
+  transition: all 0.3s ease;
   
   
-  &-primary {
-    background: linear-gradient(135deg, #d6336c, #a61e4d);
-    color: white;
+  &:hover {
+    background: $primary-dark;
+    transform: translateY(-2px);
   }
   }
   
   
   &-outline {
   &-outline {
     background: transparent;
     background: transparent;
-    border: 1px solid #d6336c;
-    color: #d6336c;
+    border: 2px solid $primary;
+    color: $primary;
+    
+    &:hover {
+      background: $primary-light;
+    }
   }
   }
+}
+
+.view-controls {
+  display: flex;
+  gap: 10px;
+}
+
+.view-btn {
+  padding: 8px 16px;
+  border-radius: 8px;
+  background: transparent;
+  border: 2px solid $border;
+  cursor: pointer;
+  font-weight: 600;
+  transition: all 0.2s;
+  
+  &.active {
+    background: $primary;
+    color: white;
+    border-color: $primary;
+  }
+  
+  &:hover:not(.active) {
+    background: #f0f0f0;
+  }
+}
+
+/* 筛选区域 */
+.filters {
+  display: flex;
+  gap: 15px;
+  margin-bottom: 20px;
+  flex-wrap: wrap;
+}
+
+.filter-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  background: $bg-card;
+  padding: 10px 15px;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px $shadow;
+  cursor: pointer;
+  transition: all 0.2s;
   
   
   &:hover {
   &:hover {
     transform: translateY(-2px);
     transform: translateY(-2px);
-    box-shadow: 0 4px 10px rgba(214, 51, 108, 0.2);
+    box-shadow: 0 4px 12px $shadow;
+  }
+  
+  &.active {
+    background: $primary;
+    color: white;
+  }
+}
+
+.filter-color {
+  width: 16px;
+  height: 16px;
+  border-radius: 50%;
+  
+  &.wedding {
+    background: linear-gradient(135deg, #e91e63, #9c27b0);
+  }
+  
+  &.banquet {
+    background: linear-gradient(135deg, #4caf50, #8bc34a);
+  }
+  
+  &.meeting {
+    background: linear-gradient(135deg, #2196f3, #03a9f4);
+  }
+  
+  &.other {
+    background: linear-gradient(135deg, #ff9800, #ffc107);
   }
   }
 }
 }
 
 
+/* 日历区域 */
 .calendar {
 .calendar {
-  display: grid;
-  grid-template-columns: repeat(7, 1fr);
-  gap: 12px;
-  margin-bottom: 25px;
+  background: $bg-card;
+  border-radius: 12px;
+  overflow: hidden;
+  box-shadow: 0 8px 30px $shadow;
 }
 }
 
 
-.weekdays {
+/* 月视图 */
+.month-view {
   display: grid;
   display: grid;
   grid-template-columns: repeat(7, 1fr);
   grid-template-columns: repeat(7, 1fr);
+  width: 100%;
+}
+
+.day-header {
+  padding: 15px 10px;
   text-align: center;
   text-align: center;
-  margin-bottom: 15px;
-  font-weight: 500;
-  color: #777;
+  font-weight: 600;
+  background: $primary;
+  color: white;
 }
 }
 
 
 .calendar-day {
 .calendar-day {
-  height: 110px;
-  background: #fff;
-  border-radius: 12px;
-  padding: 12px;
+  min-height: 120px;
+  border: 1px solid $border;
+  padding: 10px;
   position: relative;
   position: relative;
-  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.05);
   transition: all 0.3s ease;
   transition: all 0.3s ease;
-  border: 1px solid #eee;
-  overflow: hidden;
-  
-  &.empty {
-    background: transparent;
-    box-shadow: none;
-    border: none;
-  }
   
   
-  &:hover:not(.empty) {
-    transform: translateY(-3px);
-    box-shadow: 0 5px 15px rgba(214, 51, 108, 0.15);
+  &:hover {
+    background: #fafafa;
+    transform: scale(1.02);
+    z-index: 2;
+    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
   }
   }
 }
 }
 
 
-.day-header {
-  display: flex;
-  justify-content: space-between;
-  font-size: 14px;
+.day-number {
+  font-size: 18px;
+  font-weight: 700;
   margin-bottom: 8px;
   margin-bottom: 8px;
 }
 }
 
 
-.day-num {
-  font-weight: 700;
-  font-size: 18px;
+.other-month {
+  background: #fafafa;
+  color: #aaa;
 }
 }
 
 
-.lunar-date {
-  color: #999;
-  font-size: 12px;
+.today .day-number {
+  display: inline-block;
+  background: $primary;
+  color: white;
+  width: 30px;
+  height: 30px;
+  line-height: 30px;
+  text-align: center;
+  border-radius: 50%;
 }
 }
 
 
-.day-events {
-  font-size: 12px;
+/* 事件样式 */
+.events {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  margin-top: 5px;
 }
 }
 
 
-.event-tag {
-  display: inline-block;
-  padding: 3px 8px;
-  border-radius: 20px;
-  font-size: 11px;
-  margin-top: 5px;
-  white-space: nowrap;
+.event {
+  padding: 6px 8px;
+  border-radius: 6px;
+  font-size: 13px;
+  color: white;
+  cursor: pointer;
   overflow: hidden;
   overflow: hidden;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
-  max-width: 100%;
+  white-space: nowrap;
+  transition: all 0.2s;
   
   
-  &.event-booked {
-    background: rgba(214, 51, 108, 0.1);
-    color: #d6336c;
-    border-left: 3px solid #d6336c;
+  &:hover {
+    transform: translateX(3px);
   }
   }
   
   
-  &.event-available {
-    background: rgba(40, 167, 69, 0.1);
-    color: #28a745;
-    border-left: 3px solid #28a745;
+  &.wedding {
+    background: linear-gradient(90deg, #e91e63, #9c27b0);
   }
   }
   
   
-  &.event-consult {
-    background: rgba(23, 162, 184, 0.1);
-    color: #17a2b8;
-    border-left: 3px solid #17a2b8;
+  &.banquet {
+    background: linear-gradient(90deg, #4caf50, #8bc34a);
   }
   }
-}
-
-.today {
-  background: rgba(255, 228, 230, 0.5);
-  border: 2px solid #d6336c;
-}
-
-.today-label {
-  background: #d6336c;
-  color: white;
-  padding: 2px 6px;
-  border-radius: 4px;
-  font-size: 11px;
-  margin-left: 5px;
-}
-
-.venue-section {
-  width: 400px;
-  background: rgba(255, 255, 255, 0.95);
-  border-radius: 16px;
-  padding: 25px;
-  box-shadow: 0 8px 20px rgba(186, 47, 142, 0.08);
-}
-
-.venue-list {
-  margin-top: 20px;
-  max-height: 600px;
-  overflow-y: auto;
-  padding-right: 10px;
   
   
-  &::-webkit-scrollbar {
-    width: 6px;
+  &.meeting {
+    background: linear-gradient(90deg, #2196f3, #03a9f4);
   }
   }
   
   
-  &::-webkit-scrollbar-thumb {
-    background: rgba(214, 51, 108, 0.3);
-    border-radius: 4px;
+  &.other {
+    background: linear-gradient(90deg, #ff9800, #ffc107);
   }
   }
 }
 }
 
 
-.venue-item {
-  padding: 15px;
-  margin-bottom: 12px;
-  border-radius: 12px;
-  background: #fff;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05);
-  transition: all 0.3s ease;
-  border-left: 4px solid #d6336c;
-  
-  &:hover {
-    transform: translateX(5px);
-    box-shadow: 0 5px 15px rgba(214, 51, 108, 0.15);
+/* 周视图 */
+.week-view {
+  .week-grid {
+    display: grid;
+    grid-template-columns: 100px repeat(7, 1fr);
   }
   }
-}
-
-.venue-info {
-  flex: 1;
-}
-
-.venue-name {
-  font-weight: 600;
-  margin-bottom: 5px;
-  color: #333;
-}
-
-.venue-status {
-  font-size: 13px;
-  padding: 3px 10px;
-  border-radius: 20px;
-  display: inline-block;
   
   
-  &.status-available {
-    background: rgba(40, 167, 69, 0.15);
-    color: #28a745;
+  .time-slot {
+    height: 80px;
+    border: 1px solid $border;
+    padding: 5px;
+    position: relative;
   }
   }
   
   
-  &.status-booked {
-    background: rgba(214, 51, 108, 0.15);
-    color: #d6336c;
+  .time-label {
+    position: absolute;
+    top: -10px;
+    left: 5px;
+    font-size: 12px;
+    color: $text-light;
   }
   }
   
   
-  &.status-hold {
-    background: rgba(255, 193, 7, 0.15);
-    color: #fd7e14;
+  .week-event {
+    position: absolute;
+    border-radius: 6px;
+    padding: 5px;
+    color: white;
+    font-size: 12px;
+    overflow: hidden;
+    cursor: pointer;
+    width: 95%;
+    height: 70px;
+    
+    &.wedding {
+      background: linear-gradient(90deg, #e91e63, #9c27b0);
+    }
+    
+    &.banquet {
+      background: linear-gradient(90deg, #4caf50, #8bc34a);
+    }
+    
+    &.meeting {
+      background: linear-gradient(90deg, #2196f3, #03a9f4);
+    }
+    
+    &.other {
+      background: linear-gradient(90deg, #ff9800, #ffc107);
+    }
   }
   }
 }
 }
 
 
-.venue-actions {
+/* 统计数据 */
+.stats {
   display: flex;
   display: flex;
-  gap: 10px;
+  gap: 20px;
+  margin: 20px 0;
+  flex-wrap: wrap;
 }
 }
 
 
-.action-btn {
-  width: 30px;
-  height: 30px;
-  border-radius: 50%;
+.stat-card {
+  flex: 1;
+  min-width: 200px;
+  background: $bg-card;
+  padding: 20px;
+  border-radius: 12px;
+  box-shadow: 0 4px 12px $shadow;
   display: flex;
   display: flex;
-  align-items: center;
-  justify-content: center;
-  background: #f8f9fa;
-  color: #777;
-  cursor: pointer;
-  transition: all 0.3s ease;
+  flex-direction: column;
+  gap: 10px;
   
   
-  &:hover {
-    background: #d6336c;
-    color: white;
+  .stat-value {
+    font-size: 32px;
+    font-weight: 700;
+    color: $primary;
   }
   }
-}
-
-.ai-section {
-  background: rgba(255, 255, 255, 0.95);
-  border-radius: 16px;
-  padding: 25px;
-  margin-top: 10px;
-  box-shadow: 0 8px 20px rgba(186, 47, 142, 0.08);
-}
-
-.ai-header {
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  margin-bottom: 20px;
   
   
-  h3 {
-    font-size: 20px;
-    color: #d6336c;
+  .stat-title {
+    color: $text-light;
+    font-weight: 600;
   }
   }
   
   
-  i {
-    font-size: 24px;
-    color: #d6336c;
+  .stat-icon {
+    width: 50px;
+    height: 50px;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 10px;
   }
   }
-}
-
-.ai-recommendation {
-  background: linear-gradient(135deg, #fdf2f8, #f0f9ff);
-  border-radius: 12px;
-  padding: 20px;
-  margin-bottom: 20px;
-}
-
-.ai-title {
-  font-weight: 600;
-  margin-bottom: 10px;
-  color: #d6336c;
-  display: flex;
-  align-items: center;
-  gap: 8px;
-}
-
-.ai-content {
-  font-size: 14px;
-  line-height: 1.6;
-  color: #555;
   
   
-  ul {
-    padding-left: 20px;
-    margin: 10px 0;
+  &:nth-child(1) .stat-icon {
+    background: rgba(156, 39, 176, 0.1);
+    color: $primary;
   }
   }
   
   
-  li {
-    margin-bottom: 8px;
+  &:nth-child(2) .stat-icon {
+    background: rgba(76, 175, 80, 0.1);
+    color: $accent;
   }
   }
-}
-
-.stats-grid {
-  display: grid;
-  grid-template-columns: repeat(3, 1fr);
-  gap: 15px;
-  margin-top: 25px;
-}
-
-.stat-card {
-  background: #fff;
-  border-radius: 12px;
-  padding: 18px;
-  text-align: center;
-  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.05);
-}
-
-.stat-value {
-  font-size: 28px;
-  font-weight: 700;
-  color: #d6336c;
-  margin: 10px 0;
-}
-
-.stat-label {
-  font-size: 14px;
-  color: #777;
-}
-
-.view-more {
-  display: block;
-  text-align: center;
-  margin-top: 25px;
-  padding: 12px;
-  background: rgba(214, 51, 108, 0.08);
-  color: #d6336c;
-  border-radius: 12px;
-  text-decoration: none;
-  font-weight: 500;
-  transition: all 0.3s ease;
   
   
-  &:hover {
-    background: rgba(214, 51, 108, 0.15);
+  &:nth-child(3) .stat-icon {
+    background: rgba(33, 150, 243, 0.1);
+    color: #2196f3;
+  }
+  
+  &:nth-child(4) .stat-icon {
+    background: rgba(255, 152, 0, 0.1);
+    color: $secondary;
   }
   }
 }
 }
 
 
 /* 响应式设计 */
 /* 响应式设计 */
-@media (max-width: 1200px) {
-  .main-content {
+@media (max-width: 992px) {
+  .controls {
     flex-direction: column;
     flex-direction: column;
+    gap: 15px;
+    align-items: flex-start;
   }
   }
   
   
-  .venue-section {
-    width: 100%;
+  .view-controls {
+    align-self: flex-end;
   }
   }
 }
 }
 
 
 @media (max-width: 768px) {
 @media (max-width: 768px) {
-  header {
-    flex-direction: column;
-    gap: 15px;
+  .month-view {
+    grid-template-columns: repeat(1, 1fr);
   }
   }
   
   
-  .nav-links {
-    flex-wrap: wrap;
-    justify-content: center;
+  .day-header {
+    display: none;
   }
   }
   
   
-  .stats-grid {
-    grid-template-columns: 1fr;
+  .calendar-day {
+    min-height: auto;
   }
   }
   
   
-  .calendar {
-    grid-template-columns: repeat(7, 1fr);
+  .header {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 15px;
+  }
+  
+  .week-grid {
+    grid-template-columns: 60px repeat(1, 1fr);
     overflow-x: auto;
     overflow-x: auto;
   }
   }
   
   
-  .calendar-day {
-    min-width: 120px;
+  .stats {
+    flex-direction: column;
   }
   }
 }
 }

+ 5 - 5
manager-web/src/modules/manager/mobile/page-mine/page-mine.spec.ts

@@ -1,18 +1,18 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 
-import { PageMineComponent } from './page-mine';
+import { PageMine } from './page-mine';
 
 
 describe('PageMine', () => {
 describe('PageMine', () => {
-  let component: PageMineComponent;
-  let fixture: ComponentFixture<PageMineComponent>;
+  let component: PageMine ;
+  let fixture: ComponentFixture<PageMine >;
 
 
   beforeEach(async () => {
   beforeEach(async () => {
     await TestBed.configureTestingModule({
     await TestBed.configureTestingModule({
-      imports: [PageMineComponent]
+      imports: [PageMine ]
     })
     })
     .compileComponents();
     .compileComponents();
 
 
-    fixture = TestBed.createComponent(PageMineComponent);
+    fixture = TestBed.createComponent(PageMine );
     component = fixture.componentInstance;
     component = fixture.componentInstance;
     fixture.detectChanges();
     fixture.detectChanges();
   });
   });

+ 167 - 130
manager-web/src/modules/manager/mobile/page-mine/page-mine.ts

@@ -1,174 +1,211 @@
-// page-mine.component.ts
+import { CommonModule } from '@angular/common';
 import { Component, OnInit } from '@angular/core';
 import { Component, OnInit } from '@angular/core';
 
 
+interface CalendarDay {
+  date: number;
+  isCurrentMonth: boolean;
+  isToday: boolean;
+  events: any[];
+}
+
+interface Event {
+  id: number;
+  date: Date;
+  title: string;
+  type: string;
+  time: string;
+  venue: string;
+}
+
 @Component({
 @Component({
   selector: 'app-page-mine',
   selector: 'app-page-mine',
   templateUrl: './page-mine.html',
   templateUrl: './page-mine.html',
-  styleUrls: ['./page-mine.scss']
+  styleUrls: ['./page-mine.scss'],
+  standalone:true,
+  imports:[
+    CommonModule
+  ]
 })
 })
-export class PageMineComponent implements OnInit {
-  weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
-  currentYear: number;
-  currentMonth: number;
-  calendarDays: any[] = [];
+export class PageMine implements OnInit {
+  weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
+  hours = Array.from({length: 24}, (_, i) => i); // 0-23小时
+  currentDate: Date = new Date(); // 初始化当前日期
+  currentMonth: number = this.currentDate.getMonth(); // 初始化当前月份
+  currentYear: number = this.currentDate.getFullYear(); // 初始化当前年份
+  viewMode: 'month' | 'week' = 'month';
+  activeFilter: string = 'all';
   
   
-  venues = [
-    { name: '九州厅', status: 'booked', date: '7月5日' },
-    { name: '奥斯卡厅', status: 'available' },
-    { name: '星空之镜', status: 'available' },
-    { name: '塞纳花园', status: 'available' },
-    { name: '海洋之心', status: 'hold' },
-    { name: '冰雪奇缘', status: 'booked', date: '7月12日' },
-    { name: '暮光之城', status: 'booked', date: '7月19日' },
-    { name: '月光礼堂', status: 'booked', date: '7月26日' }
-  ];
+  monthDays: CalendarDay[] = [];
+  events: Event[] = [];
   
   
-  eventVenues = ['九州厅', '奥斯卡厅', '星空之镜', '塞纳花园', '海洋之心', '冰雪奇缘', '暮光之城', '月光礼堂'];
-  eventNames = ['张先生婚礼', '李小姐婚礼', '王先生婚礼', '赵小姐婚礼', '陈先生婚礼', '刘小姐婚礼'];
-  
-  constructor() { 
-    const today = new Date();
-    this.currentYear = today.getFullYear();
-    this.currentMonth = today.getMonth();
-  }
+  stats = {
+    totalEvents: 0,
+    weddings: 0,
+    banquets: 0,
+    meetings: 0
+  };
 
 
-  ngOnInit(): void {
+  ngOnInit() {
+    // 不需要再次初始化,因为已经在声明时初始化
+    this.initializeEvents();
     this.generateCalendar();
     this.generateCalendar();
+    this.calculateStats();
   }
   }
 
 
-  generateCalendar(): void {
-    this.calendarDays = [];
+  initializeEvents() {
+    this.events = [
+      { id: 1, date: new Date(this.currentYear, this.currentMonth, 5), title: '张先生 & 李女士婚礼', type: 'wedding', time: '10:00-14:00', venue: '玫瑰厅' },
+      { id: 2, date: new Date(this.currentYear, this.currentMonth, 8), title: '公司年度晚宴', type: 'banquet', time: '18:00-21:00', venue: '宴会A厅' },
+      { id: 3, date: new Date(this.currentYear, this.currentMonth, 12), title: '王先生 & 赵女士婚礼', type: 'wedding', time: '11:00-15:00', venue: '百合厅' },
+      { id: 4, date: new Date(this.currentYear, this.currentMonth, 15), title: '产品发布会', type: 'meeting', time: '14:00-16:00', venue: '会议中心' },
+      { id: 5, date: new Date(this.currentYear, this.currentMonth, 18), title: '陈先生 & 刘女士婚礼', type: 'wedding', time: '09:30-13:30', venue: '牡丹厅' },
+      { id: 6, date: new Date(this.currentYear, this.currentMonth, 20), title: '生日宴会', type: 'banquet', time: '19:00-22:00', venue: '宴会B厅' },
+      { id: 7, date: new Date(this.currentYear, this.currentMonth, 25), title: '婚庆行业交流会', type: 'meeting', time: '10:00-12:00', venue: '会议中心' },
+      { id: 8, date: new Date(this.currentYear, this.currentMonth, 28), title: '周先生 & 吴女士婚礼', type: 'wedding', time: '10:30-14:30', venue: '玫瑰厅' },
+      { id: 9, date: new Date(this.currentYear, this.currentMonth, 30), title: '公司团建活动', type: 'other', time: '全天', venue: '户外场地' }
+    ];
+  }
+
+  generateCalendar() {
+    this.monthDays = [];
     
     
-    // 获取当月第一天和最后一天
+    // 获取当月第一天
     const firstDay = new Date(this.currentYear, this.currentMonth, 1);
     const firstDay = new Date(this.currentYear, this.currentMonth, 1);
+    // 获取当月最后一天
     const lastDay = new Date(this.currentYear, this.currentMonth + 1, 0);
     const lastDay = new Date(this.currentYear, this.currentMonth + 1, 0);
+    // 获取当月第一天是星期几(0为周日,6为周六)
+    const firstDayIndex = firstDay.getDay() === 0 ? 6 : firstDay.getDay() - 1;
+    // 获取当月最后一天是星期几
+    const lastDayIndex = lastDay.getDay();
     
     
-    // 获取第一天是星期几(0=周日,1=周一...)
-    const firstDayIndex = firstDay.getDay();
-    
-    // 获取当月天数
-    const daysInMonth = lastDay.getDate();
-    
-    // 获取今天日期
+    // 上个月最后一天
+    const prevLastDay = new Date(this.currentYear, this.currentMonth, 0).getDate();
     const today = new Date();
     const today = new Date();
-    const isCurrentMonth = today.getMonth() === this.currentMonth && today.getFullYear() === this.currentYear;
     
     
-    // 生成空白日期(上个月)
-    for (let i = 0; i < firstDayIndex; i++) {
-      this.calendarDays.push({ isEmpty: true });
+    // 添加上个月日期
+    for (let i = firstDayIndex; i > 0; i--) {
+      const day = prevLastDay - i + 1;
+      this.monthDays.push({
+        date: day,
+        isCurrentMonth: false,
+        isToday: false,
+        events: []
+      });
     }
     }
     
     
-    // 生成当月日期
-    for (let day = 1; day <= daysInMonth; day++) {
-      const dateObj = new Date(this.currentYear, this.currentMonth, day);
-      const weekday = this.weekdays[dateObj.getDay()];
+    // 添加当月日期
+    for (let i = 1; i <= lastDay.getDate(); i++) {
+      const dayDate = new Date(this.currentYear, this.currentMonth, i);
+      const isToday = dayDate.toDateString() === today.toDateString();
       
       
-      // 简化版农历日期
-      const lunarDate = this.getLunarDate(day);
+      // 获取当天的活动(根据筛选条件)
+      const dayEvents = this.events.filter(event => 
+        event.date.getDate() === i && 
+        event.date.getMonth() === this.currentMonth && 
+        event.date.getFullYear() === this.currentYear &&
+        (this.activeFilter === 'all' || event.type === this.activeFilter)
+      );
       
       
-      // 判断是否是今天
-      const isToday = isCurrentMonth && day === today.getDate();
-      
-      // 生成随机事件
-      const events = this.generateRandomEvents(day);
-      
-      this.calendarDays.push({
-        date: day,
-        lunarDate: lunarDate,
-        weekday: weekday,
+      this.monthDays.push({
+        date: i,
+        isCurrentMonth: true,
         isToday: isToday,
         isToday: isToday,
-        events: events,
-        isEmpty: false
+        events: dayEvents
       });
       });
     }
     }
-  }
-  
-  getLunarDate(day: number): string {
-    // 简化版农历日期
-    const lunarMonths = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
-    const lunarDays = ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十',
-                      '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
-                      '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'];
-    
-    // 随机生成农历月份(模拟)
-    const monthIndex = Math.floor(Math.random() * lunarMonths.length);
-    const dayIndex = Math.min(day - 1, lunarDays.length - 1);
     
     
-    return `${lunarMonths[monthIndex]}月${lunarDays[dayIndex]}`;
+    // 添加下个月日期
+    const daysNeeded = 42 - (firstDayIndex + lastDay.getDate()); // 42是6行7列的格子总数
+    for (let i = 1; i <= daysNeeded; i++) {
+      this.monthDays.push({
+        date: i,
+        isCurrentMonth: false,
+        isToday: false,
+        events: []
+      });
+    }
   }
   }
-  
-  generateRandomEvents(day: number): any[] {
-    const events = [];
-    const today = new Date();
-    
-    // 如果是周末,增加事件数量
-    const dateObj = new Date(this.currentYear, this.currentMonth, day);
-    const isWeekend = dateObj.getDay() === 0 || dateObj.getDay() === 6;
+
+  getEventsForDayAndHour(dayIndex: number, hour: number): Event[] {
+    const dayOfWeek = dayIndex; // 0-6对应周一到周日
+    const currentDate = new Date(this.currentYear, this.currentMonth, 1);
     
     
-    // 如果是过去的日期,只显示已预订事件
-    const isPast = dateObj < today;
+    // 计算当前月份第一天是星期几
+    const firstDayOfWeek = currentDate.getDay() === 0 ? 7 : currentDate.getDay();
     
     
-    // 随机决定事件数量 (0-3)
-    let eventCount = Math.floor(Math.random() * (isWeekend ? 4 : 3));
-    if (isPast) eventCount = Math.min(eventCount, 2);
+    // 计算目标日期
+    const targetDate = new Date(
+      this.currentYear, 
+      this.currentMonth, 
+      1 + (dayOfWeek - firstDayOfWeek + 7) % 7
+    );
     
     
-    for (let i = 0; i < eventCount; i++) {
-      const venue = this.eventVenues[Math.floor(Math.random() * this.eventVenues.length)];
+    // 过滤事件
+    return this.events.filter(event => {
+      const eventDate = event.date;
       
       
-      // 事件类型:30%空闲,30%咨询,40%已预订
-      const eventType = Math.random() < 0.3 ? 'available' : 
-                      Math.random() < 0.3 ? 'consult' : 'booked';
+      // 检查日期是否匹配
+      const sameDate = 
+        eventDate.getDate() === targetDate.getDate() &&
+        eventDate.getMonth() === targetDate.getMonth() &&
+        eventDate.getFullYear() === targetDate.getFullYear();
       
       
-      let title = '';
-      switch (eventType) {
-        case 'available':
-          title = '空闲';
-          break;
-        case 'consult':
-          title = '咨询';
-          break;
-        case 'booked':
-          title = this.eventNames[Math.floor(Math.random() * this.eventNames.length)];
-          break;
-      }
+      if (!sameDate) return false;
       
       
-      events.push({
-        venue: venue,
-        type: eventType,
-        title: title
-      });
-    }
-    
-    return events;
+      // 检查时间是否匹配
+      if (event.time === '全天') return true;
+      
+      const [startTime] = event.time.split('-');
+      const eventHour = parseInt(startTime.split(':')[0]);
+      
+      return eventHour === hour;
+    });
   }
   }
-  
-  changeMonth(offset: number): void {
-    // 更改月份
+
+  changeMonth(offset: number) {
     this.currentMonth += offset;
     this.currentMonth += offset;
     
     
-    // 处理年份变化
-    if (this.currentMonth > 11) {
-      this.currentMonth = 0;
-      this.currentYear++;
-    } else if (this.currentMonth < 0) {
+    if (this.currentMonth < 0) {
       this.currentMonth = 11;
       this.currentMonth = 11;
       this.currentYear--;
       this.currentYear--;
+    } else if (this.currentMonth > 11) {
+      this.currentMonth = 0;
+      this.currentYear++;
     }
     }
     
     
-    // 重新生成日历
+    this.initializeEvents();
     this.generateCalendar();
     this.generateCalendar();
+    this.calculateStats();
   }
   }
-  
-  getStatusText(status: string, date?: string): string {
-    switch (status) {
-      case 'available':
-        return '空闲';
-      case 'booked':
-        return `已预订 (${date})`;
-      case 'hold':
-        return '意向沟通中';
-      default:
-        return '';
-    }
+
+  goToToday() {
+    const today = new Date();
+    this.currentDate = today;
+    this.currentMonth = today.getMonth();
+    this.currentYear = today.getFullYear();
+    
+    this.initializeEvents();
+    this.generateCalendar();
+    this.calculateStats();
+  }
+
+  setFilter(filter: string) {
+    this.activeFilter = filter;
+    this.generateCalendar();
+    this.calculateStats();
+  }
+
+  calculateStats() {
+    const currentMonthEvents = this.events.filter(event => 
+      event.date.getMonth() === this.currentMonth && 
+      event.date.getFullYear() === this.currentYear
+    );
+    
+    this.stats = {
+      totalEvents: currentMonthEvents.length,
+      weddings: currentMonthEvents.filter(e => e.type === 'wedding').length,
+      banquets: currentMonthEvents.filter(e => e.type === 'banquet').length,
+      meetings: currentMonthEvents.filter(e => e.type === 'meeting').length
+    };
   }
   }
 }
 }