软件工程项目开发Vue3实践:Element Plus与Vue Router德使用

1. 实验目的

本实验旨在帮助学生进一步优化"惊喜一餐"页面,构建应用中的常用页面(如首页、餐厅详情页、用户信息页等),提升界面美观性和交互性。通过合理的页面布局与细节优化,学生将掌握前端页面设计的基本技巧,并熟悉Vue项目的常见目录结构,为后续的前后端联动打下坚实基础。

2. 实验要求

  1. 在实验8中餐厅列表页面的基础上进行优化,改善页面布局、信息展示与交互效果。
  2. 使用Element Plus设计和实现"惊喜一餐"应用的常用页面,包括首页、餐厅详情页和用户信息页,确保页面样式统一、功能清晰。
  3. 使用Vue Router实现页面之间的跳转,并设计全局导航栏,为用户提供便捷的页面切换功能。
  4. 将最终完成的代码推送至自己的"惊喜一餐"Gitee项目中,提交时需包含清晰的描述性信息。

3. 实验内容

3.1. 常见Vue项目结构

  • 了解Vue 3项目的基础目录结构,掌握各目录的功能及文件的作用。
  • 熟悉目录中的核心内容及其组织方式,包括components、pages、router、assets等。

3.2. 构建"惊喜一餐"常用页面

  • 设计并实现"惊喜一餐"应用的常用页面,包括首页、餐厅详情页和用户信息页等。页面需结合Element Plus组件,保持风格一致。
  • 具体页面包括:
    • 首页用于展示应用的主要功能,提供快速导航入口。具体内容包括欢迎语(如"欢迎使用惊喜一餐!")、推荐餐厅(随机展示几家高评分餐厅)等。
    • 餐厅详情页用于展示单个餐厅的详细信息。具体内容包括餐厅名称、图片、菜品列表(包括菜品名称、价格、图片等)、用户评分与评价等。
    • 用户信息页用于展示当前用户的个人信息和收藏内容。具体内容包括用户基本信息、收藏的餐厅列表、历史操作记录(如评论、收藏等)等。

3.3. 实现页面跳转和导航

  • 使用Vue Router实现页面之间的跳转,确保各页面可以互相访问,为用户提供便捷的页面切换功能。
  • 导航栏需包含首页、餐厅、用户信息等跳转入口, 确保导航栏在每个页面中可见,并能正确跳转到对应页面。

3.4. 提交代码至Gitee

  • 将最终完成的代码推送至自己的"惊喜一餐"Gitee项目中。
  • 提交信息应包含描述性文字(如"优化餐厅列表页面,新增详情页"等)。
  • 确保项目代码清晰完整,无多余文件。

4. 操作记录

4.1. 常见Vue项目结构

  • 了解Vue 3项目的基础目录结构,掌握各目录的功能及文件的作用。
  • 熟悉目录中的核心内容及其组织方式,包括components、pages、router、assets等。
  1. 项目根目录

本地根目录截图:

Vue 3项目中常见的目录结构的详细说明:

    • node_modules/:存放项目依赖的第三方库和模块。
    • public/:存放静态资源文件,如 index.htmlrobots.txt 等,这些文件不会被 Webpack 处理。
    • src/:存放项目的源代码。
    • tests/:存放测试文件。
    • package.json:定义项目的依赖、脚本和元数据。
    • package-lock.jsonyarn.lock:记录确切的依赖版本,确保在不同环境中的一致性。
    • babel.config.js.babelrc:Babel 配置文件,用于转译 ES6+ 代码。
    • vue.config.jsvite.config.js:Vue CLI 或 Vite 的配置文件。
  1. src/(源代码目录)
    • assets/:存放静态资源,如图片、样式文件等,这些文件会被 Webpack 处理。
    • components/:存放 Vue 组件,通常是可复用的 UI 组件。
    • pages/:存放页面级别的组件,通常用于服务端渲染(SSR)或多页面应用。
    • router/:存放路由配置文件,用于定义应用的路由规则。
    • store/:存放 Vuex 状态管理文件,用于管理全局状态。
    • views/:存放视图级别的组件,通常用于组织页面结构。
    • App.vue:根组件,作为所有页面的顶层组件。
    • main.jsmain.ts:入口文件,用于创建 Vue 实例并挂载到 DOM 上。
    • tsconfig.json:TypeScript 配置文件。
  1. components/(组件目录)
    • 存放可复用的 Vue 组件,通常按照功能或页面进行分组。
  1. pages/(页面目录)
    • 存放页面级别的组件,每个文件或子目录对应一个页面。
  1. router/(路由目录)
    • index.jsindex.ts:定义路由配置,使用 Vue Router。
  1. assets/(资源目录)
    • 存放静态资源,如 CSS、图片、字体等。
  1. store/(状态管理目录)
    • 使用 Vuex 进行状态管理,存放状态、getters、mutations 和 actions。
  1. views/(视图目录)
    • 存放视图级别的组件,通常用于组织页面结构,如布局组件。
  1. App.vue
    • 根组件,作为所有页面的顶层组件,通常包含全局样式和混入。
  1. main.jsmain.ts
    • 入口文件,用于创建 Vue 实例并挂载到 DOM 上。

4.2. 构建"惊喜一餐"常用页面

  • 设计并实现"惊喜一餐"应用的常用页面,包括首页、餐厅详情页和用户信息页等。页面需结合Element Plus组件,保持风格一致。
  • 具体页面包括:
    • 首页用于展示应用的主要功能,提供快速导航入口。具体内容包括欢迎语(如"欢迎使用惊喜一餐!")、推荐餐厅(随机展示几家高评分餐厅)等。
    • 餐厅详情页用于展示单个餐厅的详细信息。具体内容包括餐厅名称、图片、菜品列表(包括菜品名称、价格、图片等)、用户评分与评价等。
    • 用户信息页用于展示当前用户的个人信息和收藏内容。具体内容包括用户基本信息、收藏的餐厅列表、历史操作记录(如评论、收藏等)等。

具体代码架构分析以及功能实现原理分析:

4.2.1. 一、项目结构
4.2.2. 二、 main.js

我在main.js里面导入了createApp函数、根组件App.vue、Vue Router的配置、并且从Element Plus UI库中导入了一系列组件和CSS样式文件,此外还创建了一个新的Vue应用实例。

总的来说,我完成了初始化Vue应用,注册路由和Element Plus组件,并将应用挂载到DOM中,使其开始运行。

主要内容的详细分析:

  1. import { createApp } from 'vue'; 这行代码从Vue包中导入了createApp函数,这个函数用于创建一个新的Vue应用实例。

  2. import App from './App.vue'; 这行代码导入了根组件App.vue,这是Vue应用的入口组件,所有的页面和组件都将被嵌套在这个组件内。

  3. import router from './router'; 这行代码导入了Vue Router的配置,它允许我们在不同的页面之间进行导航。

  4. const app = createApp(App); 这行代码创建了一个新的Vue应用实例,并将其赋值给变量appApp组件被作为根组件传递给createApp函数。

  5. app.component('el-button', ElButton); 这些行代码将Element Plus的组件注册到Vue应用中。每个app.component调用都是将一个Element Plus组件注册到Vue中,这样它们就可以在任何组件的模板中通过自定义元素的形式使用。

  6. app.mount('#app'); 这行代码告诉Vue应用挂载到DOM中,#app是HTML中一个元素的ID,Vue应用将替换这个元素的内容。这标志着Vue应用的启动。

    import './assets/main.css';

    import { createApp } from 'vue';
    import App from './App.vue';
    import ElementPlus from 'element-plus';
    import 'element-plus/dist/index.css';
    import router from './router/index.js';
    import store from './store/index.js';
    import { ElButton, ElCard, ElImage, ElMenu, ElMenuItem, ElHeader, ElMain, ElAside, ElFooter } from 'element-plus';

    const app = createApp(App)
    .use(ElementPlus)
    .use(router)
    .use(store)
    .component('el-button', ElButton)
    .component('el-card', ElCard)
    .component('el-image', ElImage)
    .component('el-menu', ElMenu)
    .component('el-menu-item', ElMenuItem)
    .component('el-header', ElHeader)
    .component('el-main', ElMain)
    .component('el-aside', ElAside)
    .component('el-footer', ElFooter)
    .mount('#app');

4.2.3. 三、router/index.js

我在./src/router/index.js文件里面使用Vue Router创建一个路由器的配置文件,它定义了应用中的路由规则,允许用户在不同的页面间导航。

为这三个页面:Home、RestaurantDetail、UserInfo定义了路由规则,并创建了一个路由器实例,这个实例将在应用中用于控制页面的导航。通过这种方式,用户可以通过URL访问不同的页面,并且Vue Router会根据URL的变化来渲染相应的组件。

主要内容的详细分析:

  1. 定义路由:

const routes = [...]; 这个数组定义了应用中的路由规则。每个路由规则都是一个对象,包 含以下属性:

    • path:定义了路由的路径。例如,'/'是首页的路径,'/restaurant/:id'是餐厅详情页的路径,其中:id是一个动态参数,用于匹配URL中的餐厅ID。
    • name:为路由规则提供一个名称,这可以在编程式导航中使用。
    • component:指定了哪个组件应该被渲染在该路由下。
  1. 创建路由器实例:

const router = createRouter({ history: createWebHistory(), routes }); 这 行代码创建了一个新的路由器实例,并传入一个配置对象。这个对象包含两个属性:

    • history:指定了使用createWebHistory创建的历史模式实例,它允许应用使用HTML5的pushState和replaceState方法来管理历史记录,而不是传统的hash(#)模式。
    • routes:包含了上面定义的所有路由规则。

详细代码:

复制代码
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../components/Home.vue';
import RestaurantDetail from '../components/RestaurantDetail.vue';
import UserInfo from '../components/UserInfo.vue';

const routes = [
{
    path: '/',
    name: 'Home',
    component: Home
},
{
path: '/restaurant/:id',
name: 'RestaurantDetail',
component: RestaurantDetail
},
{
path: '/user',
name: 'UserInfo',
component: UserInfo
}
];

const router = createRouter({
    history: createWebHistory(),
    routes: [
    { path: '/', component: Home },
    { path: '/restaurant', component: RestaurantDetail },
    { path: '/user', component: UserInfo },
    ],
});

export default router;
4.2.4. 四、App.vue

我在APP.vue里面定义了一个包含导航栏和主要内容区域的页面布局。该组件使用了 Element Plus 库来构建用户界面,并结合 Vue Router 来实现页面间的导航。

它主要用于构建应用的主布局,包括一个固定的顶部导航栏和一个可变的内容区域。导航栏允许用户在不同页面之间切换,而内容区域则根据当前选择的页面展示相应的内容。通过这种方式,用户可以在不重新加载整个页面的情况下浏览不同的页面。

主要内容的详细分析:

模板部分 (<template>)

  • 整体结构:
    • 组件的整体结构被包裹在一个 <div> 标签内,这通常用于设置一些全局样式或行为。
  • 头部 (Header):
    • 使用了 el-headerel-menu 组件来自 Element Plus 库,创建了一个水平导航菜单。
    • 导航菜单中包含了三个菜单项(el-menu-item):首页、餐厅、用户信息。
    • 每个菜单项都通过 <router-link> 组件链接到相应的路由路径:/, /restaurant, 和 /user。这些链接将根据 Vue Router 的配置加载对应的组件。
  • 主要内容区域 (Main Content):
    • 使用了 el-main 组件作为内容的主要容器。
    • el-main 内部放置了一个 <router-view />,这是 Vue Router 提供的一个特殊组件,用于渲染当前路由所对应的组件。

脚本部分 (<script setup>)

  • 导入组件:
    • 从不同的文件路径导入了多个 Vue 组件:HelloWorld, TheWelcome, Home, RestaurantDetail, 和 UserInfo。虽然这些组件在模板中没有直接使用,但它们可能是在其他地方被引用或者为后续开发准备的。
  • 脚本类型:
    • 使用了 <script setup> 语法糖,这是一种简化的 Vue 3 Composition API 的写法,可以更简洁地编写逻辑代码。

样式部分 (<style scoped>)

  • 自定义样式:
    • .header-custom 类设置了顶部导航栏的背景颜色和阴影效果。
    • .mode-custom 类设置了导航菜单的背景图片、文字颜色及阴影效果。
    • .index 类定义了一些背景相关的样式属性,如背景图覆盖整个容器、居中显示且不重复等。此外还设置了高度为视口的高度,以及使用 Flexbox 实现子元素的居中对齐。

详细代码:

复制代码
<template>
  <div >
    <el-header class="header-custom">
      <el-menu mode="horizontal" class="mode-custom">
        <el-menu-item index="1">
          <router-link to="/">首页</router-link>
        </el-menu-item>
        <el-menu-item index="2">
          <router-link to="/restaurant">餐厅</router-link>
        </el-menu-item>
        <el-menu-item index="3">
          <router-link to="/user">用户信息</router-link>
        </el-menu-item>
      </el-menu>
    </el-header>

    <el-main>
      <router-view />
    </el-main>
  </div>
</template>

<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
import Home from './components/Home.vue'
import RestaurantDetail from './components/RestaurantDetail.vue'
import UserInfo from './components/UserInfo.vue'
</script>

<style scoped>
.header-custom {
  background-color: #b68516;
  box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
}
.mode-custom {
  background-image: url('src/assets/restcard.png');
  color: #ffffff; /* 设置头部文字颜色 */
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.index {

   background-size: cover; /* 背景图覆盖整个容器 */
   background-position: center; /* 背景图居中显示 */
   background-repeat: no-repeat; /* 防止背景图重复 */
   background-color: #fade7a; /* 设置背景颜色为绿色,并允许背景图透过 */
   height: 100vh; /* 视口高度 */
   display: flex; /* 使用 Flexbox 布局 */
   flex-direction: column; /* 垂直排列子元素 */
   align-items: center; /* 垂直居中对齐子元素 */
   justify-content: center; /* 水平居中对齐子元素 */
}

/* 其他样式保持不变 */
</style>
4.2.5. 五、components/Home.vue
5.1具体要求:

首页用于展示应用的主要功能,提供快速导航入口。具体内容包括欢迎语(如"欢迎使用惊喜一餐!")、推荐餐厅(随机展示几家高评分餐厅)等。

5.2实现过程:

1、页面跳转:

  • 当用户点击某个餐厅的"查看详情"按钮时,
  • viewDetails 方法会被触发。这个方法使用 this.$router.push 将用户导航到 RestaurantDetails 路由,并传递餐厅的 id 作为参数。这需要在路由配置中定义相应的路由

2、搜索功能:

  • 搜索栏和搜索按钮目前只是静态的 UI 元素,要实现搜索功能,后期我会在 methods 中添加一个处理搜索的方法,例如 handleSearch,并在 el-input 上绑定 @input@change 事件,在 el-button 上绑定 @click 事件。

3、搜索栏:

  • 使用了 el-input 组件创建了一个搜索输入框,设置了占位符文本"搜索餐厅..."和搜索图标。
  • 旁边有一个 el-button 组件作为搜索按钮,类型为 primary,类名为 search-button

4、欢迎卡片:

  • 使用了 el-card 组件创建了一个欢迎卡片,显示了欢迎语和一段描述文字。
  • 欢迎卡片内有一个标题 (<h2>) 和一段描述 (<p>),介绍了应用并提示用户查看推荐的餐厅。

5、推荐餐厅列表:

  • 使用了 el-rowel-col 组件来创建一个响应式的网格布局,用于展示推荐餐厅。
  • 通过 v-for 指令遍历 recommendedRestaurants 数组,为每个餐厅生成一个 el-card 组件。
  • 每个餐厅卡片包含:
    • 一张显示餐厅的图片。
    • 餐厅信息 (<div class="restaurant-info">),包括餐厅名称、评分、描述、位置和查看详情按钮。
  • 查看详情按钮 (<el-button>) 绑定了 @click 事件,点击时会调用 viewDetails 方法,并传递餐厅的 ID。
5.3运行效果:

页面实现了欢迎语(如"欢迎使用惊喜一餐!")、推荐餐厅(随机展示几家高评分餐厅):

5.4详细代码:
复制代码
<template>
  <div class="index">
    <el-container>
      <el-main>
        <el-input
            class="search-bar"
            placeholder="搜索餐厅..."
            suffix-icon="el-icon-search"
        ></el-input>
        <el-button type="primary" class="search-button">搜索</el-button>
        <el-card class="welcome-card">
          <h2>欢迎使用惊喜一餐!</h2>
          <p>圣诞节到了,快来看看我们为你精心推荐的餐厅吧!</p>
        </el-card>
        <el-row :gutter="20" class="restaurant-list">
          <el-col v-for="restaurant in recommendedRestaurants" :key="restaurant.id" :span="8">
            <el-card class="restaurant-card">
              <img :src="restaurant.image" class="restaurant-image" alt="餐厅图片" />
              <div class="restaurant-info">
                <h3 class="restaurant-name">{{ restaurant.name }}</h3>
                <p class="restaurant-mask">评分: {{ restaurant.rating }}</p>
                <p class="restaurant-description"> {{ restaurant.description }}</p>
                <p class="restaurant-location">{{ restaurant.location }}</p>
                <el-button class="detail-button" @click="viewDetails(restaurant.id)">查看详情</el-button>
              </div>
            </el-card>
          </el-col>
        </el-row>
      </el-main>
      <el-footer>
        <p>© 2024 惊喜一餐. 保留所有权利.</p>
      </el-footer>
    </el-container>
  </div>
</template>

<script>
export default {
  data() {
    return {
      recommendedRestaurants: [
        {
          id: 1,
          name: '茉莉花语茶餐厅',
          rating: 4.7,
          location: '镜湖区中山路11号苏宁广场8楼',
          description: '⭐️镜湖区西餐好评榜第一',
          image: 'src/assets/茶餐厅.png',
        },
        {
          id: 2,
          name: '岘港·越南料理',
          rating: 4.8,
          location: '镜湖区镜湖路14号万达广场4楼',
          description: '⭐️提供精致的越南料理',
          image: 'src/assets/越南料理.png',
        },
        {
          id: 3,
          name: '班叮叮·炉鸡煲',
          rating: 4.9,
          location: '镜湖区中山路11号苏宁广场5楼',
          description: '⭐️港式火锅',
          image: 'src/assets/炉鸡煲.png',
        }
      ],
    };
  },
  methods: {
    viewDetails(id) {
      this.$router.push({ name: 'RestaurantDetails', params: { id } });
    },
  },
};
</script>

<style scoped>
.index {
  background-color: #fade7a;
  background-image: url('src/assets/homebank.JPG');
  background-size: cover;
  background-position: center;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.welcome-card {
  text-align: center;
  background-image: url('src/assets/homebank.JPG');

  background-color: #ff5b4d;
  margin-bottom: 20px;
  font-size: 15px;
  color: #a67603;
}


.restaurant-list {
  margin-top: 10px;

}

.restaurant-card {
  text-align: center;
  background-image: url('src/assets/homebank.JPG');
  background-color: #ff5b4d;
  margin: 10px;
}

.restaurant-image {
  width: 100%;
  height: auto;
}

.restaurant-name {
  font-size: 15px;
  font-weight: bold;
}

.restaurant-mask {
  font-size: 13px;
  background: -webkit-linear-gradient(#FFD700, #9b7601);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.restaurant-location {
  font-size: 12px;
  color: #666;
}

.restaurant-info {
  padding: 10px;
}

.search-bar {
  width: 50%;
  background-color: #f8ef7e;
  margin-top: 10px;
  margin-bottom: 10px;
}
.detail-button{
  background-color: #f8ef7e;
}
.search-button {
  background-color: #f8ef7e;
  margin-top: 10px;
  margin-left: 10px;
  margin-bottom: 10px;
}
body {
  background-color: #a8d8b4; /* 浅绿色 */
}
</style>
5.5代码详细分析

1、模板部分 (<template>)

整体结构:

  • 使用了 el-container 组件来组织页面布局,其中包含了 el-mainel-footer 两个部分。

页脚:

  • 使用了 el-footer 组件来创建页脚,显示版权信息。

2、脚本部分 (<script>)

  • 数据:
    • data 函数返回一个对象,包含 recommendedRestaurants 数组,存储了推荐餐厅的信息。每个餐厅对象包含 id, name, rating, location, description, 和 image 属性。
  • 方法:
    • viewDetails(id) 方法:当用户点击查看详情按钮时,该方法会被调用。它使用 this.$router.push 方法将用户导航到特定餐厅的详情页面。这里假设存在一个名为 RestaurantDetails 的路由,并且该路由接受一个 id 参数。

3、样式部分 (<style scoped>)

具体看代码就欧克,此处略

4.2.6. 六、components/RestaurantDetail.vue
6.1具体要求:

餐厅详情页用于展示单个餐厅的详细信息。具体内容包括餐厅名称、图片、菜品列表(包括菜品名称、价格、图片等)、用户评分与评价等。

6.2实现过程:

我使用Vue框架和Element-Plus UI库构建的餐厅详情页的前端代码,通过组合HTML模板、JavaScript逻辑和CSS样式来展示单个餐厅的详细信息。下面是对代码的详细分析:

1、HTML模板部分(<template>

  1. 店铺信息
    • 使用<el-card>组件来展示餐厅的基本信息,包括名称、位置和提示信息。
    • {``{ restaurant.name }}:展示餐厅名称。
    • {``{ restaurant.location }}{``{ restaurant.tips }}:展示餐厅的位置和一些提示信息。
    • <img :src="restaurant.image" ... />:展示餐厅的图片,图片地址通过restaurant.image绑定。
  1. 菜品列表
    • 使用<el-table>组件来展示菜品列表。
    • <el-table-column>定义了表格的列,分别展示菜品名称、价格和图片。
    • prop="name"prop="price"prop="image":分别对应菜品的名称、价格和图片。
    • template #default="{ row }":自定义图片列的展示方式,使用row.image来展示每道菜的图片。
  1. 用户评价
    • 同样使用<el-table>组件来展示用户评价。
    • <el-table-column>定义了表格的列,展示用户名称、评价内容和评分。

2、JavaScript逻辑部分(<script setup>

  1. 数据定义
    • 使用reactive函数定义了一个响应式的restaurant对象,包含了餐厅的详细信息,如名称、位置、图片、菜品列表和用户评价等。
  1. 分页计算属性
    • paginatedDishespaginatedReviews是两个计算属性,它们根据当前页码和每页显示的数量来计算并返回当前页面应该显示的菜品列表和用户评价。
  1. 分页变量
    • currentPagepageSizereviewCurrentPagereviewPageSize是响应式变量,用于控制当前页码和每页显示的条目数。

3、CSS样式部分(<style scoped>

  1. 整体样式
    • .restaurant-detail定义了整个详情页的背景颜色和布局。
    • .restaurant-info.menu-list.reviews定义了信息卡片的样式,包括背景颜色和文本对齐。
  1. 图片样式
    • .restaurant-image.dish-image定义餐厅图片和菜品图片的样式,包括尺寸和背景色。
  1. 其他样式
    • .restaurant-name.restaurant-location定义了餐厅名称和位置的背景颜色和边距。
6.3运行效果:
6.4详细代码:
复制代码
<template>
  <div class="restaurant-detail">
    <!-- 店铺信息 -->
    <el-card class="restaurant-info">
      <div class="restaurant-name">
        <h1>{{ restaurant.name }}</h1>
      </div>
      <div class="restaurant-location">
        <p>🌟💫 {{ restaurant.location }}</p>
        <p>🌈🌈{{ restaurant.tips }}</p>
      </div>
      <img :src="restaurant.image" class="restaurant-image" alt="餐厅图片" />
    </el-card>

    <!-- 菜品列表 -->
    <el-card class="menu-list">
      <h2>菜品列表</h2>
      <el-table :data="paginatedDishes">
        <el-table-column prop="name" label="菜品名称"></el-table-column>
        <el-table-column prop="price" label="价格"></el-table-column>
        <el-table-column prop="image" label="图片">
          <template #default="{ row }">
            <img :src="row.image" class="dish-image" alt="菜品图片" />
          </template>
        </el-table-column>
      </el-table>
    </el-card>

    <!-- 用户评价 -->
    <el-card class="reviews">
      <h2>用户评价</h2>
      <el-table :data="paginatedReviews">
        <el-table-column prop="user" label="用户"></el-table-column>
        <el-table-column prop="comment" label="评价"></el-table-column>
        <el-table-column prop="rating" label="评分">
        </el-table-column>
      </el-table>
    </el-card>
  </div>
</template>

<script setup>
  import { ref, reactive, computed } from 'vue';
  import { ElCard, ElTable, ElTableColumn, ElRate, ElPagination } from 'element-plus';

  const restaurant = reactive({
    id: 1,
    name: 'GANSO元祖蛋糕',
    rating: 4.8,
    location: '弋江区花津南路83号',
    tips: '弋江区生日蛋糕好评第三名🎂🎂',
    image: 'src/assets/元祖.png',
    dishes: [
      { name: '【白羊座】阳光朵朵动物奶油生日蛋糕', price: '200', image: 'src/assets/白羊座.png' },
      { name: '圆梦启航鲜奶生日蛋糕(动物奶油)', price: '210', image: 'src/assets/圆梦启航.png' },
      {name: '水果之恋鲜奶蛋糕 ', price: '230', image: 'src/assets/水果之恋.png'
      }
      // 菜品数据...
    ],
    reviews: [
      {id: 1, user: '维尼', comment: '非常好吃', rating:'⭐️⭐️⭐️⭐️⭐️⭐️'},
      {id: 2, user: '跳跳虎', comment: '服务不错', rating: '⭐️⭐️⭐️⭐️⭐️'},
      // 用户评价数据...
    ],
    averageRating: 4.8
  });

  const currentPage = ref(1);
  const pageSize = ref(5);
  const reviewCurrentPage = ref(1);
  const reviewPageSize = ref(5);


  const paginatedDishes = computed(() => {
    const start = (currentPage.value - 1) * pageSize.value;
    const end = start + pageSize.value;
    return restaurant.dishes.slice(start, end);
  });


  const paginatedReviews = computed(() => {
    const start = (reviewCurrentPage.value - 1) * reviewPageSize.value;
    const end = start + reviewPageSize.value;
    return restaurant.reviews.slice(start, end);
  });


</script>

<style scoped>
  .restaurant-detail {
    background-color: #e1ce6c;
    background-image: url('src/assets/homebank.JPG');
    padding: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .restaurant-info {
    text-align: center;
    background-color: #f5de8b;
  }

  .restaurant-name {
    background-color: #fadb7a;
    margin-bottom: 7px;
  }

  .restaurant-location {
    background-color: #fadb7a;
    margin-bottom: 3px;
  }

  .restaurant-image {
  width: 80px;
  height: auto;
  border-radius: 10px;
  background-color: #fadb7a;
}

.menu-list {
  background-image: url('src/assets/homebank.JPG');
  margin-top: 20px;
  width: 100%;
  background-color: #e1ce6c;
}
.reviews{
  background-image: url('src/assets/homebank.JPG');
  margin-top: 20px;
  width: 100%;
  background-color: #e1ce6c;
}

.dish-image {
  width: 100px; /* 根据需要调整大小 */
  height: auto;
  background-color: #fadb7a;
}

.reviews {
  margin-top: 20px;
  background-color: #fadb7a;
}
</style>
4.2.7. 七、components/UserInfo.vue
7.1具体要求:

用户信息页用于展示当前用户的个人信息和收藏内容。具体内容包括用户基本信息、收藏的餐厅列表、历史操作记录(如评论、收藏等)等。

7.2实现过程:

我使用Vue框架和Element-Plus UI库构建的个人信息页面的前端代码,通过组合HTML模板、JavaScript逻辑和CSS样式来展示当前用户的个人信息、收藏的餐厅列表以及历史操作记录。

下面是对代码的详细分析:

1、HTML模板部分(<template>

  1. 用户信息展示
    • 使用<el-card>组件来展示用户信息。
    • <el-avatar>组件展示用户的头像,头像地址通过user.avatar绑定。
    • 通过插值表达式{``{ user.name }}{``{ user.email }}{``{ user.birthday }}{``{ user.favoriteFood }}展示用户的基本信息。
  1. 收藏的餐厅列表
    • 使用<el-table>组件来展示用户收藏的餐厅列表。
    • <el-table-column>定义了表格的列,分别展示餐厅名称、照片、评分、距离和配送时间。
    • 使用插槽<template #default="{ row }">自定义图片列的展示方式,使用row.image来展示每家餐厅的图片。
  1. 历史操作记录
    • 同样使用<el-table>组件来展示用户的历史评价记录。
    • <el-table-column>定义了表格的列,展示评价的时间、餐厅名字、具体评价和评分。

2、JavaScript逻辑部分(<script setup>

  1. 数据定义
    • 使用ref函数定义了一个响应式的user对象,

    • 包含了用户的基本信息、收藏的餐厅列表和历史操作记录。

  1. 分页属性
    • currentPagepageSize是响应式变量,用于控制当前页码和每页显示的条目数。
  1. 分页计算属性
    • paginatedFavoritespaginatedHistory是两个计算属性,它们根据当前页码和每页显示的数量来计算并返回当前页面应该显示的收藏餐厅列表和历史操作记录。
  1. 分页改变处理函数
    • handlePageChange函数用于处理分页变化,更新currentPage的值。

3、CSS样式部分(<style scoped>

对:用户信息卡片样式、用户详细信息样式、收藏餐厅和历史记录样式等进行了设计,具体看代码。

7.3运行效果:
7.4详细代码:
复制代码
<template>
  <el-card class="user-info-card">
    <h2>用户信息</h2>
    <div class="user-avatar">
      <el-avatar size="large" :src="user.avatar"></el-avatar>
    </div>
    <div class="user-details">
      <p>用户名: <strong>{{ user.name }}</strong></p>
      <p>邮箱: <strong>{{ user.email }}</strong></p>
      <p>生日: <strong>{{ user.birthday }}</strong></p>
      <p>最喜欢的食物: <strong>{{ user.favoriteFood }}</strong></p>
    </div>
    <!-- 收藏的餐厅列表 -->
    <h3>收藏的餐厅列表</h3>
    <el-table :data="paginatedFavorites">
      <el-table-column prop="name" label="餐厅名称" width="180"></el-table-column>
      <el-table-column prop="image" label="照片" width="100">
        <template #default="{ row }">
          <img :src="row.image" class="restaurant-image" alt="餐厅图片" />
        </template>
      </el-table-column>
      <el-table-column prop="rating" label="评分" width="80"></el-table-column>
      <el-table-column prop="distance" label="距离" width="100"></el-table-column>
      <el-table-column prop="deliveryTime" label="配送时间" width="120"></el-table-column>
    </el-table>
    <!-- 历史操作记录 -->
    <h3>用户评价记录</h3>
    <el-table :data="paginatedHistory">
      <el-table-column prop="time" label="时间" width="180"></el-table-column>
      <el-table-column prop="restaurantName" label="餐厅名字" width="180"></el-table-column>
      <el-table-column prop="comment" label="具体评价" width="300"></el-table-column>
      <el-table-column prop="rating" label="评分" width="80"></el-table-column>
    </el-table>
  </el-card>
</template>

<script setup>
  import { ref, computed } from 'vue';
  import { ElCard, ElTable, ElTableColumn, ElRate, ElPagination } from 'element-plus';

  // 使用 ref 创建响应式引用
  const user = ref({
    name: '维尼熊',
    birthday: '1925-12-24',
    favoriteFood: '蜂蜜',
    email: '[email protected]',
    avatar: 'src/assets/维尼熊.png',
    favorites: ref([
      { id: 1, name: '元祖蛋糕', image: 'src/assets/元祖.png', rating:'⭐️⭐️⭐️️', distance: '3km', deliveryTime: '30分钟' },
      { id: 2, name: '詹记', image: 'src/assets/詹记.png', rating: '⭐️⭐️⭐️⭐️️', distance: '5km', deliveryTime: '45分钟' }
    ]),
    history: ref([
      { id: 1, time: '2024-06-13', restaurantName: '楠火锅', comment: '非常好吃!', rating: '⭐️️⭐️⭐️⭐️' },
      { id: 2, time: '2024-06-14', restaurantName: '新疆炒米粉', comment: '服务不错', rating: '⭐️⭐️⭐️' }
    ])
  });

  // 分页属性
  const currentPage = ref(1); // 当前页码
  const pageSize = ref(5); // 每页显示条目数

  // 分页计算属性
  const paginatedFavorites = computed(() => {
    const start = (currentPage.value - 1) * pageSize.value;
    const end = start + pageSize.value;
    return user.value.favorites.slice(start, end);
  });

  const paginatedHistory = computed(() => {
    const start = (currentPage.value - 1) * pageSize.value;
    const end = start + pageSize.value;
    return user.value.history.slice(start, end);
  });

  // 分页改变处理函数
  const handlePageChange = (newPage) => {
    currentPage.value = newPage;
  };
</script>

<style scoped>
  .user-info-card {
    background-image: url('src/assets/homebank.JPG');
    margin: 20px;
    padding: 20px;
    background-color: #f9f9f9;
    border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.user-avatar {
  margin-bottom: 20px;
}

.user-details p {
  color: #333;
  font-size: 16px;
}

.user-details strong {
  color: #409EFF;
}

.favorites-row {
  margin-top: 20px;
}

.favorite-card {
  margin-right: 20px;
  margin-bottom: 20px;
}

.restaurant-image {
  width: 100px; /* 根据需要调整大小 */
  height: auto;
  border-radius: 8px;
  transition: transform 0.3s;
}

.restaurant-image:hover {
  transform: scale(1.1);
}

.history-list {
  margin-top: 20px;
  padding: 0;
  list-style-type: none;
}

.history-item {
  margin-bottom: 10px;
  color: #666;
}
</style>

4.3. 实现页面跳转和导航

要求:

  • 使用Vue Router实现页面之间的跳转,确保各页面可以互相访问,为用户提供便捷的页面切换功能。
  • 导航栏需包含首页、餐厅、用户信息等跳转入口, 确保导航栏在每个页面中可见,并能正确跳转到对应页面。

接下来,我将结合main.js、index.js 和 APP.vue,详细介绍我是如何实现**页面跳转和导航功能的(**具体代码见4.2.2、4.2.3和4.2.4)

4.3.1. 使用 Vue Router 实现页面之间的跳转

(1)定义路由

在项目中创建一个路由配置文件( router/index.js),在这个文件里定义了3个不同路径对应的组件。三个路由如下:

  • / 对应 Home 组件。
  • /restaurant/:id 对应 RestaurantDetail 组件,其中 :id 是动态参数,用于获取特定餐厅的信息。
  • /user 对应 UserInfo 组件。

(2)在主应用中使用路由器

在主应用文件( main.js )中引入并使用这个路由配置:

这样就可以将路由功能集成到了 Vue 应用中。

4.3.2. 导航栏的设计和实现

(1)设置导航栏的 HTML 结构

为了确保导航栏在每个页面中都可见,你可以将导航栏的 HTML结构放在 App.vue(顶层组件)中。这样,无论哪个路由被激活,导航栏都会保持不变。

在APP.vue的模板部分 (<template>),我使用了

  • <el-header><el-menu> 是 Element Plus 提供的组件,用于创建一个水平菜单。
  • <el-menu-item> 用于定义菜单项。
  • <router-link> 用于创建导航链接,当用户点击这些链接时,会触发页面跳转而不会重新加载整个页面。
  • <router-view /> 作为占位符,用来显示当前路由所对应的组件。

(2)样式设置

我在APP.vue里面设置了导航栏的背景颜色、阴影效果以及文字颜色等样式,以确保良好的视觉效果。

4.4. 提交代码至Gitee

  • 将最终完成的代码推送至自己的"惊喜一餐"Gitee项目中。
  • 提交信息应包含描述性文字(如"优化餐厅列表页面,新增详情页"等)。
  • 确保项目代码清晰完整,无多余文

5. 实验小结

相关推荐
Evand J10 分钟前
MATLAB技巧——平滑滤波,给出一定的例程和输出参考
开发语言·matlab
DC...14 分钟前
vue滑块组件设计与实现
前端·javascript·vue.js
LCY13339 分钟前
python 与Redis操作整理
开发语言·redis·python
暮乘白帝过重山41 分钟前
路由逻辑由 Exchange 和 Binding(绑定) 决定” 的含义
开发语言·后端·中间件·路由流程
PingdiGuo_guo1 小时前
C++动态分配内存知识点!
开发语言·c++
人类群星闪耀时1 小时前
5G赋能远程医疗:从愿景到现实的技术变革
开发语言·5g·php
雪落山庄1 小时前
LeetCode100题
java·开发语言·数据结构
吃面必吃蒜1 小时前
从 Vue 到 React:React 合成事件
javascript·vue.js·react.js
前端练习生2 小时前
vue2如何二次封装表单控件如input, select等
前端·javascript·vue.js