Pārlūkot izejas kodu

feat.story&fix.bug

0235648 5 dienas atpakaļ
vecāks
revīzija
c9da79ec53

+ 2 - 2
menu-web/src/app/app.routes.ts

@@ -16,8 +16,8 @@ export const routes: Routes = [
         loadComponent: () => import('../modules/food/mobile/page-home/page-home').then(m => m.PageHomeComponent)
       },
        {
-        path: 'menus',
-        loadComponent: () => import('../modules/food/mobile/page-menus/page-menus').then(m => m.PageMenus)
+        path: 'story',
+        loadComponent: () => import('../modules/food/mobile/page-story/page-story').then(m => m.PageStory)
       },
          {
         path: 'reserve',

+ 1 - 0
menu-web/src/index.html

@@ -7,6 +7,7 @@
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <link rel="icon" type="image/x-icon" href="favicon.ico">
   <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
+  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 </head>
 <body>
   <app-root></app-root>

+ 3 - 3
menu-web/src/modules/food/mobile/nav-mobile-tabs/nav-mobile-tabs.ts

@@ -12,9 +12,9 @@ export class NavMobileTabs {
   // 底部导航
   navItems = [
     { path:"/menu/home",icon: 'fas fa-home', text: '首页' },
-    { path:"/menu/menus",icon: 'fas fa-search', text: '发现' },
-    { path:"/menu/order",icon: 'fas fa-book-open', text: '故事' },
-    { path:"/menu/reserve",icon: 'fas fa-shopping-cart', text: '购物车' },
+    { path:"/menu/story",icon: 'fas fa-book-open', text: '故事' },
+    { path:"/menu/order",icon: 'fas fa-shopping-cart', text: '购物车' },
+    { path:"/menu/reserve",icon: 'fas fa-search', text: '发现' },
     { path:"/menu/mine",icon: 'fas fa-user', text: '我的', active: true }
   ];
 }

+ 13 - 13
menu-web/src/modules/food/mobile/page-reserve/components/header/header.component.scss

@@ -1,13 +1,13 @@
-.text {
-  color: variables.$primary-color; // 如果直接 @use
-}
+// 完全移除这行 @use 语句
+// @use 'src/modules/food/mobile/page-reserve/components/header/header.component.scss/styles/variables' as *;
+
 header {
-  background: $white;
+  background: var(--white); // 使用 CSS 变量
   padding: 15px 0 10px;
   position: sticky;
   top: 0;
   z-index: 100;
-  box-shadow: $card-shadow;
+  box-shadow: var(--card-shadow); // 使用 CSS 变量
 }
 
 .header-container {
@@ -23,7 +23,7 @@ header {
   gap: 8px;
   font-size: 18px;
   font-weight: 700;
-  color: $primary;
+  color: var(--primary); // 使用 CSS 变量
   
   fa-icon {
     font-size: 22px;
@@ -42,21 +42,21 @@ header {
   display: flex;
   align-items: center;
   justify-content: center;
-  background: $light-gray;
-  color: $gray;
+  background: var(--light-gray); // 使用 CSS 变量
+  color: var(--gray); // 使用 CSS 变量
   font-size: 16px;
   border: none;
-  transition: $transition;
+  transition: var(--transition); // 使用 CSS 变量
   cursor: pointer;
   
   &:hover {
-    background: $primary;
-    color: $white;
+    background: var(--primary); // 使用 CSS 变量
+    color: var(--white); // 使用 CSS 变量
   }
 }
 
 .search-bar {
-  background: $light-gray;
+  background: var(--light-gray); // 使用 CSS 变量
   border-radius: 25px;
   padding: 10px 20px;
   margin: 12px 15px 0;
@@ -64,7 +64,7 @@ header {
   align-items: center;
   
   fa-icon {
-    color: $gray;
+    color: var(--gray); // 使用 CSS 变量
     margin-right: 10px;
   }
   

+ 15 - 14
menu-web/src/modules/food/mobile/page-reserve/components/menu-card/menu-card.component.scss

@@ -1,10 +1,11 @@
+@use 'src/modules/food/mobile/page-reserve/components/menu-card/menu-card.component.scss/styles/variables' as *;
 
 .menu-card {
-  background: white;
+  background: $white;
   border-radius: 15px;
   overflow: hidden;
-  // box-shadow: $card-shadow;
-  // transition: $transition;
+  box-shadow: $card-shadow;
+  transition: $transition;
   position: relative;
   
   &:hover {
@@ -27,8 +28,8 @@
   position: absolute;
   top: 10px;
   right: 10px;
-  // background: $accent;
-  // color: $white;
+  background: $accent;
+  color: $white;
   padding: 3px 10px;
   border-radius: 20px;
   font-size: 12px;
@@ -51,13 +52,13 @@
 }
 
 .menu-price {
-  // color: $primary;
+  color: $primary;
   font-weight: 600;
 }
 
 .menu-desc {
   font-size: 12px;
-  // color: $gray;
+  color: $gray;
   line-height: 1.5;
   height: 36px;
   overflow: hidden;
@@ -70,7 +71,7 @@
   display: flex;
   justify-content: space-between;
   font-size: 11px;
-  // color: $gray;
+  color: $gray;
   margin-top: 8px;
   
   fa-icon {
@@ -91,9 +92,9 @@
   text-align: center;
   font-size: 12px;
   cursor: pointer;
-  // transition: $transition;
+  transition: $transition;
   background: rgba(78, 205, 196, 0.1);
-  // color: $secondary;
+  color: $secondary;
   border: none;
   
   &:hover {
@@ -104,20 +105,20 @@
 .add-btn {
   width: 32px;
   height: 32px;
-  // background: $primary;
-  // color: $white;
+  background: $primary;
+  color: $white;
   border: none;
   border-radius: 50%;
   font-size: 14px;
   display: flex;
   align-items: center;
   justify-content: center;
-  // transition: $transition;
+  transition: $transition;
   margin-left: 10px;
   cursor: pointer;
   
   &:hover {
-    // background: $primary-dark;
+    background: $primary-dark;
     transform: scale(1.1);
   }
 }

+ 136 - 0
menu-web/src/modules/food/mobile/page-story/page-story.html

@@ -0,0 +1,136 @@
+<div class="container">
+  <!-- Header -->
+  <header>
+    <div class="header-container">
+      <div class="logo">
+        <i class="fas fa-cat"></i>
+        <span>点菜喵</span>
+      </div>
+      <div class="user-actions">
+        <button class="icon-btn">
+          <i class="fas fa-bell"></i>
+        </button>
+        <button class="icon-btn">
+          <i class="fas fa-search"></i>
+        </button>
+      </div>
+    </div>
+  </header>
+
+  <!-- Featured Story -->
+  <div class="featured-story">
+    <div class="featured-image" [style.background-image]="'url(' + featuredStory.image + ')'">
+      <div class="featured-content">
+        <div class="featured-tag">{{ featuredStory.tag }}</div>
+        <h2 class="featured-title">{{ featuredStory.title }}</h2>
+        <p class="featured-desc">{{ featuredStory.description }}</p>
+        <div class="featured-stats">
+          <span><i class="fas fa-eye"></i> {{ featuredStory.views | number }}浏览</span>
+          <span><i class="fas fa-heart"></i> {{ featuredStory.likes | number }}点赞</span>
+          <span><i class="fas fa-comment"></i> {{ featuredStory.comments | number }}评论</span>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- Category Navigation -->
+  <div class="category-nav">
+    <div 
+      *ngFor="let category of categories" 
+      class="category-item" 
+      [class.active]="category.active"
+      (click)="setActiveCategory(category.id)">
+      <div class="category-icon">
+        <i [class]="category.icon"></i>
+      </div>
+      <div class="category-text">{{ category.name }}</div>
+    </div>
+  </div>
+
+  <!-- Stories List -->
+  <div class="story-section">
+    <div class="story-header">
+      <h2>热门故事</h2>
+      <a href="#" class="more">查看更多</a>
+    </div>
+    
+    <div 
+      *ngFor="let story of stories" 
+      class="story-card"
+      (click)="cardClick($event)">
+      <div class="story-image" [style.background-image]="'url(' + story.image + ')'">
+        <h3 class="story-title">{{ story.title }}</h3>
+      </div>
+      <div class="story-meta">
+        <div class="story-stats">
+          <span class="story-stat"><i class="fas fa-eye"></i> {{ story.views | number }}</span>
+          <span class="story-stat"><i class="fas fa-heart"></i> {{ story.likes | number }}</span>
+        </div>
+        <span><i class="fas fa-clock"></i> {{ story.timeAgo }}</span>
+      </div>
+      <div class="story-actions">
+        <button class="story-btn read-btn" (click)="openStoryModal(story.id); $event.stopPropagation()">阅读故事</button>
+        <button class="story-btn share-btn" (click)="shareStory(); $event.stopPropagation()">分享</button>
+      </div>
+    </div>
+  </div>
+</div>
+
+<!-- Story Detail Modal -->
+<div 
+  class="modal-overlay" 
+  [class.active]="isModalOpen()"
+  (click)="closeModal()">
+  <div class="modal-content" (click)="$event.stopPropagation()">
+    <div class="modal-header">
+      <div class="modal-title">美食故事</div>
+      <div class="modal-close" (click)="closeModal()">
+        <i class="fas fa-times"></i>
+      </div>
+    </div>
+    <div class="modal-body">
+      <div 
+        class="modal-image" 
+        [style.background-image]="'url(' + getSelectedStory().image + ')'"></div>
+      <h2 class="modal-story-title">{{ getSelectedStory().title }}</h2>
+      <div class="modal-story-content" [innerHTML]="getSelectedStory().content"></div>
+    </div>
+    <div class="modal-footer">
+      <div class="story-stats">
+        <span class="story-stat"><i class="fas fa-eye"></i> {{ getSelectedStory().views | number }}浏览</span>
+        <span class="story-stat"><i class="fas fa-heart"></i> {{ getSelectedStory().likes | number }}点赞</span>
+        <span class="story-stat"><i class="fas fa-comment"></i> {{ getSelectedStory().comments | number }}评论</span>
+      </div>
+      <div class="modal-actions">
+        <button class="story-btn share-btn" (click)="shareStory()">分享</button>
+        <button class="story-btn read-btn" (click)="likeStory()">
+          <i class="fas fa-heart"></i> 点赞
+        </button>
+      </div>
+    </div>
+  </div>
+</div>
+
+<!-- Bottom Navigation -->
+<div class="bottom-nav">
+  <div class="nav-item">
+    <i class="fas fa-home"></i>
+    <div>首页</div>
+  </div>
+  <div class="nav-item">
+    <i class="fas fa-search"></i>
+    <div>发现</div>
+  </div>
+  <div class="nav-item active">
+    <i class="fas fa-book-open"></i>
+    <div>故事</div>
+  </div>
+  <div class="nav-item">
+    <i class="fas fa-shopping-cart"></i>
+    <div>购物车</div>
+  </div>
+  <div class="nav-item">
+    <i class="fas fa-user"></i>
+    <div>我的</div>
+  </div>
+</div>

+ 539 - 0
menu-web/src/modules/food/mobile/page-story/page-story.scss

@@ -0,0 +1,539 @@
+:host {
+  --primary: #ff6b6b;
+  --primary-light: #ff8e8e;
+  --primary-dark: #e55a5a;
+  --secondary: #4ecdc4;
+  --dark: #333;
+  --gray: #666;
+  --light-gray: #f5f5f5;
+  --white: #fff;
+  --card-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+  --transition: all 0.3s ease;
+}
+
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
+  -webkit-tap-highlight-color: transparent;
+}
+
+body {
+  background: linear-gradient(135deg, #fff5f5 0%, #fff0f0 100%);
+  color: var(--dark);
+  line-height: 1.6;
+  padding-bottom: 80px;
+}
+
+.container {
+  width: 100%;
+  max-width: 480px;
+  margin: 0 auto;
+  padding: 0 15px;
+}
+
+/* Header Styles */
+header {
+  background: var(--white);
+  padding: 15px 0 10px;
+  position: sticky;
+  top: 0;
+  z-index: 100;
+  box-shadow: var(--card-shadow);
+}
+
+.header-container {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 0 15px;
+}
+
+.logo {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  font-size: 18px;
+  font-weight: 700;
+  color: var(--primary);
+}
+
+.logo i {
+  font-size: 22px;
+}
+
+.user-actions {
+  display: flex;
+  gap: 15px;
+}
+
+.icon-btn {
+  width: 36px;
+  height: 36px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: var(--light-gray);
+  color: var(--gray);
+  font-size: 16px;
+  border: none;
+  transition: var(--transition);
+  cursor: pointer;
+}
+
+.icon-btn:hover {
+  background: var(--primary);
+  color: var(--white);
+}
+
+/* Featured Story */
+.featured-story {
+  margin: 15px;
+  position: relative;
+  border-radius: 15px;
+  overflow: hidden;
+  box-shadow: var(--card-shadow);
+}
+
+.featured-image {
+  width: 100%;
+  height: 200px;
+  background-size: cover;
+  background-position: center;
+  position: relative;
+}
+
+.featured-image::after {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: linear-gradient(to top, rgba(0,0,0,0.7), transparent 50%);
+}
+
+.featured-content {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 20px;
+  color: var(--white);
+  z-index: 2;
+}
+
+.featured-tag {
+  background: var(--primary);
+  color: var(--white);
+  padding: 4px 12px;
+  border-radius: 20px;
+  font-size: 12px;
+  font-weight: bold;
+  display: inline-block;
+  margin-bottom: 10px;
+}
+
+.featured-title {
+  font-size: 22px;
+  font-weight: bold;
+  margin-bottom: 8px;
+}
+
+.featured-desc {
+  font-size: 14px;
+  opacity: 0.9;
+  margin-bottom: 15px;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+.featured-stats {
+  display: flex;
+  gap: 15px;
+  font-size: 12px;
+  opacity: 0.9;
+}
+
+/* Category Navigation */
+.category-nav {
+  background: var(--white);
+  border-radius: 12px;
+  margin: 15px;
+  padding: 15px 0;
+  display: flex;
+  justify-content: space-around;
+  box-shadow: var(--card-shadow);
+}
+
+.category-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 8px;
+  cursor: pointer;
+  transition: var(--transition);
+}
+
+.category-item:hover {
+  transform: translateY(-5px);
+}
+
+.category-icon {
+  width: 50px;
+  height: 50px;
+  background: rgba(255, 107, 107, 0.1);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: var(--primary);
+  font-size: 20px;
+  transition: var(--transition);
+}
+
+.category-item.active .category-icon {
+  background: var(--primary);
+  color: var(--white);
+}
+
+.category-text {
+  font-size: 13px;
+  color: var(--gray);
+}
+
+/* Story Section */
+.story-section {
+  background: var(--white);
+  border-radius: 15px;
+  overflow: hidden;
+  box-shadow: var(--card-shadow);
+  margin: 15px;
+  position: relative;
+}
+
+.story-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15px;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.story-header h2 {
+  font-size: 18px;
+  color: var(--dark);
+  position: relative;
+  padding-left: 12px;
+}
+
+.story-header h2::before {
+  content: '';
+  position: absolute;
+  left: 0;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 4px;
+  height: 16px;
+  background: var(--primary);
+  border-radius: 2px;
+}
+
+.story-header .more {
+  color: var(--gray);
+  font-size: 13px;
+}
+
+/* Story Card */
+.story-card {
+  padding: 15px;
+  display: flex;
+  flex-direction: column;
+  gap: 15px;
+  transition: var(--transition);
+  animation: fadeIn 0.5s ease forwards;
+}
+
+.story-card:hover {
+  transform: translateY(-3px);
+}
+
+.story-image {
+  width: 100%;
+  height: 180px;
+  border-radius: 12px;
+  background-size: cover;
+  background-position: center;
+  position: relative;
+  overflow: hidden;
+  box-shadow: var(--card-shadow);
+}
+
+.story-image::after {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: linear-gradient(to top, rgba(0,0,0,0.5), transparent 50%);
+}
+
+.story-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: var(--white);
+  position: absolute;
+  bottom: 15px;
+  left: 15px;
+  z-index: 2;
+  max-width: 80%;
+}
+
+.story-meta {
+  display: flex;
+  justify-content: space-between;
+  font-size: 12px;
+  color: var(--gray);
+}
+
+.story-stats {
+  display: flex;
+  gap: 15px;
+}
+
+.story-stat {
+  display: flex;
+  align-items: center;
+  gap: 5px;
+}
+
+.story-actions {
+  display: flex;
+  gap: 10px;
+  margin-top: 10px;
+}
+
+.story-btn {
+  flex: 1;
+  padding: 10px 0;
+  border-radius: 20px;
+  text-align: center;
+  font-size: 14px;
+  cursor: pointer;
+  transition: var(--transition);
+  border: none;
+}
+
+.read-btn {
+  background: var(--primary);
+  color: var(--white);
+}
+
+.read-btn:hover {
+  background: var(--primary-dark);
+}
+
+.share-btn {
+  background: rgba(255, 107, 107, 0.1);
+  color: var(--primary);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 5px;
+}
+
+.share-btn:hover {
+  background: rgba(255, 107, 107, 0.2);
+}
+
+/* Story Detail Modal */
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0,0,0,0.7);
+  z-index: 1000;
+  display: none;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+  opacity: 0;
+  transition: opacity 0.3s ease;
+}
+
+.modal-overlay.active {
+  display: flex;
+  opacity: 1;
+}
+
+.modal-content {
+  background: var(--white);
+  border-radius: 15px;
+  max-width: 100%;
+  max-height: 90vh;
+  overflow-y: auto;
+  width: 100%;
+  position: relative;
+  transform: translateY(20px);
+  transition: transform 0.3s ease;
+}
+
+.modal-overlay.active .modal-content {
+  transform: translateY(0);
+}
+
+.modal-header {
+  position: sticky;
+  top: 0;
+  background: var(--white);
+  padding: 15px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border-bottom: 1px solid #f0f0f0;
+  z-index: 2;
+}
+
+.modal-title {
+  font-size: 18px;
+  font-weight: bold;
+}
+
+.modal-close {
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  background: var(--light-gray);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  transition: var(--transition);
+}
+
+.modal-close:hover {
+  background: var(--primary);
+  color: var(--white);
+}
+
+.modal-body {
+  padding: 15px;
+}
+
+.modal-image {
+  width: 100%;
+  height: 200px;
+  border-radius: 12px;
+  background-size: cover;
+  background-position: center;
+  margin-bottom: 15px;
+}
+
+.modal-story-title {
+  font-size: 20px;
+  font-weight: bold;
+  margin-bottom: 10px;
+  color: var(--primary);
+}
+
+.modal-story-content {
+  font-size: 15px;
+  line-height: 1.8;
+  color: var(--dark);
+  margin-bottom: 20px;
+}
+
+.modal-story-content p {
+  margin-bottom: 15px;
+}
+
+.modal-footer {
+  padding: 15px;
+  border-top: 1px solid #f0f0f0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.modal-actions {
+  display: flex;
+  gap: 10px;
+}
+
+/* Bottom Navigation */
+.bottom-nav {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  background: var(--white);
+  display: flex;
+  border-top: 1px solid #eee;
+  z-index: 100;
+}
+
+.nav-item {
+  flex: 1;
+  text-align: center;
+  padding: 12px 0;
+  color: var(--gray);
+  font-size: 12px;
+  position: relative;
+  cursor: pointer;
+}
+
+.nav-item.active {
+  color: var(--primary);
+}
+
+.nav-item.active::after {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 50%;
+  transform: translateX(-50%);
+  width: 30px;
+  height: 3px;
+  background: var(--primary);
+  border-radius: 3px;
+}
+
+.nav-item i {
+  font-size: 20px;
+  margin-bottom: 4px;
+}
+
+/* Animation */
+@keyframes fadeIn {
+  from { opacity: 0; }
+  to { opacity: 1; }
+}
+
+@keyframes slideUp {
+  from { transform: translateY(20px); opacity: 0; }
+  to { transform: translateY(0); opacity: 1; }
+}
+
+/* Responsive */
+@media (max-width: 480px) {
+  .category-icon {
+    width: 42px;
+    height: 42px;
+    font-size: 18px;
+  }
+  
+  .featured-title {
+    font-size: 18px;
+  }
+  
+  .featured-desc {
+    font-size: 13px;
+  }
+}

+ 41 - 0
menu-web/src/modules/food/mobile/page-story/page-story.spec.ts

@@ -0,0 +1,41 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { PageStory } from './page-story';
+
+describe('PageStory', () => {
+  let component: PageStory;
+  let fixture: ComponentFixture<PageStory>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [PageStory]
+    }).compileComponents();
+    
+    fixture = TestBed.createComponent(PageStory);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should open story modal', () => {
+    const storyId = 1;
+    component.openStoryModal(storyId);
+    expect(component.isModalOpen()).toBeTrue();
+    expect(component.selectedStoryId()).toBe(storyId);
+  });
+
+  it('should close modal', () => {
+    component.openStoryModal(1);
+    component.closeModal();
+    expect(component.isModalOpen()).toBeFalse();
+  });
+
+  it('should set active category', () => {
+    const categoryId = 2;
+    component.setActiveCategory(categoryId);
+    const activeCategory = component.categories.find(c => c.active);
+    expect(activeCategory?.id).toBe(categoryId);
+  });
+});

+ 160 - 0
menu-web/src/modules/food/mobile/page-story/page-story.ts

@@ -0,0 +1,160 @@
+import { Component, signal } from '@angular/core';
+import { CommonModule, DecimalPipe } from '@angular/common';
+
+interface Category {
+  id: number;
+  name: string;
+  icon: string;
+  active?: boolean;
+}
+
+interface Story {
+  id: number;
+  title: string;
+  image: string;
+  content?: string;
+  views: number;
+  likes: number;
+  timeAgo?: string;
+  comments?: number;
+  tag?: string;
+  description?: string;
+}
+
+@Component({
+  selector: 'app-page-story',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './page-story.html',
+  styleUrls: ['./page-story.scss']
+})
+export class PageStory {
+  isModalOpen = signal(false);
+  selectedStoryId = signal<number | null>(null);
+  
+  // 定义 categories 数组
+  categories: Category[] = [
+    { id: 1, name: '全部', icon: 'fas fa-utensils', active: true },
+    { id: 2, name: '历史', icon: 'fas fa-history' },
+    { id: 3, name: '地域', icon: 'fas fa-map-marked-alt' },
+    { id: 4, name: '食材', icon: 'fas fa-seedling' },
+    { id: 5, name: '名厨', icon: 'fas fa-user-tie' }
+  ];
+
+  // 定义 featuredStory 对象
+  featuredStory: Story = {
+    id: 0,
+    title: '中华美食五千年',
+    image: 'https://images.unsplash.com/photo-1546069901-ba9599a7e63c?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80',
+    tag: '本周精选',
+    description: '从远古时期的火烤食物到现代的分子料理,中华美食经历了漫长而精彩的发展历程...',
+    views: 5842,
+    likes: 2156,
+    comments: 328
+  };
+
+  // 定义 stories 数组
+  stories: Story[] = [
+    {
+      id: 1,
+      title: '川菜的麻辣传奇',
+      image: 'https://images.unsplash.com/photo-1563245372-f21724e3856d?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80',
+      views: 3256,
+      likes: 1203,
+      timeAgo: '3天前',
+      content: `
+        <p>川菜,中国八大菜系之一,以其麻辣鲜香著称于世。它的历史可以追溯到秦汉时期,但真正形成特色是在明清时期。</p>
+        <p>辣椒虽然是明代才传入中国的外来物种,但却在四川这片土地上找到了最佳的生长环境,并与当地的花椒完美结合,创造了独特的"麻辣"风味。</p>
+        <p>传说清朝乾隆年间,一位四川厨师在制作水煮鱼时,不小心将大量辣椒和花椒倒入锅中,本以为会毁掉这道菜,没想到却意外创造了令人难忘的美味。从此,"麻辣"成为了川菜的标志性味道。</p>
+        <p>现代川菜已经发展出24种味型,除了麻辣外,还有鱼香、怪味、红油等独特风味。每一道经典川菜背后,都有一段有趣的历史故事。</p>
+      `
+    },
+    {
+      id: 2,
+      title: '披萨的世界之旅',
+      image: 'https://images.unsplash.com/photo-1513104890138-7c749659a591?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80',
+      views: 2856,
+      likes: 987,
+      timeAgo: '1周前',
+      content: `
+        <p>披萨起源于意大利那不勒斯,最初是穷人的食物。简单的面团配上当地产的番茄和马苏里拉奶酪,就成了这道风靡全球的美食。</p>
+        <p>19世纪末,随着意大利移民涌入美国,披萨开始在美国流行。二战后,美国大兵从欧洲战场带回对披萨的喜爱,加速了披萨在全球的传播。</p>
+        <p>有趣的是,意大利人坚持传统披萨的做法,而美国人则不断创新,发明了芝加哥深盘披萨、纽约风格披萨等多种变体。</p>
+        <p>今天,披萨已经成为全球最受欢迎的食物之一,每年全球消费约50亿个披萨,仅美国就有约7万家披萨店。</p>
+      `
+    },
+    {
+      id: 3,
+      title: '寿司的艺术与哲学',
+      image: 'https://images.unsplash.com/photo-1551504734-5ee1c4a1479b?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80',
+      views: 4125,
+      likes: 1856,
+      timeAgo: '2周前',
+      content: `
+        <p>寿司起源于东南亚的发酵鱼保存方法,后传入日本并发展为今天的形态。江户时代(17-19世纪)是寿司发展的重要时期。</p>
+        <p>最初的寿司是发酵鱼和米饭一起食用,后来发展为将新鲜鱼放在醋饭上的"江户前寿司",这就是现代寿司的雏形。</p>
+        <p>寿司大师小野二郎曾说:"寿司不仅仅是食物,它是一种艺术,一种哲学。"制作寿司需要多年的训练,从煮饭到切鱼,每一个细节都至关重要。</p>
+        <p>2008年,寿司被联合国教科文组织列为非物质文化遗产,这是对寿司文化和艺术的最高认可。</p>
+      `
+    },
+    {
+      id: 4,
+      title: '法式甜点的浪漫',
+      image: 'https://images.unsplash.com/photo-1563805042-7684c019e1cb?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80',
+      views: 3542,
+      likes: 1654,
+      timeAgo: '3周前',
+      content: `
+        <p>法国甜点以其精致和浪漫闻名世界。从中世纪的蜂蜜蛋糕到文艺复兴时期的奶油甜点,再到现代的分子甜点,法国一直是甜点创新的中心。</p>
+        <p>马卡龙最初是意大利修女发明的杏仁饼干,后来被法国王后凯瑟琳·德·美第奇带到法国,经过法国厨师的改良,成为今天的样子。</p>
+        <p>法国大革命后,许多宫廷厨师失业后开设甜点店,使得原本只有贵族才能享用的甜点走入寻常百姓家,促进了法国甜点业的发展。</p>
+        <p>今天,法国甜点不仅是一种美食,更是一种生活艺术的象征,代表着法国人对美好生活的追求。</p>
+      `
+    }
+  ];
+
+  // 设置活动分类
+  setActiveCategory(id: number) {
+    this.categories.forEach(cat => cat.active = cat.id === id);
+  }
+
+  // 卡片点击动画效果
+  cardClick(event: Event) {
+    const card = event.currentTarget as HTMLElement;
+    card.style.transform = 'scale(0.98)';
+    setTimeout(() => {
+      card.style.transform = '';
+    }, 200);
+  }
+
+  // 打开故事模态框
+  openStoryModal(storyId: number) {
+    this.selectedStoryId.set(storyId);
+    this.isModalOpen.set(true);
+    document.body.style.overflow = 'hidden';
+  }
+
+  // 关闭模态框
+  closeModal() {
+    this.isModalOpen.set(false);
+    document.body.style.overflow = '';
+  }
+
+  // 获取选中的故事
+  getSelectedStory() {
+    return this.stories.find(story => story.id === this.selectedStoryId()) || this.stories[0];
+  }
+
+  // 分享故事
+  shareStory() {
+    alert('分享功能即将打开');
+  }
+
+  // 点赞故事
+  likeStory() {
+    const story = this.getSelectedStory();
+    if (story) {
+      story.likes++;
+    }
+  }
+}

+ 6 - 2
menu-web/src/styles.scss

@@ -10,8 +10,12 @@
   --gray: #666;
   --light-gray: #f5f5f5;
   --white: #fff;
-  --card-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
-  --transition: all 0.3s ease;
+  --card-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); // 确保这个变量存在
+  --transition: all 0.3s ease; // 确保这个变量存在
+  
+  // 添加其他组件可能需要的变量
+  --black: #000;
+  --text-color: #333;
 }
 
 * {