文章目录

  • 一、·首页搜索功能
    • 1. 搜索页面
    • 2. 历史记录和热门搜索组件
    • 3. 搜索框提示列表组件
    • 4. 综合-价格-分类
    • 5. 搜索出的产品展示
    • 6. 异常修复
    • 7. 路由拦截/路由守卫
  • 二、详情页
    • 2.1. 效果图
    • 2.2. 详情api
    • 2.3. 配置路由
    • 2.4. 详情页面
    • 2.5. 详情页源码

技术选型

组件 版本 说明
vue ^2.6.11 数据处理框架
vue-router ^3.5.3 动态路由
vant ^2.12.37 移动端UI
axios ^0.24.0 前后端交互
amfe-flexible ^2.2.1 Rem 布局适配
postcss-pxtorem ^5.1.1 Rem 布局适配
less ^4.1.2 css编译
less-loader ^5.0.0 css编译
vue/cli ~4.5.0 项目脚手架

vue-cli + vant+ less +axios 开发

一、·首页搜索功能
1. 搜索页面



 {path: '/home',//首页name: 'Home',component: () => import('@/views/home/Home'),meta: { // 用来判断该组件对应的页面是否显示底部tabbarisShowTabbar: true},children: [{path: 'searchPopup',name: 'SearchPopup',component: () => import('@/views/home/search/SearchPopup')}]},

1.在http.js文件中定义接口请求

// 2.搜索页 SearchPopup
// 历史记录列表和热门搜索列表
export function GetPopupData(params) {return instance({url: '/search/index',method: 'get',params})
}
//删除历史记录
export function Clearhistory(params) {return instance({url: '/search/clearhistory',method: 'post',data: params})
}//搜索提示列表
export function GetSearchTipsListData(params) {return instance({url: '/search/helper',method: 'get',params})
}
//根据关键字搜索商品
export function GetSearchData(params) {return instance({url: '/goods/list',method: 'get',params})
}

2.views /SearchPopup.vue

在views 目录下创建SearchPopup.vue 页面,作为点击搜索后的页面

<template><div class="search-popup-box"><!--搜索框 --><van-searchshape="round"v-model="value"show-action:placeholder="placeholderVal"@search="onSearch"@cancel="onCancel"@input="onInput"/><!-- 历史记录和热门搜索 组件 --><!-- 接受子组件传来的数据 --><HistoryHotv-if="blockShow == 1":historyKeywordList="historyKeywordList":hotKeywordList="hotKeywordList"@goSearch="setValue"></HistoryHot><!-- 搜索提示 组件 --><SearchTipsListv-else-if="blockShow == 2":dataList="dataList"@setValue="setValue"></SearchTipsList><!-- 在父组件中绑定自定义事件 priceChange1 categoryChange1 --><!-- 下拉菜单 --><searchProducts:filterCategory="filterCategory":goodsList="goodsList"@priceChange1="priceChange1"@categoryChange1="categoryChange1"v-else></searchProducts></div>
</template><script>
import HistoryHot from "@/views/home/search/HistoryHot";
import searchProducts from "@/views/home/search/searchProducts";
import SearchTipsList from "@/views/home/search/SearchTipsList";
// 引入请求接口
import { GetSearchData, GetPopupData, GetSearchTipsListData } from "@/https/http";
export default {name: "search-popup",data() {return {value: "", // 搜索值page: 1, //页数size: 20, // 每页数据条数order: "desc", // 价格由高到底categoryId: 0, // 商品类别id 全家、居家、餐饮。。。sort: "id", // 排序字段 price 和id  2种情况goodsList: [], // 搜索出来的商品数据filterCategory: [], // 商品类别参数historyKeywordList: [],   //历史记录数据hotKeywordList: [],   // 热门搜索 数据blockShow: 1,  //控制显示哪个?( 历史记录和热门搜索组件 、 搜索提示组件、下拉菜单组件)dataList: [],  //搜索提示数据placeholderVal: ''  // 搜索框的默认提示词};},methods: {priceChange1(value) {// value为子组件searchProducts传来的价格排序参数asc,descconsole.log("父组件:价格", value);this.order = value;this.sort = "price";this.onSearch();//   this.$router.go(0);},categoryChange1(value) {// value为子组件searchProducts传来的种类参数console.log("父组件:类别id", value);this.sort = "id";this.categoryId = value;this.onSearch();},// 搜索框搜索功能onSearch() {this.blockShow = 3// 关键字搜索接口数据let obj = {keyword: this.value,page: 1,size: this.size, // 每页数据条数order: this.order, // 价格由高到底categoryId: this.categoryId, // 商品类别id 全家、居家、餐饮。。。sort: this.sort, // 排序字段};//   发送数据请求// 搜索框商品搜索功能接口,获取对应的商品列表GetSearchData(obj).then((res) => {// console.log(22, res);this.filterCategory = res.data.filterCategory;// 将商品的类别中的字段替换一下this.filterCategory = JSON.parse(JSON.stringify(this.filterCategory).replace(/id/g, "value").replace(/name/g, "text"));this.goodsList = res.data.goodsList;});},// 搜索取消onCancel() {this.$router.push("/home");},// 获得历史记录gethistoryHotData() {GetPopupData().then((res) => {console.log(555, res);this.historyKeywordList = res.data.historyKeywordList;this.hotKeywordList = res.data.hotKeywordList;this.placeholderVal = res.data.defaultKeyword.keyword;});},// 获取搜索提示getSearchHelperData(value) {this.blockShow = 2   // 展示搜索提示列表组件// 输入触发// 搜索提示数据请求GetSearchTipsListData({ keyword: value }).then((res) => {console.log(333333, res);this.dataList = res.data})},setValue(m) {console.log(6666, m);this.value = mthis.onSearch()},onInput(value) {this.getSearchHelperData(value)}},created() {this.gethistoryHotData();},components: {HistoryHot,searchProducts,SearchTipsList,},
};
</script><style></style>
2. 历史记录和热门搜索组件

components/HistoryHot.vue

在components目录下 创建 HistoryHot.vue(历史记录和热门搜索) 组件,引入到SearchPopup.vue中

<template><div class="box"><div class="history-hot" v-if="isShowHistory"><h4>历史记录</h4><van-icon name="delete" class="delete-icon" @click="clearFn" /><van-tagplaintype="primary"v-for="(item, index) in historyKeywordList":key="index"v-if="item"@click="goSearch(item)">{{ item }}</van-tag></div><div class="hot-box"><h4>热门搜索</h4><van-tagplain:type="item.is_hot ? 'danger' : 'primary'"v-for="(item, index) in hotKeywordList":key="index"v-if="item.keyword"@click="goSearch(item.keyword)">{{ item.keyword }}</van-tag></div></div>
</template><script>
import { Clearhistory } from '@/https/http'
export default {name: "history-hot",data() {return {isShowHistory: 1}},props: ["historyKeywordList", "hotKeywordList"],methods: {clearFn() {Clearhistory().then((res) => {console.log(res);this.isShowHistory = 0})},goSearch(value) {this.$emit('goSearch', value)}}
};
</script>
<style lang="less" scoped>
.box {font-size: 16px;span {margin-right: 3px;}.history-hot {margin-bottom: 10px;position: relative;.delete-icon {position: absolute;top: 10px;right: 10px;}}
}
</style>
3. 搜索框提示列表组件

components/SearchTipsList.vue

在components目录下 创建 SearchTipsList.vue(搜索框提示列表组件) 组件,引入到SearchPopup.vue中

<template><div class="search-tips-list-box"><van-listv-model="loading":finished="finished"finished-text="没有更多了"@load="onLoad"><van-cellv-for="item in dataList":key="item":title="item"@click="geiValue(item)"/></van-list></div>
</template><script>
export default {name: "search-tips-list",data() {return {list: [],loading: false,  // //是否处于加载状态finished: false, // 是否加载完成};},props: ["dataList"],   父传子数组methods: {onLoad() {},geiValue(value){this.$emit('setValue',value)}},
};
</script><style lang="less" scoped>
</style>
4. 综合-价格-分类

components/SearchProducts.vue




在components 下 新建SearchProducts.vue (综合-价格-分类组件)组件,引入到SearchPopup.vue

<template><div class="search-popup-box"><!--搜索框 --><van-searchshape="round"v-model="value"show-action:placeholder="placeholderVal"@search="onSearch"@cancel="onCancel"@input="onInput"/><!-- 历史记录和热门搜索 组件 --><!-- 接受子组件传来的数据 --><HistoryHotv-if="blockShow == 1":historyKeywordList="historyKeywordList":hotKeywordList="hotKeywordList"@goSearch="setValue"></HistoryHot><!-- 搜索提示 组件 --><SearchTipsListv-else-if="blockShow == 2":dataList="dataList"@setValue="setValue"></SearchTipsList><!-- 在父组件中绑定自定义事件 priceChange1 categoryChange1 --><!-- 下拉菜单 --><searchProducts:filterCategory="filterCategory":goodsList="goodsList"@priceChange1="priceChange1"@categoryChange1="categoryChange1"v-else></searchProducts></div>
</template><script>
import HistoryHot from "@/views/home/search/HistoryHot";
import searchProducts from "@/views/home/search/searchProducts";
import SearchTipsList from "@/views/home/search/SearchTipsList";
// 引入请求接口
import { GetSearchData, GetPopupData, GetSearchTipsListData } from "@/https/http";
export default {name: "search-popup",data() {return {value: "", // 搜索值page: 1, //页数size: 20, // 每页数据条数order: "desc", // 价格由高到底categoryId: 0, // 商品类别id 全家、居家、餐饮。。。sort: "id", // 排序字段 price 和id  2种情况goodsList: [], // 搜索出来的商品数据filterCategory: [], // 商品类别参数historyKeywordList: [],   //历史记录数据hotKeywordList: [],   // 热门搜索 数据blockShow: 1,  //控制显示哪个?( 历史记录和热门搜索组件 、 搜索提示组件、下拉菜单组件)dataList: [],  //搜索提示数据placeholderVal: ''  // 搜索框的默认提示词};},methods: {priceChange1(value) {// value为子组件searchProducts传来的价格排序参数asc,descconsole.log("父组件:价格", value);this.order = value;this.sort = "price";this.onSearch();//   this.$router.go(0);},categoryChange1(value) {// value为子组件searchProducts传来的种类参数console.log("父组件:类别id", value);this.sort = "id";this.categoryId = value;this.onSearch();},// 搜索框搜索功能onSearch() {this.blockShow = 3// 关键字搜索接口数据let obj = {keyword: this.value,page: 1,size: this.size, // 每页数据条数order: this.order, // 价格由高到底categoryId: this.categoryId, // 商品类别id 全家、居家、餐饮。。。sort: this.sort, // 排序字段};//   发送数据请求// 搜索框商品搜索功能接口,获取对应的商品列表GetSearchData(obj).then((res) => {// console.log(22, res);this.filterCategory = res.data.filterCategory;// 将商品的类别中的字段替换一下this.filterCategory = JSON.parse(JSON.stringify(this.filterCategory).replace(/id/g, "value").replace(/name/g, "text"));this.goodsList = res.data.goodsList;});},// 搜索取消onCancel() {this.$router.push("/home");},// 获得历史记录gethistoryHotData() {GetPopupData().then((res) => {console.log(555, res);this.historyKeywordList = res.data.historyKeywordList;this.hotKeywordList = res.data.hotKeywordList;this.placeholderVal = res.data.defaultKeyword.keyword;});},// 获取搜索提示getSearchHelperData(value) {this.blockShow = 2   // 展示搜索提示列表组件// 输入触发// 搜索提示数据请求GetSearchTipsListData({ keyword: value }).then((res) => {console.log(333333, res);this.dataList = res.data})},setValue(m) {console.log(6666, m);this.value = mthis.onSearch()},onInput(value) {this.getSearchHelperData(value)}},created() {this.gethistoryHotData();},components: {HistoryHot,searchProducts,SearchTipsList,},
};
</script><style></style>
5. 搜索出的产品展示

components/Products.vue

在components 下 新建 Products.vue(搜索出的产品展示组件),引入到SearchProducts.vue,作为其子组件使用。

<template><!-- 数据列表渲染 --><ul class="goods_list"><li v-for="item in goodsList" :key="item.id" @click="gotodetail(item.id)"><img v-lazy="item.list_pic_url" alt="" /><p>{{ item.name }}</p><p>{{ item.retail_price | moneyFlrmat }}</p></li></ul>
</template><script>
export default {name:'products',props:['goodsList'],data(){return{}},methods: {gotodetail(id_){this.$router.push({path:'/productDetail',query:{id:id_}})}}
}
</script><style lang="less" scoped>.goods_list {display: flex;flex-wrap: wrap;justify-content: space-between;font-size: 16px;line-height: 20px;text-align: center;li {width: 48%;img {width: 100%;}}}
</style>
6. 异常修复

关于重复点击同一个路由出现的报错问题解决
在新版本的vue-router中,重复点击同一个路由会出现以下报错:

方案1、vue-router降级处理(但不推荐)

npm i vue-router@3.0.7

方案2、直接在push方法最后添加异常捕获,例如:

<van-search v-model="SearchVal" shape="round" placeholder="请输入搜索关键词" disabled @click="$router.push('/home/searchPopup').catch(err=>{})"/>

方案3、直接修改原型方法push(推荐)

// 把这段代码直接粘贴到router/index.js中的Vue.use(VueRouter)之前
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function(location) {return originalPush.call(this, location).catch(err => {})
};
7. 路由拦截/路由守卫

vue-router文档地址
路由拦截(导航守卫:前置导航守卫和后置导航守卫)
前置导航守卫有三个参数
to: 表示即将进入的路由
from: 表示即将离开的路由
next() :表示执行进入这个路由

// 路由前置守卫
router.beforeEach((to, from, next) => {// 有token就表示已经登录// 想要进入购物车页面,必须有登录标识token// console.log('to:', to)// console.log('from:', from)let token = localStorage.getItem('token')if (to.path == '/cart') {// 此时必须要有tokenif (token) {next(); // next()去到to所对应的路由界面} else {Vue.prototype.$toast('请先登录');// 定时器setTimeout(() => {next("/user"); // 强制去到"/user"所对应的路由界面}, 1000);}} else {// 如果不是去往购物车的路由,则直接通过守卫,去到to所对应的路由界面next()}
})
二、详情页
2.1. 效果图


2.2. 详情api

1.在http.js 文件中定义详情页请求接口

//3.详情页  ProductDetail
// 产品详情
export function GoodsDetailApi(params) {return instance({url: '/goods/detail',method: 'get',params})
}
//详情页相关产品
export function GetGoodsRelatedData(params) {return instance({url: '/goods/related',method: 'get',params})
}
//获取商品数量
export function GetCartNum(params) {return instance({url: '/cart/goodscount',method: 'get',params})
}
// 添加到购物车
export function AddToCart(params) {return instance({url: '/cart/add',method: 'post',data: params})
}
2.3. 配置路由

router/index.js
在components 目录下创建ProductDetail.vue (详情页组件),并在路由中配置( 一级路由)

 {path: '/productDetail', //产品详情name: 'ProductDetail',component: () => import('@/views/productdetail/ProductDetail')},
2.4. 详情页面

components /ProductDetail.vue

2.5. 详情页源码
<template><div class="product-detail-box"><van-swipe :autoplay="3000"><van-swipe-item v-for="item in gallery" :key="item.id"><img :src="item.img_url" /></van-swipe-item></van-swipe><div class="info" v-if="info.name"><p class="info-name">{{ info.name }}</p><p class="info-brief">{{ info.goods_brief }}</p><p class="info-price">{{ info.retail_price | moneyFlrmat }}</p></div><div class="attribute"><div class="mytitle"><span></span><h3>商品参数</h3></div><ul><li v-for="item in attribute" :key="item.id"><span class="attribute-name">{{ item.name }}</span><span class="attribute-value">{{ item.value }}</span></li></ul></div><!-- 产品详情 --><div class="mytitle"><span></span><h3>产品详情</h3></div><!-- 产品描述信息 --><div class="goods_desc" v-html="info.goods_desc"></div><div class="mytitle"><span></span><h3>常见问题</h3></div><!-- 常见问题 --><ul class="issue"><li v-for="item in issue" :key="item.id"><h3>{{ item.question }}</h3><p>{{ item.answer }}</p></li></ul><!-- 产品列表 --><Weekproduct :newGoodsList="goodsList" title="相关产品"> </Weekproduct><!-- 添加购物车面板 --><van-skuv-model="show"ref="sku":sku="sku":goods="goods":hide-stock="sku.hide_stock"@add-cart="onAddCartClicked"/><!-- 下方购物车      --><van-goods-action><van-goods-action-icon:icon="star_flag ? 'star' : 'star-o'":text="star_flag ? '已收藏' : '未收藏'":color="star_flag ? '#ff5000' : '#323233'"@click="clickFn"/><van-goods-action-iconicon="cart-o"text="购物车":badge="badge"@click="$router.push('/cart')"/><van-goods-action-buttontype="warning"text="加入购物车"@click="addCar"/><van-goods-action-button type="danger" text="立即购买" /></van-goods-action></div>
</template><script>
import { getDetailData, AddToCart, GetGoodsRelatedData,GetCartCountData } from "@/https/http";
import Weekproduct from "@/views/home/Weekproduct";export default {name: "product-detail",data() {return {gallery: [],info: {},attribute: [], //参数show: false,sku: {tree: [], //规格类目 颜色 尺寸 。。。price: "", // 默认价格(单位元)stock_num: 227, // 商品总库存// 数据结构见下方文档hide_stock: false, //是否隐藏剩余库存},goods: {// 默认商品 sku 缩略图picture: ''},productList: [], // 当前产品信息issue: [],goodsList: [],star_flag: false,badge:0,};},created() {this.GetDetailData()this.getRelatedData()this.getCartData()},methods: {addCar() {this.show = true},// 加入购物车onAddCartClicked() {console.log(666,this.$refs.sku.getSkuData());let obj = {}obj.goodsId = this.$route.query.idobj.productId = this.productList[0].idobj.number = this.$refs.sku.getSkuData().selectedNumAddToCart(obj).then((res) => {console.log(res);// 显示添加成功this.$toast.success("添加成功");this.getCartData()})// 隐藏 商品规格面板this.show = false;},// 获取产品明细数据列表GetDetailData() {getDetailData({ id: this.$route.query.id }).then((res) => {console.log(33, res);this.gallery = res.data.gallery;this.info = res.data.info;this.productList = res.data.productList;this.attribute = res.data.attribute;this.issue = res.data.issue;this.goods.picture = res.data.info.list_pic_urlthis.sku.price = res.data.info.retail_pricethis.sku.stock_num = res.data.info.goods_number});},// 获取相关产品数据列表getRelatedData() {GetGoodsRelatedData({ id: this.$route.query.id }).then((res) => {console.log(3366, res);this.goodsList = res.data.goodsList});},// 获取购物车商品数量getCartData(){GetCartCountData().then((res)=>{console.log(7778,res);this.badge = res.data.cartTotal.goodsCount})},clickFn() {this.star_flag = !this.star_flagif (this.star_flag) {this.$toast('收藏成功')} else {this.$toast('取消宝贝收藏成功')}}},components: {Weekproduct}
};
</script><style lang="less" scoped>
.product-detail-box {font-size: 14px;line-height: 30px;padding-bottom: 100px;img {width: 100%;}.info {text-align: center;.info-brief {color: #666;}.info-price {color: red;}}.attribute {ul {li {border-bottom: 1px solid #eee;font-size: 12px;display: flex;.attribute-name {width: 15%;}.attribute-value {flex: 1;}}}}.mytitle {text-align: center;font-size: 16px;margin-top: 20px;position: relative;height: 50px;span {width: 50%;height: 2px;background-color: #ccc;display: inline-block;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);}h3 {width: 30%;background-color: #fff;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);}}.issue {li {h3 {padding-left: 10px;line-height: 20px;position: relative;&:before {content: "";width: 4px;height: 4px;border-radius: 50%;background-color: red;display: inline-block;position: absolute;left: 2px;top: 50%;margin-top: -2px;}}margin-bottom: 15px;}}/deep/.goods_desc {img {width: 100%;}}
}
</style>

vue+vant 移动端H5 商城项目_03相关推荐

  1. vue+vant 移动端H5 商城项目_01

    文章目录 一.·Rem 布局适配 1. 安装 amfe-flexible 2. px转化rem 3. 全局配置 4. 重置样式表 5. 引入重置样式表 二.组件安装和配置与封装 2.1. 安装less ...

  2. vue+vant 移动端H5 商城项目_04

    文章目录 一.专题页 1. 效果图 2. 专题api 2.Topic.vue 组件 3. 专题源码 二.分类页 2.1. 效果图 2.2. 分类api 2.3. Category.vue 组件 三.购 ...

  3. vue+vant 移动端H5 商城项目_02

    文章目录 一.路由规划 1. 新建路由配置 2. 下载vue-router 3. 路由注册 4. 路由基础配置 5. 路由挂载 6. AppTabBar 7. 二.移动端首页 2.1.首页效果 2.2 ...

  4. 最新vue+vant移动端电商项目

    今天为大家带来一个Vue+VantUi完成的一个一个移动端电商项目,供大家学习和参考. 源码已上传到码云上面https://gitee.com/jiuyueqi/vyx, 如果有需要源码或者接口文档的 ...

  5. Vue 搭建移动端 h5 项目步骤

    Vue 搭建移动端 h5 项目步骤 简介 最近团队里的其他前端小伙伴需要自己搭建移动端 h5 项目,没有整体的思路,于是我就写了这个步骤. 提示:(2022-10-28更新) vue-cli 和vue ...

  6. Capacitor+Vue+Vant移动端打包总结

    Capacitor+Vue+Vant移动端打包总结 本笔记为打包Vue移动端Android Apk 打包步骤 Capacitor与Vue项目结合基本配置自行百度/谷歌,这里是已集成配置好的项目. 1. ...

  7. html5地区级联选择,【JS】vue+vant移动端地区级联选择组件

    首页 专栏 javascript 文章详情 0 vue+vant移动端地区级联选择组件 quanta发布于 今天 08:25 写在开头:项目的框架是vue+vant,业务需求一个级联的地区选择,写完才 ...

  8. vue实现移动端H5页面截图

    vue实现移动端H5页面截图 1.vue使用html2canvas实现移动端H5页面截图并下载. 2.html2canvas能够实现在用户浏览器端直接对整个或部分页面进行截屏.这个html2canva ...

  9. animate inater插件_基于animate.css动画库的全屏滚动小插件,适用于vue.js(移动端、pc)项目...

    功能简介 基于animate.css动画库的全屏滚动,适用于vue.js(移动端.pc)项目. 安装 npm install vue-animate-fullpage --save 使用 main.j ...

最新文章

  1. 【Kotlin】apply 内联扩展函数 ( apply 函数原型 | apply 函数示例 | Kotlin 调用 Java API )
  2. jQuery获取Select选择的Text和 Value
  3. windows远程下载
  4. 你怎么了珍妮,你醒醒啊珍妮~ | 今日最佳
  5. [2021] node连接oracle数据库示例[使用oracle官方组件]
  6. devops 应用_如何在DevOps中应用系统思考
  7. 【Python爬虫】入门知识
  8. 3d激光雷达开发(字符串输出和实体绘制)
  9. 很多人在销售过程中,喜欢考虑用一些话术
  10. ios刻录软件_好用免费的剪辑软件神器可以剪辑、导出,让大家都变成为大师级!...
  11. ffmpeg音频转换命令
  12. 深入解析Scheduler
  13. pycharm运行scrapy框架爬取豆瓣电影250可能遇到的问题
  14. 基于JavaWeb的文献管理系统设计与实现
  15. 院校-美国:美国国立卫生研究院(NIH)
  16. 80%的人都不知道在Excel中掐头去尾求平均分用这个函数
  17. 华为交换机常见ARP操作
  18. 递归算法详解——递归算法的三要素以及例题分析
  19. 快速有效的从零开始学习3d建模?
  20. 深度分析如何在Hadoop中控制Map的数量

热门文章

  1. 关于科技创新,2021年政府工作报告这样说
  2. 中枪!这才是当代博士生真实日常大赏
  3. 26岁的她,成为深圳大学史上最年轻正教授!
  4. 【文末有福利】生成对抗网络
  5. 站在前人的肩膀上,新一代数学家正在崛起
  6. Appium安装使用总结
  7. 【转载保存】获取页面编码
  8. 技术分享:从双11看实时数仓Hologres高可用设计与实践
  9. 新型DDoS来袭 | 基于STUN协议的DDoS反射攻击分析
  10. 涂鸦智能dubbo-go亿级流量的实践与探索