vue美食杰项目实现菜谱详情效果

目录
detail.vue
detail-header.vue
detail-content.vue
comment.vue
注意:
因为用到了element-ui组件,所以需要先安装element-ui组件,进入项目根目录后
执行

npm i element-ui

思路:
detail.vue:
1.根据menuId请求数据(查看数据结构),渲染页面(如果没有menuId),提示框请登录

​ 注意:数据结构过多,在保存到组件中时,提前写好默认值,避免在异步请求返回数据之前找不到属性

detail-header.vue(头部页):
1.根据数据,逐个渲染头部数据,用户信息通过query传入用户id,跳转个人空间
​2.收藏按钮:判断是否本人登录再显示
3.收藏功能:判断是否登录,再请求,否则提示框弹出‘请先登录’

detail-content.vue (内容):
1.逐个渲染数据中,菜品的步骤:编号使用数组下标+1即可
1.效果展示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
2.视图页面:views
detail.vue

<template>
  <div class="menu-detail">
    <detail-header :info="menuInfo"></detail-header>
    <detail-content :info="menuInfo"></detail-content>
    <Comment :info="menuInfo"></Comment>
  </div>
</template>
<script>
import DetailHeader from "./detail-header";
import DetailContent from "./detail-content";
import Comment from "./comment";
import { menuInfo } from "@/service/api";
export default {
  components: { DetailHeader, DetailContent, Comment },
  data() {
    return {
      menuInfo: {//接受菜谱的详细信息
        userInfo: {},//放个过渡层,找数据时不会报错
        raw_material: {
          main_material: [],
          accessories_material: [],
        },
        steps: [],//接收菜谱的详细信息
      }, 
    };
  },
  watch: {
    $route: {
      handler() {
        //通过query拿到menuId的数据
        let { menuId } = this.$route.query;
        if (menuId) {
          //发送请求
          menuInfo({ menuId }).then(({ data }) => {
            console.log(data);
            this.menuInfo = data.info;
          });
        } else {
          this.$message({
            showClose: true,
            message: "重进",
            type: "warrning",
          });
        }
      },
      immediate: true,//立即执行
    },
  },
};
</script>

detail-header.vue
通过收藏点击事件 toggleCollection()判断 点no-collection-at为灰色是收藏,点info.isCollection是红色是取消收藏,通过isOnwer()方法进行判断是否是本人登录

<template>
  <section class="detail-header">
    <img class="detail-img" :src="info.product_pic_url" />
    <div class="detail-header-right">
      <div class="detail-title clearfix">
        <h1 class="title">{{ info.title }}</h1>
        <!--
            1. 不显示,这个菜谱是当前用户发布的
            2. 显示,后端返回一个是否收藏的字段
          -->
        <div class="detail-collection" v-if="!isOnwer">
          <a
            href="javascript:;"
            class="collection-at"
            :class="{ 'no-collection-at': info.isCollection }"
            @click="toggleCollection">
            {{ info.isCollection ? "已收藏" : "收藏" }}
          </a>
        </div>
      </div>
      <ul class="detail-property clearfix">
        <li v-for="item in info.properties_show" :key="item.type">
          <strong>{{ item.parent_name }}</strong>
          <span>{{ item.name }}</span>
        </li>
      </ul>
      <div class="user">
        <router-link
          id="tongji_author_img"
          class="img"
          :to="{ name: 'space', query: { userId: info.userInfo.userId } }">
          <img :src="info.userInfo.avatar" />
        </router-link>
        <div class="info">
          <h4>
            <router-link
              id="tongji_author"
              :to="{ name: 'space', query: { userId: info.userInfo.userId } }">
              {{ info.userInfo.name }}
            </router-link>
          </h4>
          <span>菜谱:{{ info.userInfo.work_menus_len }}/ 关注:{{
              info.userInfo.following_len
            }} / 粉丝:{{ info.userInfo.follows_len }}</span>
          <strong>{{ info.userInfo.createdAt }}</strong>
        </div>
      </div>
    </div>
  </section>
</template>
<script>
import { toggleCollection } from "@/service/api";
export default {
  props: {
    info: {
      type: Object,
      default: () => ({}),
    },
  },
  computed: {
    isOnwer() {
      //判断是否本人,是的就登陆,不是就先登录或者注册
      return this.info.userInfo.userId === this.$store.state.userInfo.userId;
    },
  },
  methods: {
    async toggleCollection() {//通过async等待异步渲染的数据
      if (!this.$store.getters.isLogin) {
        this.$message({
          showClose: true,
          message: "请登录再收藏",
          type: "warrning",
        });
        return ;
      }
      //  收藏菜品的接口
      const data = await toggleCollection({ menuId: this.info.menuId });
      console.log(data);
      //  返回值有两种 true   未收藏转为已收藏的
      // flase    已收藏的转为未收藏的
      this.info.isCollection = data.data.isCollection;
    },
  },
};
</script>

detail-content.vue
通过父组件props来接受子组件的信息。
逐个渲染数据,在菜品编号时使用数组下标{{index+1}}.

<template>
  <section class="detail-content">
    <div class="detail-materials">
      <p class=""><strong></strong>{{info.product_story}}<strong></strong></p>
      <h2>用料</h2>
      <div class="detail-materials-box clearfix" v-if="info.raw_material.main_material.length">
        <h3>主料</h3>
        <ul>
          <li class="" v-for="item in info.raw_material.main_material" :key="item._id">
           {{ item.name}}
            <span>{{item.specs}}</span>
          </li>
        </ul>
      </div>
      <div class="detail-materials-box clearfix" v-if="info.raw_material.main_material.length">
        <h3>辅料</h3>
        <ul>
          <li class="" v-for="item in info.raw_material.main_material" :key="item._id">
            {{ item.name}}
            <span>{{item.specs}}</span>
          </li>
        </ul>
      </div>
    </div>
    <div class="detail-explain">
      <h2>{{info.title}}的做法</h2>
      <section class="detail-section clearfix" v-for="(item,index) in info.steps" :key='item._id'>
        <em class="detail-number">{{index+1}}.</em>
        <div class="detail-explain-desc">
          <p>{{item.describe}}</p>
          <img class="conimg" :src="item.img_url" v-if="item.img_url" alt="">
        </div>
      </section>
      <div class="skill">
        <h2>烹饪技巧</h2>
        <p>{{info.skill}}</p>
      </div>
    </div>
  </section>
</template>
<script>
export default {
  name: 'DetailContent',
  props:{//接收数据
   info: {
      type: Object,
      default: () => ({})
    }
  }
}
</script>

comment.vue
通过postComment给后端发送请求,再通过send()方法提交评论

<template>
  <div class="comment-box">
    <h2>{{ info.title }}的讨论</h2>
    <div class="comment-text">
      <a href="javascript:;" class="useravatar" v-if="isLogin">
        <img :src="userInfo.avatar" />
      </a>
      <div v-if="!isLogin">
        请先登录后,再评论<router-link 
        :to="{name:'login',query:{userId: info.userInfo.userId}}">登录</router-link>
      </div>
      <!-- 输入内容需要做一个双向数据绑定 -->
      <div class="comment-right" v-if="isLogin">
        <el-input
          type="textarea"
          :rows="5"
          :cols="50"
          placeholder="请输入内容"
          v-model="commentText">
        </el-input>
        <div class="comment-button" v-if="isLogin">
          <el-button
            class="send-comment"
            type="primary"
            size="medium"
            @click="send">提交</el-button>
        </div>
      </div>
    </div>
    <div class="comment-list-box">
      <ul class="comment-list">
        <li v-for="item in comments" :key="item.commentId">
          <router-link
            :to="{ name: 'space', query: { userId: item.userInfo.userId } }"
            class="avatar"
          >
            <img :src="item.userInfo.avatar" />
            <h5>{{ item.userInfo.name }}</h5>
          </router-link>
          <div class="comment-detail">
            <p class="p1">{{ item.commentText }}</p>
            <div class="info clearfix">
              <span style="float: left">{{ item.createdAt }}</span>
            </div>
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
import { getComments, postComment ,menuId} from "@/service/api";
export default {
  name: "Comment",
  props: {//接收数据
    info: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      comments: [],//空的变量
      commentText: "",//评论的内容
    };
  },
  computed: {
    userInfo() {
      // vuex里面拿
      return this.$store.state.userInfo;
    },
    isLogin(){
      return this.$store.getters.isLogin;
    }
  },
  async mounted() {
    if (menuId) {
      console.log(data);
      //向后端发送请求  评论内容
      let data = await getComments({ menuId: menuId });
      console.log(data);
      this.comments = data.data.comments;//存在组件当中
    }
  },
  methods: {
    async send() {
      // 发送数据 (两种方式)
      // 1.query中获取		 2.父组件传入的info
      let data= await postComment({menuId: this.info.menuId,commentText:this.commentText});
      console.log(data)
      this.comments.unshift(data.data.comments);//向前添加数据
      this.commentText='';//清除上次里面的评论
    },
  },
};
</script>

总结
今天的内容就先到这里 因为还有一些没完善所以不是很好 后面再继续改进!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值