【前端Vue】Vue3+Pinia小兔鲜电商项目第4篇:静态结构搭建和路由配置,1. 准备分类组件【附代码文档】

Vue3+ElementPlus+Pinia开发小兔鲜电商项目完整教程(附代码资料)主要内容讲述:认识Vue3,使用create-vue搭建Vue3项目1. Vue3组合式API体验,2. Vue3更多的优势,1. 认识create-vue,2. 使用create-vue创建项目,1. setup选项的写法和执行时机,2. setup中写代码的特点。什么是pinia,创建空Vue项目并安装Pinia1. 安装elementPlus和自动导入插件,2. 配置自动按需导入,3. 测试组件,1. 安装sass,2. 准备定制化的样式文件,3. 自动导入配置。静态结构搭建和分类实现,banner轮播图实现1. 整体结构创建,2. 分类实现,1. 熟悉组件,2. 获取数据渲染组件,1. 纯静态结构,2. 完整代码。静态结构搭建和路由配置,面包屑导航渲染1. 准备分类组件,2. 配置路由,3. 配置导航区域链接,1. 认识组件准备模版,2. 封装接口,3. 渲染面包屑导航。整体认识和路由配置,渲染基础数据1. 准备组件模版,2. 配置路由,3. 绑定模版测试跳转,1. 封装接口,2. 获取数据渲染模版,1. 渲染基础热榜数据。整体认识和路由配置,表单校验实现1. 准备模版,2. 配置路由跳转,1. 校验要求,2. 代码实现。本地购物车,接口购物车1. 添加购物车,2. 头部购物车,3. 列表购物车-基础内容渲染,4. 列表购物车-单选功能实现,5. 列表购物车-全选功能实现,6. 列表购物车-统计数据功能实现。路由配置和基础数据渲染,切换地址-打开弹框交互1. 准备组件模版,2. 配置路由,3. 封装接口,4. 渲染数据,1. 准备弹框模版,2. 控制弹框打开。![image.png](,基础数据渲染1. 准备接口,2. 获取数据渲染内容,1. 支付携带参数,2. 沙箱账号信息,1. 准备模版,2. 绑定路由。Sku组件封装1. 准备模版渲染规格数据,2. 选中和取消选中实现,3. 规格禁用功能实现,4. 产出Prop数据。

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~


全套教程部分目录:


部分文件图片:

静态结构搭建和路由配置

1. 准备分类组件

<script setup>

</script>

<template>
  <div class='top-category'>
    我是分类
  </div>
</template>

2. 配置路由

import { createRouter, createWebHashHistory } from 'vue-router'
import Layout from '@/views/Layout/index.vue'
import Home from '@/views/Home/index.vue'
import Category from '@/views/Category/index.vue'
const router = createRouter({
  history: createWebHashHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'layout',
      component: Layout,
      children: [
        {
          path: '',
          name: 'home',
          component: Home
        },
        {
          path: 'category/:id',
          name: 'category',
          component: Category
        }
      ]
    },
    {
      path: '/login',
      name: 'login',
      component: Login
    },

  ]
})

export default router

3. 配置导航区域链接

<li v-for="item in categoryStore.categoryList" :key="item.id">
  <RouterLink active-class="active" :to="`/category/${item.id}`">
    {{ item.name }}
  </RouterLink>
</li>

面包屑导航渲染

1. 认识组件准备模版

<script setup>

</script>

<template>
  <div class="top-category">
    <div class="container m-top-20">
      <!-- 面包屑 -->
      <div class="bread-container">
        <el-breadcrumb separator=">">
          <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
          <el-breadcrumb-item>居家</el-breadcrumb-item>
        </el-breadcrumb>
      </div>
    </div>
  </div>
</template>


<style scoped lang="scss">
.top-category {
  h3 {
    font-size: 28px;
    color: #666;
    font-weight: normal;
    text-align: center;
    line-height: 100px;
  }

  .sub-list {
    margin-top: 20px;
    background-color: #fff;

    ul {
      display: flex;
      padding: 0 32px;
      flex-wrap: wrap;

      li {
        width: 168px;
        height: 160px;


        a {
          text-align: center;
          display: block;
          font-size: 16px;

          img {
            width: 100px;
            height: 100px;
          }

          p {
            line-height: 40px;
          }

          &:hover {
            color: $xtxColor;
          }
        }
      }
    }
  }

  .ref-goods {
    background-color: #fff;
    margin-top: 20px;
    position: relative;

    .head {
      .xtx-more {
        position: absolute;
        top: 20px;
        right: 20px;
      }

      .tag {
        text-align: center;
        color: #999;
        font-size: 20px;
        position: relative;
        top: -20px;
      }
    }

    .body {
      display: flex;
      justify-content: space-around;
      padding: 0 40px 30px;
    }
  }

  .bread-container {
    padding: 25px 0;
  }
}
</style>

2. 封装接口

import request from '@/utils/request'

/**
 * @description: 获取分类数据
 * @param {*} id 分类id 
 * @return {*}
 */
export const getTopCategoryAPI = (id) => {
  return request({
    url:'/category',
    params:{
      id
    }
  })
}

3. 渲染面包屑导航

<script setup>
  import { findTopCategoryAPI } from '@/apis/category'
  const categoryData = ref({})
  const route = useRoute()
  const getCategory = async (id) => {
    // 如何在setup中获取路由参数 useRoute() -> route 等价于this.$route
    const res = await findTopCategoryAPI(id)
    categoryData.value = res.result
  }
  getCategory(route.params.id)
</script>


<template>
  <div class="bread-container">
    <el-breadcrumb separator=">">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>{{ categoryData.name }}</el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>

1. 适配接口

export function getBannerAPI (params = {}) {
  // 默认为1 商品为2
  const { distributionSite = '1' } = params
  return httpInstance({
    url: '/home/banner',
    params: {
      distributionSite
    }
  })
}

2. 迁移首页Banner逻辑

<script setup>
// 部分代码省略
import { getBannerAPI } from '@/apis/home'

// 获取banner
const bannerList = ref([])

const getBanner = async () => {
  const res = await getBannerAPI({
    distributionSite: '2'
  })
  console.log(res)
  bannerList.value = res.result
}

onMounted(() => getBanner())

</script>

<template>
  <div class="top-category">
    <div class="container m-top-20">
      <!-- 面包屑 -->
      <div class="bread-container">
        <el-breadcrumb separator=">">
          <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
          <el-breadcrumb-item>{{ categoryData.name }}</el-breadcrumb-item>
        </el-breadcrumb>
      </div>
      <!-- 轮播图 -->
      <div class="home-banner">
        <el-carousel height="500px">
          <el-carousel-item v-for="item in bannerList" :key="item.id">
            <img :src="item.imgUrl" alt="">
          </el-carousel-item>
        </el-carousel>
      </div>
    </div>
  </div>
</template>


<style scoped lang="scss">
// 部分代码省略
.home-banner {
  width: 1240px;
  height: 500px;
  margin: 0 auto;
  img {
    width: 100%;
    height: 500px;
  }
}
</style>

导航激活设置分类列表渲染

1. 导航激活状态设置

<RouterLink active-class="active" :to="`/category/${item.id}`">{{ item.name }}</RouterLink>

2. 分类数据模版

<div class="sub-list">
  <h3>全部分类</h3>
  <ul>
    <li v-for="i in categoryData.children" :key="i.id">
      <RouterLink to="/">
        <img :src="i.picture" />
        <p>{{ i.name }}</p>
      </RouterLink>
    </li>
  </ul>
</div>
<div class="ref-goods" v-for="item in categoryData.children" :key="item.id">
  <div class="head">
    <h3>- {{ item.name }}-</h3>
  </div>
  <div class="body">
    <GoodsItem v-for="good in item.goods" :goods="good" :key="good.id" />
  </div>
</div>

路由缓存问题解决

缓存问题:当路由path一样,参数不同的时候会选择直接复用路由对应的组件 解决方案: 1. 给 routerv-view 添加key属性,破坏缓存 2. 使用 onBeforeRouteUpdate钩子函数,做精确更新

基于业务逻辑的函数拆分

基本思想:把组件内独立的业务逻辑通过 useXXX 函数做封装处理,在组件中做组合使用

![image.png](

// 封装分类数据业务相关代码
import { onMounted, ref } from 'vue'
import { getCategoryAPI } from '@/apis/category'
import { useRoute } from 'vue-router'
import { onBeforeRouteUpdate } from 'vue-router'

export function useCategory () {
  // 获取分类数据
  const categoryData = ref({})
  const route = useRoute()
  const getCategory = async (id = route.params.id) => {
    const res = await getCategoryAPI(id)
    categoryData.value = res.result
  }
  onMounted(() => getCategory())

  // 目标:路由参数变化的时候 可以把分类数据接口重新发送
  onBeforeRouteUpdate((to) => {
    // 存在问题:使用最新的路由参数请求最新的分类数据
    getCategory(to.params.id)
  })
  return {
    categoryData
  }
}
// 封装banner轮播图相关的业务代码
import { ref, onMounted } from 'vue'
import { getBannerAPI } from '@/apis/home'

export function useBanner () {
  const bannerList = ref([])

  const getBanner = async () => {
    const res = await getBannerAPI({
      distributionSite: '2'
    })
    console.log(res)
    bannerList.value = res.result
  }

  onMounted(() => getBanner())

  return {
    bannerList
  }
}
<script setup>

import GoodsItem from '../Home/components/GoodsItem.vue'
import { useBanner } from './composables/useBanner'
import { useCategory } from './composables/useCategory'
const { bannerList } = useBanner()
const { categoryData } = useCategory()

</script>

本资源由 收集整理

整体业务认识和路由配置

1. 准备组件模版

<script setup>


</script>

<template>
  <div class="container ">
    <!-- 面包屑 -->
    <div class="bread-container">
      <el-breadcrumb separator=">">
        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
        <el-breadcrumb-item :to="{ path: '/' }">居家
        </el-breadcrumb-item>
        <el-breadcrumb-item>居家生活用品</el-breadcrumb-item>
      </el-breadcrumb>
    </div>
    <div class="sub-container">
      <el-tabs>
        <el-tab-pane label="最新商品" name="publishTime"></el-tab-pane>
        <el-tab-pane label="最高人气" name="orderNum"></el-tab-pane>
        <el-tab-pane label="评论最多" name="evaluateNum"></el-tab-pane>
      </el-tabs>
      <div class="body">
         <!-- 商品列表-->
      </div>
    </div>
  </div>

</template>



<style lang="scss" scoped>
.bread-container {
  padding: 25px 0;
  color: #666;
}

.sub-container {
  padding: 20px 10px;
  background-color: #fff;

  .body {
    display: flex;
    flex-wrap: wrap;
    padding: 0 10px;
  }

  .goods-item {
    display: block;
    width: 220px;
    margin-right: 20px;
    padding: 20px 30px;
    text-align: center;

    img {
      width: 160px;
      height: 160px;
    }

    p {
      padding-top: 10px;
    }

    .name {
      font-size: 16px;
    }

    .desc {
      color: #999;
      height: 29px;
    }

    .price {
      color: $priceColor;
      font-size: 20px;
    }
  }

  .pagination-container {
    margin-top: 20px;
    display: flex;
    justify-content: center;
  }


}
</style>

2. 配置路由关系

import { createRouter, createWebHashHistory } from 'vue-router'
import Layout from '@/views/Layout/index.vue'
import Home from '@/views/Home/index.vue'
import Category from '@/views/Category/index.vue'
import SubCategory from '@/views/SubCategory/index.vue'
const router = createRouter({
  history: createWebHashHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'layout',
      component: Layout,
      children: [
        {
          path: '',
          name: 'home',
          component: Home
        },
        {
          path: 'category/:id',
          name: 'category',
          component: Category
        },
        {
          path: 'category/sub/:id',
          name: 'subCategory',
          component: SubCategory
        },
      ]
    },
    {
      path: '/login',
      name: 'login',
      component: Login
    },

  ]
})

export default router

3. 跳转配置

<div class="sub-list">
  <h3>全部分类</h3>
  <ul>
    <li v-for="i in categoryData.children" :key="i.id">
      <RouterLink :to="`/category/sub/${i.id}`">
        <img :src="i.picture" />
        <p>{{ i.name }}</p>
      </RouterLink>
    </li>
  </ul>
</div>

面包屑导航实现

1. 准备接口

/**
 * @description: 获取二级分类列表数据
 * @param {*} id 分类id 
 * @return {*}
 */

export const getCategoryFilterAPI = (id) => {
  return request({
    url:'/category/sub/filter',
    params:{
      id
    }
  })
}

2. 获取数据渲染模版

<script setup>
import { getCategoryFilterAPI } from '@/apis/category'
// 获取面包屑导航数据
const filterData = ref({})
const getFilterData = async () => {
  const res = await getCategoryFilterAPI(route.params.id)
  filterData.value = res.result
}
getFilterData()
</script>

<template>
  <div class="bread-container">
    <el-breadcrumb separator=">">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item :to="{ path: `/category/${filterData.parentId}` }">{{ filterData.parentName }}
      </el-breadcrumb-item>
      <el-breadcrumb-item>{{ filterData.name }}</el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>

分类基础列表实现

1. 准备接口

/**
 * @description: 获取导航数据
 * @data { 
     categoryId: 1005000 ,
     page: 1,
     pageSize: 20,
     sortField: 'publishTime' | 'orderNum' | 'evaluateNum'
   } 
 * @return {*}
 */
export const getSubCategoryAPI = (data) => {
  return request({
    url:'/category/goods/temporary',
    method:'POST',
    data
  })
}

2. 获取数据列表

<script setup>

// 获取基础列表数据渲染
const goodList = ref([])
const reqData = ref({
  categoryId: route.params.id,
  page: 1,
  pageSize: 20,
  sortField: 'publishTime'
})

const getGoodList = async () => {
  const res = await getSubCategoryAPI(reqData.value)
  console.log(res)
  goodList.value = res.result.items
}

onMounted(() => getGoodList())

</script>

列表筛选实现

思路:tab组件切换时修改reqData中的sortField字段,重新拉取接口列表

<script setup>
// tab切换回调
const tabChange = () => {
  console.log('tab切换了', reqData.value.sortField)
  reqData.value.page = 1
  getGoodList()
}
</script>

<template>
  <el-tabs v-model="reqData.sortField" @tab-change="tabChange">
    <el-tab-pane label="最新商品" name="publishTime"></el-tab-pane>
    <el-tab-pane label="最高人气" name="orderNum"></el-tab-pane>
    <el-tab-pane label="评论最多" name="evaluateNum"></el-tab-pane>
  </el-tabs>
</template>

无限加载实现

基础思路 1. 触底条件满足之后 page++,拉取下一页数据 2. 新老数据做数组拼接 3. 判断是否已经全部加载完毕,停止监听

// 加载更多
const disabled = ref(false)
const load = async () => {
  console.log('加载更多数据咯')
  // 获取下一页的数据
  reqData.value.page++
  const res = await getSubCategoryAPI(reqData.value)
  goodList.value = [...goodList.value, ...res.result.items]
  // 加载完毕 停止监听
  if (res.result.items.length === 0) {
    disabled.value = true
  }
}

未完待续, 同学们请等待下一期

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/523431.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

以诚待人,用心做事,做到最好,追求更好

无数个日日夜夜&#xff0c;终于换来了这样一份努力的证明。 2023年&#xff0c;收获满满&#xff0c;前一阵子拿到了证书&#xff0c;忘记拍照了&#xff0c;今天抽空记录一下 收获&#xff01;又得到一份肯定&#xff0c;这份荣誉证书将伴随我一直为了进步而奋斗&#xff1a…

云备份day04

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C云备份项目 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 主要内容介绍了文件工具了类的实现 文章目录 云备份day041.文件…

2024 软考备考资料+教学视频+历史真题及答案(含2023)(中级软件设计师)免费分享

2024软考备考资料教学视频历史真题及答案(含2023)&#xff08;中级软件设计师&#xff09;免费分享 最近软考报名结束了&#xff0c;马上五月份就要考试&#xff0c;想必很多人都在迎战软考吧。在此我分享一下我整理的一些软考备考资料&#xff0c; 其中包含 教学视频 、教学…

Linux系统中网络协议栈优化

在现代计算机网络中&#xff0c;网络协议栈是实现网络通信的核心组件之一。在Linux系统中&#xff0c;网络协议栈的优化对于提高网络性能、降低延迟、增强安全性等方面至关重要。本文将深入探讨Linux系统中网络协议栈的优化方法和技术&#xff0c;包括使用更快的网络协议栈和禁…

Spyder无法载入(load)或者闪退问题

在Anaconda prompt中直接输入spyder&#xff0c;报错如下 Traceback (most recent call last):File "C:\Users\user\.conda\envs\KB\Scripts\spyder-script.py", line 10, in sys.exit(main())File "C:\Users\user\.conda\envs\KB\lib\site-packages\spyder\a…

单例模式--理解

单例模式 单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时&#xff0c;为了防止频繁地创建对象使得内存飙升&#xff0c;单例模式可以让程序仅在内存中创建一个对象&#xff0c;让所有需要调用的地方都共享这一单例对象。 单…

交换机与队列的简介

1.流程 首先先介绍一个简单的一个消息推送到接收的流程&#xff0c;提供一个简单的图 黄色的圈圈就是我们的消息推送服务&#xff0c;将消息推送到 中间方框里面也就是 rabbitMq的服务器&#xff0c;然后经过服务器里面的交换机、队列等各种关系&#xff08;后面会详细讲&…

二维相位解包理论算法和软件【全文翻译- 残差、梯度和相分解:示例 (2.72.8)】

2.7 残差、梯度和相分解&#xff1a;示例 观察第 2.3 节中提出的局部相位特性如何应用于现实世界是很有启发性的。我们将借助第 2.4 节中的亥姆霍兹分解定理来实现这一点。图 2.7(a)描述了一个 512512 像素的包裹相位函数&#xff0c;它是实际应用中可能遇到的典型相位函数。请…

__dirname 在ES模块中的使用

前言 ECMAScript模块是 JavaScript 的新标准格式。在Node.js中越来越多的库逐渐从从CommonJS转移到ES模块 注&#xff1a;这里是指“真”ES 模块并不是指代码中 Node.js 中使用 import 写法但是实际被 tsc 转成 commonJS 的形式 但是Node.js ES 开发中此前有一个棘手的问题是获…

Java-字符知识扩展

前言&#xff1a;了解字符有关的知识将会对你后面的学习和掌握有关知识有更多的帮助 1.字符的强制转换 所以字符本质还是数字 2.转义字符&#xff1a; 3.布尔值扩展 搬砖好累呜呜呜

请你先搞清楚什么是it行业!

IT&#xff0c;即信息技术&#xff0c;掌握了它就等于拥有了通向未来的钥匙&#x1f5dd;️。现在&#xff0c;就来看看怎样从0基础起步&#xff0c;成为IT圈的一员吧&#xff01; 第一步&#xff1a;认识it IT"代表信息技术&#xff08;Information Technology&#xf…

[羊城杯 2020]Easyphp2 ---不会编程的崽

摆烂一周了&#xff0c;继续更&#xff01;&#xff01;题目还是简单哦。 提示明显要我们修改数据包&#xff0c;第一反应是修改referer。试了一下不太对。url很可能存在文件包含 使用伪协议读取一下源码吧。它过滤了base64关键字。尝试url编码绕过&#xff0c;这里可以使用二…

搜索与图论——拓扑排序

有向图的拓扑排序就是图的宽度优先遍历的一个应用 有向无环图一定存在拓扑序列&#xff08;有向无环图又被称为拓扑图&#xff09;&#xff0c;有向有环图一定不存在拓扑序列。无向图没有拓扑序列。 拓扑序列&#xff1a;将一个图排成拓扑序后&#xff0c;所有的边都是从前指…

【算法篇】三道题理解算法思想——认识BFS

BFS&#xff08;宽搜&#xff09; 宽度优先遍历和深度优先遍历组成了大家熟悉的搜索算法&#xff0c;这两种算法也是蓝桥杯之类竞赛题的常考思想&#xff0c;正巧马上蓝桥杯临近&#xff0c;博主也是刷了很多BFS相关的题型&#xff0c;在这篇文章中会从力扣上选取三道简单的宽搜…

vue2 列表一般不使用索引删除的原因

在 Vue 中使用索引来删除列表项可能会导致一系列问题&#xff0c;尤其是在处理动态列表时。以下是一些可能的问题和相应的例子&#xff1a; 1. 数据不一致问题 当你使用索引来删除列表中的某个项时&#xff0c;如果列表中的其他项发生了变化&#xff08;比如新增或重新排序&a…

全网短剧搜索前端源码开源分享可改自己的接口

全网短剧搜索前端源码 内含7000短剧资源(不支持在线播放&#xff09; 源码全开源&#xff0c;可以修改成自己的接口 178、226、347行修改 源码免费下载地址抄笔记 (chaobiji.cn)https://chaobiji.cn/

全国月度平均风速空间分布数据/月度降雨量分布/月均气温分布

引言 风速是指空气相对于地球某一固定地点的运动速率。一般来讲&#xff0c;风速越大&#xff0c;风力等级越高&#xff0c;风的破坏性越大。平均风速&#xff0c;一定时段内&#xff0c;数次观测的风速的平均值。一般表达方式为[m/s]。 正文 我国位于欧亚大陆东部、太平洋西岸…

YOLO算法改进Backbone系列之:PVT

摘要&#xff1a;尽管基于CNNs的backbone在多种视觉任务中取得重大进展&#xff0c;但本文提出了一个用于密集预测任务的、无CNN的的简单backbone——Pyramid Vision Transformer&#xff08;PVT&#xff09;。相比于ViT专门用于图像分类的设计&#xff0c;PVT将金字塔结构引入…

第7章.自我一致性提示

自我一致性提示技术&#xff0c;它通过确保ChatGPT的输出与输入的精准匹配&#xff0c; 提高了对话的连贯性和准确性&#xff0c;为用户带来了更加智能和满意的交互体验。 它广泛适用于事实核查、数据验证和内容一致性检查等多样化场景。 您需在输入前添加如&#xff1a;“生…

【Entity Framework】EF配置文件设置详解

【Entity Framework】EF配置文件设置详解 文章目录 【Entity Framework】EF配置文件设置详解一、概述二、实体框架配置部分三、连接字符串四、EF数据库提供程序五、EF侦听器六、将数据库操作记录到文件中七、Code First默认连接工厂八、数据库初始值设定项 一、概述 EF实体框架…