|
|
@@ -1,16 +1,141 @@
|
|
|
<div class="school-services-container">
|
|
|
- <app-page-header [title]="'校园服务'" (back)="backToDashboard()"></app-page-header>
|
|
|
+ <app-page-header [title]="'校园服务'" [showBack]="false"></app-page-header>
|
|
|
|
|
|
<main class="school-services-main">
|
|
|
<div class="school-services-content">
|
|
|
+
|
|
|
+ <!-- 顶部总览(方案 B 风格) -->
|
|
|
+ <section class="services-hero">
|
|
|
+ <div class="hero-header">
|
|
|
+ <div class="hero-text-group">
|
|
|
+ <div class="hero-title">校园服务</div>
|
|
|
+ <div class="hero-sub">常用服务都在这里,一屏查看「吃 · 动 · 场馆」</div>
|
|
|
+ </div>
|
|
|
+ <div class="hero-badge">🏫 东区 · 晴 26℃</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="hero-search">
|
|
|
+ <span class="hero-search-icon">🔍</span>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ placeholder="搜索餐厅 / 场馆 / 队伍,例如『羽毛球』"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="hero-grid">
|
|
|
+ <div class="hero-feature" (click)="openDiningPanel()">
|
|
|
+ <div class="hero-feature-icon">🍱</div>
|
|
|
+ <div class="hero-feature-body">
|
|
|
+ <div class="hero-feature-title">校园饮食</div>
|
|
|
+ <div class="hero-feature-sub">今日推荐 {{ diningOptions.length }} 家餐厅</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="hero-feature" (click)="openTeamsPanel()">
|
|
|
+ <div class="hero-feature-icon">🏃</div>
|
|
|
+ <div class="hero-feature-body">
|
|
|
+ <div class="hero-feature-title">运动组队</div>
|
|
|
+ <div class="hero-feature-sub">当前有 {{ exerciseTeams.length }} 支队伍可加入</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="hero-feature" (click)="openVenuesPanel()">
|
|
|
+ <div class="hero-feature-icon">🏟️</div>
|
|
|
+ <div class="hero-feature-body">
|
|
|
+ <div class="hero-feature-title">场馆预约</div>
|
|
|
+ <div class="hero-feature-sub">支持篮球 / 羽毛球等多个场馆</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="hero-feature" (click)="openMySchedulePanel()">
|
|
|
+ <div class="hero-feature-icon">📋</div>
|
|
|
+ <div class="hero-feature-body">
|
|
|
+ <div class="hero-feature-title">我的约队 & 预约</div>
|
|
|
+ <div class="hero-feature-sub">一键总览今日相关安排</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <!-- 今日状态总览 + 今日安排 + 常用入口(方案 B 概览区) -->
|
|
|
+ <section class="overview-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <div class="section-title">今日状态总览</div>
|
|
|
+ <div class="section-hint">帮你快速判断「现在去不去」</div>
|
|
|
+ </div>
|
|
|
+ <div class="overview-card">
|
|
|
+ <div class="state-row">
|
|
|
+ <div class="state-card">
|
|
|
+ <div class="state-title">食堂拥挤指数</div>
|
|
|
+ <div>午餐高峰 · 部分窗口排队</div>
|
|
|
+ <div class="bar-bg"><div class="bar-fill busy"></div></div>
|
|
|
+ <div class="state-footer">
|
|
|
+ <span class="state-busy">建议错峰 10 分钟再去</span>
|
|
|
+ <span>实时刷新 · 1 分钟前</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="state-card">
|
|
|
+ <div class="state-title">运动场地空闲</div>
|
|
|
+ <div>晚间 18:00-20:00 时段</div>
|
|
|
+ <div class="bar-bg"><div class="bar-fill"></div></div>
|
|
|
+ <div class="state-footer">
|
|
|
+ <span class="state-ok">羽毛球 / 网球场余量充足</span>
|
|
|
+ <span>2 小时后略忙</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <section class="overview-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <div class="section-title">我的今日安排</div>
|
|
|
+ <div class="section-hint">一屏查看与你相关的就餐和运动提醒</div>
|
|
|
+ </div>
|
|
|
+ <div class="overview-card">
|
|
|
+ <div class="overview-timeline">
|
|
|
+ <div><strong>12:30</strong> 「青春餐厅」轻食套餐 · 建议 12:20 前出发</div>
|
|
|
+ <div><strong>18:00</strong> 羽毛球馆 3 号场 · 已预约 · 提前 10 分钟到场</div>
|
|
|
+ <div><strong>21:30</strong> 睡前拉伸 10 分钟 · 帮助放松腿部肌群</div>
|
|
|
+ </div>
|
|
|
+ <div class="overview-pill-link">📅 查看本周全部运动 / 就餐提醒 ›</div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <section class="overview-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <div class="section-title">常用入口</div>
|
|
|
+ <div class="section-hint">快速跳转到其他健康相关页面</div>
|
|
|
+ </div>
|
|
|
+ <div class="overview-card">
|
|
|
+ <div class="overview-links">
|
|
|
+ <div class="overview-link-row" (click)="goToExerciseDetail()">
|
|
|
+ <span>🏃 运动详情</span>
|
|
|
+ <span class="link-sub">查看今日运动时长</span>
|
|
|
+ </div>
|
|
|
+ <div class="overview-link-row" (click)="goToStepsDetail()">
|
|
|
+ <span>👣 步数详情</span>
|
|
|
+ <span class="link-sub">查看本周步数趋势</span>
|
|
|
+ </div>
|
|
|
+ <div class="overview-link-row" (click)="goToDietManagement()">
|
|
|
+ <span>🥗 饮食管理</span>
|
|
|
+ <span class="link-sub">打开今日饮食记录</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
<!-- 校园饮食推荐 -->
|
|
|
<section class="service-section">
|
|
|
<h2>校园饮食推荐</h2>
|
|
|
<div class="dining-options">
|
|
|
@for (option of diningOptions; track option.id) {
|
|
|
- <div class="dining-card">
|
|
|
+ <div class="dining-card" (click)="openDiningDetail(option)">
|
|
|
<div class="dining-image">
|
|
|
<img [src]="option.imageUrl" alt="{{ option.name }}" onerror="this.src='https://picsum.photos/300/200'" />
|
|
|
+ <span class="status-badge" [class.crowded]="option.id === 2" [class.idle]="option.id !== 2">
|
|
|
+ {{ option.id === 2 ? '拥挤' : '空闲' }}
|
|
|
+ </span>
|
|
|
</div>
|
|
|
<div class="dining-info">
|
|
|
<div class="dining-header">
|
|
|
@@ -31,41 +156,416 @@
|
|
|
</section>
|
|
|
|
|
|
<!-- 运动组队 -->
|
|
|
- <section class="service-section">
|
|
|
- <h2>运动组队</h2>
|
|
|
- <div class="exercise-teams">
|
|
|
- @for (team of exerciseTeams; track team.id) {
|
|
|
- <div class="team-card">
|
|
|
- <div class="team-header">
|
|
|
- <h3>{{ team.name }}</h3>
|
|
|
- <span class="team-activity">{{ team.activity }}</span>
|
|
|
- </div>
|
|
|
- <div class="team-details">
|
|
|
- <p><strong>活动时间:</strong> {{ team.time }}</p>
|
|
|
- <p><strong>活动地点:</strong> {{ team.location }}</p>
|
|
|
- <p><strong>当前人数:</strong> {{ team.currentMembers }}/{{ team.maxMembers }}</p>
|
|
|
- <p><strong>队长:</strong> {{ team.captain }}</p>
|
|
|
- <p class="team-description">{{ team.description }}</p>
|
|
|
- <button class="join-team-btn" [disabled]="team.currentMembers >= team.maxMembers" (click)="joinExerciseTeam(team.id)">
|
|
|
- {{ team.currentMembers >= team.maxMembers ? '人数已满' : '加入队伍' }}
|
|
|
- </button>
|
|
|
+ <section class="service-section sports-section">
|
|
|
+ <div class="sports-header">
|
|
|
+ <h2>运动组队</h2>
|
|
|
+ <div class="sports-tabs">
|
|
|
+ <div class="tab-btn" [class.active]="activeTab === 'teams'" (click)="switchTab('teams')">发现队伍</div>
|
|
|
+ <div class="tab-btn" [class.active]="activeTab === 'plans'" (click)="switchTab('plans')">我的训练</div>
|
|
|
+ <div class="tab-btn" [class.active]="activeTab === 'venues'" (click)="switchTab('venues')">场馆预约</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Tab Content -->
|
|
|
+ <div class="tab-content" [class.active]="activeTab === 'teams'">
|
|
|
+ <div class="filter-bar">
|
|
|
+ <input type="text" class="search-input" placeholder="搜索队伍...">
|
|
|
+ <select class="type-select">
|
|
|
+ <option>所有类型</option>
|
|
|
+ <option>跑步</option>
|
|
|
+ <option>瑜伽</option>
|
|
|
+ <option>篮球</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="exercise-teams">
|
|
|
+ @for (team of exerciseTeams; track team.id) {
|
|
|
+ <div class="team-card" (click)="openTeamDetail(team)">
|
|
|
+ <div class="team-header">
|
|
|
+ <h3>{{ team.name }}</h3>
|
|
|
+ <span class="team-activity">{{ team.activity }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="team-details">
|
|
|
+ <p class="icon-text">🕒 {{ team.time }}</p>
|
|
|
+ <p class="icon-text">📍 {{ team.location }}</p>
|
|
|
+
|
|
|
+ <div class="team-footer">
|
|
|
+ <div class="members-info">
|
|
|
+ <span class="member-count">{{ team.currentMembers }}/{{ team.maxMembers }}人</span>
|
|
|
+ <div class="progress-mini">
|
|
|
+ <div class="progress-bar"
|
|
|
+ [style.width.%]="(team.currentMembers / team.maxMembers) * 100"
|
|
|
+ [style.background-color]="team.currentMembers >= team.maxMembers ? '#F9D2E4' : '#01847F'">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <button class="join-team-btn"
|
|
|
+ [disabled]="team.currentMembers >= team.maxMembers"
|
|
|
+ (click)="$event.stopPropagation(); joinExerciseTeam(team.id)">
|
|
|
+ {{ team.currentMembers >= team.maxMembers ? '已满员' : '加入' }}
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="tab-content" [class.active]="activeTab === 'plans'">
|
|
|
+ <div class="plan-layout">
|
|
|
+ <div class="plan-sidebar">
|
|
|
+ <div class="sidebar-header">我的队伍</div>
|
|
|
+ <ul class="my-team-list">
|
|
|
+ @for (team of myTeams; track team.id) {
|
|
|
+ <li class="my-team-item"
|
|
|
+ [class.active]="selectedMyTeamId === team.id"
|
|
|
+ (click)="selectMyTeam(team.id)">
|
|
|
+ <span>{{ team.name }}</span>
|
|
|
+ @if (team.role === 'captain') {
|
|
|
+ <span class="role-badge">队长</span>
|
|
|
+ }
|
|
|
+ </li>
|
|
|
+ }
|
|
|
+ </ul>
|
|
|
+ <button class="add-plan-btn">+ 发布新计划</button>
|
|
|
</div>
|
|
|
- }
|
|
|
+
|
|
|
+ <div class="plan-timeline">
|
|
|
+ <div class="timeline-header">本周训练安排 (12/06 - 12/12)</div>
|
|
|
+
|
|
|
+ @for (plan of filteredTrainingPlans; track plan.id) {
|
|
|
+ <div class="timeline-card" [class.pending]="plan.status === 'upcoming'">
|
|
|
+ <div class="time-box">
|
|
|
+ <div class="day">{{ plan.day }}</div>
|
|
|
+ <div class="week">{{ plan.weekday }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="content-box">
|
|
|
+ <div class="plan-title">{{ plan.title }}</div>
|
|
|
+ <div class="plan-desc">
|
|
|
+ 时间: {{ plan.time }} | 地点: {{ plan.location }} | 强度:
|
|
|
+ <span class="stars">
|
|
|
+ @for (i of [1,2,3,4,5]; track i) {
|
|
|
+ <span [style.color]="i <= plan.intensity ? '#01847F' : '#e0e0e0'">★</span>
|
|
|
+ }
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ @if (plan.tags.length > 0) {
|
|
|
+ <div class="plan-tags">
|
|
|
+ @for (tag of plan.tags; track tag) {
|
|
|
+ <span class="tag">{{ tag }}</span>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ <div class="action-box">
|
|
|
+ @if (plan.status === 'pending') {
|
|
|
+ <button class="btn btn-primary" (click)="checkIn(plan)">签到</button>
|
|
|
+ }
|
|
|
+ @if (plan.status === 'upcoming') {
|
|
|
+ <button class="btn btn-outline" disabled>待开始</button>
|
|
|
+ }
|
|
|
+ @if (plan.status === 'completed') {
|
|
|
+ <button class="btn btn-success" disabled>已完成</button>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="tab-content" [class.active]="activeTab === 'venues'">
|
|
|
+ <div class="venues-container">
|
|
|
+ <div class="venues-header">
|
|
|
+ <span class="header-title">快速查看今日 (12/06) 场地状态</span>
|
|
|
+ <a href="javascript:void(0)" class="view-records">查看我的预约记录 ></a>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="venue-list">
|
|
|
+ @for (venue of venues; track venue.id) {
|
|
|
+ <div class="venue-row">
|
|
|
+ <div class="venue-image">
|
|
|
+ <img [src]="venue.image" alt="{{ venue.name }}" (error)="handleImageError($event)">
|
|
|
+ </div>
|
|
|
+ <div class="venue-details">
|
|
|
+ <div class="venue-name">{{ venue.name }}</div>
|
|
|
+ <div class="venue-tags">
|
|
|
+ {{ venue.price }} | {{ venue.tags.join(' | ') }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="venue-status">
|
|
|
+ @for (slot of venue.timeSlots; track slot.time) {
|
|
|
+ <div class="status-item">
|
|
|
+ <div class="status-box" [class.full]="slot.status === 'full'" [class.available]="slot.status === 'available'">
|
|
|
+ {{ slot.status === 'full' ? '满' : '空' }}
|
|
|
+ </div>
|
|
|
+ <div class="time-label">{{ slot.time }}</div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ <button class="btn btn-primary book-btn" (click)="bookVenue(venue)">预约</button>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
<!-- 底部帮助与反馈 -->
|
|
|
<div class="help-feedback-section">
|
|
|
- <button class="help-btn" (click)="viewFAQs()">
|
|
|
+ <button type="button" class="help-btn" (click)="openFaq()">
|
|
|
<i class="help-icon">❓</i>
|
|
|
常见问题
|
|
|
</button>
|
|
|
- <button class="feedback-btn" (click)="openFeedback()">
|
|
|
+ <button type="button" class="feedback-btn" (click)="openFeedback()">
|
|
|
<i class="feedback-icon">💬</i>
|
|
|
意见反馈
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</main>
|
|
|
+
|
|
|
+ <!-- 校园饮食面板 -->
|
|
|
+ <div class="panel-overlay" [class.active]="isDiningPanelOpen" (click)="closeDiningPanel()"></div>
|
|
|
+ <div class="side-panel" [class.active]="isDiningPanelOpen">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3>校园饮食推荐</h3>
|
|
|
+ <button class="panel-close" (click)="closeDiningPanel()">×</button>
|
|
|
+ </div>
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="dining-list">
|
|
|
+ @for (option of diningOptions; track option.id) {
|
|
|
+ <div class="dining-item" (click)="openDiningDetail(option)">
|
|
|
+ <div class="dining-item-header">
|
|
|
+ <h4>{{ option.name }}</h4>
|
|
|
+ <span class="rating">{{ formatRating(option.rating) }}</span>
|
|
|
+ </div>
|
|
|
+ <p class="dining-item-sub">📍 {{ option.location }} ({{ option.distance }})</p>
|
|
|
+ <p class="dining-item-sub">健康选择: {{ option.healthyOptions.join('、') }}</p>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 运动组队面板 -->
|
|
|
+ <div class="panel-overlay" [class.active]="isTeamsPanelOpen" (click)="closeTeamsPanel()"></div>
|
|
|
+ <div class="side-panel" [class.active]="isTeamsPanelOpen">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3>运动组队</h3>
|
|
|
+ <button class="panel-close" (click)="closeTeamsPanel()">×</button>
|
|
|
+ </div>
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="teams-list">
|
|
|
+ @for (team of exerciseTeams; track team.id) {
|
|
|
+ <div class="team-item" (click)="openTeamDetail(team)">
|
|
|
+ <div class="team-item-header">
|
|
|
+ <h4>{{ team.name }}</h4>
|
|
|
+ <span class="team-activity-badge">{{ team.activity }}</span>
|
|
|
+ </div>
|
|
|
+ <p class="team-item-sub">🕒 {{ team.time }}</p>
|
|
|
+ <p class="team-item-sub">📍 {{ team.location }}</p>
|
|
|
+ <p class="team-item-sub">👥 {{ team.currentMembers }}/{{ team.maxMembers }} 人</p>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 场馆预约面板 -->
|
|
|
+ <div class="panel-overlay" [class.active]="isVenuesPanelOpen" (click)="closeVenuesPanel()"></div>
|
|
|
+ <div class="side-panel" [class.active]="isVenuesPanelOpen">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3>场馆预约</h3>
|
|
|
+ <button class="panel-close" (click)="closeVenuesPanel()">×</button>
|
|
|
+ </div>
|
|
|
+ <div class="panel-content">
|
|
|
+ <div class="venues-list">
|
|
|
+ @for (venue of venues; track venue.id) {
|
|
|
+ <div class="venue-item" (click)="bookVenue(venue)">
|
|
|
+ <div class="venue-item-header">
|
|
|
+ <h4>{{ venue.name }}</h4>
|
|
|
+ <span class="venue-price">{{ venue.price }}</span>
|
|
|
+ </div>
|
|
|
+ <p class="venue-item-sub">标签: {{ venue.tags.join(' / ') }}</p>
|
|
|
+ <div class="venue-slots">
|
|
|
+ @for (slot of venue.timeSlots; track slot.time) {
|
|
|
+ <span class="slot-badge" [class.available]="slot.status === 'available'" [class.full]="slot.status === 'full'">
|
|
|
+ {{ slot.time }} {{ slot.status === 'available' ? '空' : '满' }}
|
|
|
+ </span>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 我的约队 & 预约面板 -->
|
|
|
+ <div class="panel-overlay" [class.active]="isMySchedulePanelOpen" (click)="closeMySchedulePanel()"></div>
|
|
|
+ <div class="side-panel" [class.active]="isMySchedulePanelOpen">
|
|
|
+ <div class="panel-header">
|
|
|
+ <h3>我的约队 & 预约</h3>
|
|
|
+ <button class="panel-close" (click)="closeMySchedulePanel()">×</button>
|
|
|
+ </div>
|
|
|
+ <div class="panel-content my-schedule-content">
|
|
|
+
|
|
|
+ <!-- Section 1: 我的队伍 -->
|
|
|
+ <div class="schedule-section">
|
|
|
+ <div class="section-title">我的队伍</div>
|
|
|
+ <div class="my-teams-grid">
|
|
|
+ @for (team of myTeams; track team.id) {
|
|
|
+ <div class="my-team-card">
|
|
|
+ <div class="team-icon">{{ team.role === 'captain' ? '👑' : '🏃' }}</div>
|
|
|
+ <div class="team-info">
|
|
|
+ <div class="team-name">{{ team.name }}</div>
|
|
|
+ <div class="team-role">{{ team.role === 'captain' ? '队长' : '成员' }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ @if (myTeams.length === 0) {
|
|
|
+ <div class="empty-state">暂未加入任何队伍</div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Section 2: 近期训练计划 -->
|
|
|
+ <div class="schedule-section">
|
|
|
+ <div class="section-title">近期训练安排</div>
|
|
|
+ <div class="schedule-list">
|
|
|
+ @for (plan of myAllTrainingPlans; track plan.id) {
|
|
|
+ <div class="schedule-card" [class.today]="plan.weekday === '今天'">
|
|
|
+ <div class="schedule-date">
|
|
|
+ <span class="day">{{ plan.day }}</span>
|
|
|
+ <span class="week">{{ plan.weekday }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="schedule-info">
|
|
|
+ <div class="schedule-title">{{ plan.title }}</div>
|
|
|
+ <div class="schedule-meta">
|
|
|
+ <span class="time">🕒 {{ plan.time }}</span>
|
|
|
+ <span class="loc">📍 {{ plan.location }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="schedule-action">
|
|
|
+ @if (plan.status === 'pending') {
|
|
|
+ <button class="btn-checkin" (click)="checkIn(plan)">签到</button>
|
|
|
+ } @else if (plan.status === 'completed') {
|
|
|
+ <span class="status-done">已完成</span>
|
|
|
+ } @else {
|
|
|
+ <span class="status-wait">待开始</span>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ @if (myAllTrainingPlans.length === 0) {
|
|
|
+ <div class="empty-state">近期没有训练计划</div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Section 3: 我的预约 -->
|
|
|
+ <div class="schedule-section">
|
|
|
+ <div class="section-title">场馆预约记录</div>
|
|
|
+ <div class="booking-list">
|
|
|
+ @for (booking of myBookings; track booking.id) {
|
|
|
+ <div class="booking-card">
|
|
|
+ <div class="booking-img">
|
|
|
+ <img [src]="booking.venueImage" alt="{{ booking.venueName }}" (error)="handleImageError($event)">
|
|
|
+ </div>
|
|
|
+ <div class="booking-info">
|
|
|
+ <div class="booking-name">{{ booking.venueName }}</div>
|
|
|
+ <div class="booking-time">{{ booking.date }} {{ booking.time }}</div>
|
|
|
+ <div class="booking-price">支付: {{ booking.price }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="booking-status" [class]="booking.status">
|
|
|
+ {{ booking.status === 'upcoming' ? '待使用' : (booking.status === 'completed' ? '已完成' : '已取消') }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ @if (myBookings.length === 0) {
|
|
|
+ <div class="empty-state">暂无预约记录</div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 详情弹窗 (Bottom Sheet) -->
|
|
|
+ <div class="bottom-sheet-overlay" [class.active]="isDetailOpen" (click)="closeDiningDetail()"></div>
|
|
|
+ <div class="bottom-sheet" [class.active]="isDetailOpen">
|
|
|
+ <div class="sheet-handle"></div>
|
|
|
+ <div class="sheet-header" *ngIf="selectedDiningDetail">
|
|
|
+ <div class="sheet-title">{{ selectedDiningDetail.name }}</div>
|
|
|
+ <div class="sheet-subtitle">营业时间: 06:30 - 21:00 | 人均: ¥15</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="sheet-content" *ngIf="selectedDiningDetail">
|
|
|
+ <!-- 实时状态 -->
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="section-head">📊 实时状态</div>
|
|
|
+ <div class="crowd-meter">
|
|
|
+ <div class="crowd-value"
|
|
|
+ [style.width.%]="selectedDiningDetail.crowdLevel"
|
|
|
+ [style.background]="selectedDiningDetail.status === 'crowded' ? 'linear-gradient(90deg, #F9D2E4, #f973a8)' : 'linear-gradient(90deg, #01847F, #02a39a)'">
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="crowd-text">
|
|
|
+ {{ selectedDiningDetail.status === 'crowded' ? '当前就餐人数较多,预计排队 15 分钟' : '当前就餐人数较少,无需排队' }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 营养分析 -->
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="section-head">🥗 营养特色</div>
|
|
|
+ <div class="nutrition-tags">
|
|
|
+ <span class="nutri-tag">优质蛋白</span>
|
|
|
+ <span class="nutri-tag">低GI主食</span>
|
|
|
+ <span class="nutri-tag">根据运动量推荐</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 推荐菜品 -->
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="section-head">👍 今日健康推荐</div>
|
|
|
+ <div class="menu-grid">
|
|
|
+ <div class="menu-item" *ngFor="let item of selectedDiningDetail.menuItems">
|
|
|
+ <div class="menu-img" [style.background-image]="'url(' + item.image + ')'"></div>
|
|
|
+ <div class="menu-name">{{ item.name }}</div>
|
|
|
+ <div class="menu-cal">约 {{ item.calories }} kcal</div>
|
|
|
+ <div class="menu-price">¥{{ item.price }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="sheet-footer">
|
|
|
+ <button class="btn btn-secondary" (click)="navigateToDining()">导航前往</button>
|
|
|
+ <button class="btn btn-primary" (click)="orderFood()">去点餐</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <app-team-detail-modal
|
|
|
+ [isVisible]="isTeamModalVisible"
|
|
|
+ [team]="selectedTeam"
|
|
|
+ (close)="closeTeamDetail()"
|
|
|
+ (join)="joinExerciseTeam($event)">
|
|
|
+ </app-team-detail-modal>
|
|
|
+
|
|
|
+ <app-venue-detail-modal
|
|
|
+ [isVisible]="isVenueModalVisible"
|
|
|
+ [venue]="selectedVenue"
|
|
|
+ (close)="closeVenueDetail()"
|
|
|
+ (book)="handleVenueBooking($any($event))">
|
|
|
+ </app-venue-detail-modal>
|
|
|
+
|
|
|
+ <app-faq-modal
|
|
|
+ [isVisible]="isFaqModalVisible"
|
|
|
+ (close)="closeFaq()">
|
|
|
+ </app-faq-modal>
|
|
|
+
|
|
|
+ <app-feedback-modal
|
|
|
+ [isVisible]="isFeedbackModalVisible"
|
|
|
+ (close)="closeFeedback()">
|
|
|
+ </app-feedback-modal>
|
|
|
</div>
|