文章目录

  • 一、专题页
    • 1. 效果图
    • 2. 专题api
    • 2.Topic.vue 组件
    • 3. 专题源码
  • 二、分类页
    • 2.1. 效果图
    • 2.2. 分类api
    • 2.3. Category.vue 组件
  • 三、购物车页
    • 3.1. 效果图
    • 3.2. 购物车api
    • 3.3. 购物车页面
  • 四、我的页
    • 4.1. 效果图
    • 4.2. 定义api
    • 4.3. User.vue
  • 五、路由守卫和异常处理
    • 5.1. 编写路由守卫
    • 5.2. 异常处理

技术选型

组件 版本 说明
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. 效果图

2. 专题api

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

//5. 专题页 Topic
//专题请求
export function GetTopicApi(params) {return instance({url: '/topic/list',method: 'get',params})
}
2.Topic.vue 组件

3. 专题源码
<!-- 专题页 -->
<template><div class="zhuanti"><div class="box" v-for="item in data" :key="item.id"><img :src="item.scene_pic_url" alt="" /><div class="title">{{ item.title }}</div><div class="tip">{{ item.subtitle }}</div><div class="price">{{ item.price_info | moneyFlrmat }}</div></div><!-- 分页器 --><van-paginationv-model="currentPage":page-count="totalPages"mode="simple"@change="ChangeFn"/></div>
</template><script>
import { getTopicList } from "@/https/http.js";export default {data() {return {currentPage: 1, //当前页pageSize: 10, // 每页的条数data: [], //数据totalPages: "2", //总页数};},methods: {getPage() {getTopicList({page: this.currentPage,size: this.pageSize,}).then((res) => {console.log("res555", this.currentPage);console.log("res555", res);let { count, currentPage, data, pageSize, totalPages } = res.data;this.currentPage = currentPage; //当前页this.data = data; //数据this.totalPages = totalPages; //总页数this.pageSize = pageSize; // 每页的条数// 返回顶部document.documentElement.scrollTop = 0;});},ChangeFn() {// 会直接改变currentPageconsole.log(this.currentPage);this.getPage();},},created() {this.getPage();},
};
</script>
<style lang="less" scoped>
/deep/.van-pagination__page-desc {display: none;
}
.zhuanti {padding-bottom: 100px;box-sizing: border-box;.box {width: 100%;font-size: 14px;line-height: 40px;text-align: center;img {width: 100%;}.title {font-size: 18px;}.price {color: red;}}
}
</style>
二、分类页
2.1. 效果图


点击左侧导航,更换数据

2.2. 分类api

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

//6. 分类页 Category
// 全部分类数据接口
export function GetChannelDataApi(params) {return instance({url: '/catalog/index',method: 'get',params})
}
// 获取当前分类数据
export function GetFenleiDataApi(params) {return instance({url: '/catalog/current',method: 'get',params})
}
2.3. Category.vue 组件


<!-- 分类页 -->
<template><div class="category-box"><!--搜索框 --><van-search v-model="value" show-action placeholder="请输入搜索关键词" /><div class="fenlei"><!-- 左侧导航 --><van-sidebar v-model="activeKey" @change="onChange"><van-sidebar-item:title="item.name"v-for="item in categoryList":key="item.id"/></van-sidebar><!-- 右侧主体 --><main><!-- 上方图片 --><div class="pic-area"><img :src="currentCategory.banner_url" alt="" /><p class="desc">{{ currentCategory.front_desc }}</p></div><!-- 标题 --><div class="mytitle"><span></span><h3>{{ currentCategory.name }}</h3></div><!-- 图文混排 --><van-grid :column-num="3" ><van-grid-itemv-for="item in subCategoryList":key="item.id":icon="item.wap_banner_url":text="item.name"/></van-grid></main></div></div>
</template><script>
import { GetChannelDataApi, GetFenleiDataApi } from "@/https/http";export default {data() {return {activeKey: 0,value: "",categoryList: [], //导航数据currentCategory: {}, //选中的类别数据,currentId: "0",  subCategoryList:[]  //子类数组};},methods: {// 左侧导航被点击(index为选中的类别的索引值),更换类别onChange(index) {this.activeKey = index;this.currentCategory =this.categoryList[this.activeKey]  this.currentId = this.categoryList[this.activeKey].id;  //选中的类别的id// 获取当前分类数据this.GetCurrentCategory()},// 获取全部分类数据GetcategoryList() {GetChannelDataApi().then((res) => {// console.log("res1", res);this.categoryList = res.data.categoryList;  //左侧导航数据//选中的类别的id,默认第一个类别被选中this.currentId = this.categoryList[0].id;  // 当前显示的类别数据,图片和标题使用this.currentCategory = res.data.currentCategory;  //当前显示的类别数据 图文混排区域使用this.subCategoryList = res.data.currentCategory.subCategoryList;  });},// 获取当前分类数据GetCurrentCategory() {GetFenleiDataApi({ id: this.currentId }).then((res) => {// console.log("res12", res);// 当前显示的类别数据,图片和标题使用this.currentCategory = res.data.currentCategory;  //当前显示的类别数据 图文混排区域使用this.subCategoryList = res.data.currentCategory.subCategoryList;});},},created() {this.GetcategoryList();  // 获取全部分类数据}
};
</script>
<style scoped lang="less">
/* @import url(); 引入css类 */
.fenlei {display: flex;main {flex: 1;.pic-area {text-align: center;position: relative;height: 100px;font-size: 15px;img {width: 98%;border-radius: 5px;display: block;}.desc {position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);}}.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%);}}}
}
</style>
三、购物车页
3.1. 效果图

3.2. 购物车api

在http.js 文件中定义接口

//7.购物车页 Cart
// 购物车列表
export function GetCartData(params) {return instance({url: '/cart/index',method: 'get',params})
}
3.3. 购物车页面

Cart.vue

在views/cart目录下,.Cart.vue新建 组件,代码如下:

<!-- 购物车页 -->
<template><div class="cart-box"><div v-for="item in cartList" :key="item.id" class="cart-item"><!-- 每个商品前的按钮 --><van-checkbox:name="item"@click="onchxClickFn(item)"class="checkbox-btn"v-model="item.checked"></van-checkbox><!-- 商品信息 --><van-card :price="item.retail_price" :thumb="item.list_pic_url"><template #num><van-stepperv-model="item.number"@change="onChange(item.number, item.id)"/></template><!-- 自定义标题,删除按钮 --><template #title><span>{{ item.goods_name }}</span><van-iconname="delete-o"class="delete-icon"@click="onDelete(item)"/></template></van-card></div><!-- 按钮 --><!-- 下方结算 --><!-- vant显示的数字不对,9999元会显示成99.99元,所以需要乘以100 --><van-submit-bar:price="checkedGoodsAmount * 100"button-text="提交订单"@submit="onSubmit"><van-checkbox @click="onClickCheckAll" v-model="checkedAll">全选</van-checkbox><template #tip>你的收货地址不支持同城送,<span @click="onClickEditAddress">修改地址</span></template></van-submit-bar></div>
</template><script>
import {GetCartData, UpdateCartData, DeleteCartData,ToggleCartCheckedData, DeleteCartData2
} from "@/https/http";export default {name: "cart",data() {return {cartList: [], //商品总列表cartTotal: {}, //购物车数据// price: 0,goodsId: '',number: '',productId: '',id_: '',isChecked: '1',// productIdsList:[],productIds: '',checkedGoodsAmount: 0,  //选中的商品的总金额checkedAll: 0,};},methods: {// 获取数据getData() {// 发送请求,获取当前购物车的数据GetCartData().then((res) => {console.log(11111, res);this.cartList = res.data.cartList; //商品总列表this.cartTotal = res.data.cartTotal;  //购物车数据//选中的商品的总金额this.checkedGoodsAmount = res.data.cartTotal.checkedGoodsAmount // 如果有选中的商品if (this.cartTotal.checkedGoodsCount > 0) {// 选中的商品数量===购物车内的所有商品总数量 时候,全选按钮就会被选中if (this.cartTotal.checkedGoodsCount == this.cartTotal.goodsCount) {this.checkedAll = true} else {  //不相等的时候,全选按钮就不会被选中this.checkedAll = false}} else { // 如果没有选中的商品,全选按钮就不会被选中this.checkedAll = false}});},// 删除单个商品的时候,发送删除商品的请求onDelete(item) {DeleteCartData2({ productIds: item.product_id.toString() }).then((res) => {if (res.errno === 0) {this.getData()  //重新请求购物车商品数据,渲染}})},//  按下商品+1或者-1按钮, 购物车商品数量变化 ,onChange会接收变化的商品idonChange(value, id_) {this.cartList.forEach(item => {// 找出对应的goods_id,numberif (item.id === id_) {this.id_ = id_this.goodsId = item.goods_idthis.number = item.numberthis.productId = item.product_id}})// 发请求this.updateCartData()},// 购物车商品步进器功能接口  按下商品+1或者-1按钮,updateCartData() {// 直接发送更新数据请求,将当前的商品数量带着UpdateCartData({goodsId: this.goodsId, id: this.id_,number: this.number, productId: this.productId}).then((res) => {console.log(999, res);if (res.errno === 0) {this.getData() //重新请求购物车商品数据,渲染}})},// 点击商品单选按钮,切换购物车商品选中状态,发送请求onchxClickFn(item) {this.isChecked = item.checked ? '1' : '0'this.productIds = item.product_id.toString()this.toggleCartCheckedData()},// 切换购物车商品选中状态,发送请求toggleCartCheckedData() {console.log(this.isChecked);ToggleCartCheckedData({isChecked: this.isChecked,productIds: this.productIds}).then((res) => {console.log(667, res);if (res.errno === 0) {this.getData() //重新请求购物车商品数据,渲染}})},// 点击全选,切换购物车商品选中状态,发送请求onClickCheckAll() {this.isChecked = this.checkedAll ? '1' : '0'let productIdAllList = []this.cartList.forEach((item) => {productIdAllList.push(item.product_id.toString())})this.productIds = productIdAllList.join(',')this.toggleCartCheckedData()},// 提交onSubmit() { },// 编辑地址onClickEditAddress() { },},created() {this.getData();},
};
</script>
<style scoped lang="less">
/deep/.van-checkbox__label {flex: 1;
}
/deep/.van-checkbox {margin-bottom: 2px;
}
/deep/.van-submit-bar {bottom: 50px;
}
.cart-box {padding-bottom: 150px;box-sizing: border-box;.van-card {position: relative;}.delete-icon {position: absolute;top: 5px;right: 5px;}.cart-item {position: relative;padding-left: 40px;.checkbox-btn {position: absolute;left: 20px;top: 50%;transform: translate(-50%, -50%);}}
}
</style>


发送获取购物车数据列表时的响应数据

购物车商品步进器功能接口

切换购物车商品选中状态功能接口(含全选)响应数据

四、我的页
4.1. 效果图




4.2. 定义api

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

//登陆
export function GoLogin(params) {return instance({url: '/auth/loginByWeb',method: 'post',data: params})
}
4.3. User.vue


在views/user 目录下,新建User.vue 组件,代码如下:

<!-- 我的 -->
<template><div class="user-box"><div class="user-top"><img :src="avatarSrc" alt="" /><!-- 如果登陆了,就显示用户名,否则显示立即登录 --><h3 v-if="ifLogined">{{ username }}</h3><!-- 点击登录,显示模态框 --><h3 @click="ljdl" v-else>点击登录</h3><van-icon :name="ifLogined ? 'cross' : 'arrow'" @click="loginout" /></div><!-- 九宫格部分 --><van-grid :column-num="3"><van-grid-itemv-for="item in gridArr":key="item.id":icon="item.icon":text="item.type"/></van-grid><!-- 模态框 --><div class="modal" v-if="ifShowModal"><div class="modal-bg" @click="ifShowModal = false"></div><div class="modal-content"><van-form @submit="onSubmit"><van-fieldv-model="username"name="用户名"label="用户名"placeholder="用户名":rules="[{ required: true, message: '请填写用户名' }]"/><van-fieldv-model="pwd"type="password"name="密码"label="密码"placeholder="密码":rules="[{ required: true, message: '请填写密码' }]"/><div style="margin: 16px"><van-button round block type="danger" native-type="submit">提交</van-button></div></van-form></div></div></div>
</template><script>
// 引入登录接口
import { GoLogin } from "@/https/http";
import headImg from "@/assets/images/touxiang.png";  //默认头像export default {name: "user",data() {return {username: "",pwd: "",avatarSrc: headImg,  //头像ifLogined: false, // 登录状态ifShowModal: false, // 是否显示模态框gridArr: [// grid数组{ id: 0, icon: "label-o", type: "我的订单" },{ id: 1, icon: "bill-o", type: "优惠券" },{ id: 2, icon: "goods-collect-o", type: "礼品卡" },{ id: 3, icon: "location-o", type: "我的收藏" },{ id: 4, icon: "flag-o", type: "我的足迹" },{ id: 5, icon: "contact", type: "会员福利" },{ id: 6, icon: "aim", type: "地址管理" },{ id: 7, icon: "warn-o", type: "账号安全" },{ id: 8, icon: "service-o", type: "联系客服" },{ id: 9, icon: "question-o", type: "帮助中心" },{ id: 10, icon: "smile-comment-o", type: "意见反馈" },],};},created() {// 登陆前先看本人是否登陆过let user = JSON.parse(localStorage.getItem("userInfo"));// 用户名存在if (user) {this.username = user.username;  //用户名this.avatarSrc = user.avatar; //头像this.ifLogined = true; // 显示用户名}},methods: {// 点击立即登录,显示登录模态框ljdl() {this.ifShowModal = true;   },// 提交用户名,密码信息onSubmit() {this.getloginData(); //发送数据请求},// 发送数据请求:登录注册getloginData() {GoLogin({ username: this.username, pwd: this.pwd }).then((res) => {console.log(res);if (res.errno === 0) {console.log("登录成功");this.$toast.success("登录成功");localStorage.setItem("token", res.data.token);localStorage.setItem("userInfo", JSON.stringify(res.data.userInfo));this.ifShowModal = false; //不显示模态框this.ifLogined = true; // 显示用户名this.avatarSrc = res.data.userInfo.avatar; //头像this.username = res.data.userInfo.username;}});},// 退出登录loginout() {// 登录了if (this.ifLogined) {this.$dialog.confirm({title: "退出登录",message: "是否退出登录",}).then(() => {// on confirmthis.ifLogined = false; // 不显示用户名this.avatarSrc = headImg; //头像// 清除tokenlocalStorage.removeItem("token");localStorage.removeItem("userInfo");// 刷新当前页this.$router.go(0);// 刷新当前页this.$router.go(0);}).catch(() => {// on cancel});}},},
};
</script>
<style lang="less" scoped>
.van-grid-item {padding: 20px;
}
.user-box {.user-top {display: flex;align-items: center;font-size: 16px;padding: 20px 10px;box-sizing: border-box;background-color: #333;color: white;img {width: 70px;height: 70px;margin-right: 10px;border-radius: 50%;}h3 {flex: 1;}}.modal {width: 100%;height: 100%;position: fixed; //position: fixed让height:100%起作用left: 0;top: 0;.modal-bg {width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);}.modal-content {width: 90%;height: 200px;box-sizing: border-box;// height: 200px;background-color: #fff;padding: 20px;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);z-index: 100;}}
}
</style>
五、路由守卫和异常处理

在router 目录下的index.js 文件中,设置路由前置守卫,代码如下,用来判断购物车页面只能在用户登录的情况下才能查看。

5.1. 编写路由守卫

// 路由前置守卫
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()}
})
5.2. 异常处理

解决刷新页面,底部tabbar显示错题。

  computed:{active:{get(){console.log(this.$route.path)const path = this.$route.pathswitch(path){case '/home':return 0;case '/topic':return 1;case '/category':return 2;case '/cart':return 3;case '/user':return 4;default:return 0}},set(){}}}

2.编程式导航在跳转到与当前地址一致的URL时会报错,但这个报错不影响功能:

// 该段代码不需要记,理解即可
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {return originalPush.call(this, location).catch((err) => err);
};

3.用户页引入头像

直接在标签中引入相对路径图片地址,图片不显示,需要使用如下模块式引入方式。

// import 方式
import headImg from "../assets/touxiang.png";// require 方式
let headImg = require("../assets/touxiang.png")

项目优化—路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

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

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

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

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

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

    文章目录 一.·首页搜索功能 1. 搜索页面 2. 历史记录和热门搜索组件 3. 搜索框提示列表组件 4. 综合-价格-分类 5. 搜索出的产品展示 6. 异常修复 7. 路由拦截/路由守卫 二.详情 ...

  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. Hive-1.2.0学习笔记(一)安装配置
  2. linux(centos)下mysql忘记root密码
  3. Dell sc1425安装ESXi 5不成功
  4. 那些 别人家的孩子 ,后来都怎么样了 ?
  5. IOS-网络(GCD)
  6. python3 for_python3 for循环-range遍历
  7. Thread线程类及多线程
  8. java实现人脸识别源码【含测试效果图】——ServiceImpl层(UserServiceImpl)
  9. 基本算法研究1-冒泡排序算法测试
  10. leetcode 235. 二叉搜索树的最近公共祖先 思考分析
  11. 键入一个网页后了发生什么
  12. Objdump查看汇编和源码
  13. Aliplayer视频点播加密播放
  14. Git和码云项目平台使用文档
  15. T检验显著性检验根据t值计算p值
  16. Facebook团队提供人工智能视频教程:机器学习入门教程指南
  17. 今晚折腾了好久,win7极限精简版239M在虚拟机未成功?
  18. 如何解决EXCEL中弹出“信息检索”的信息
  19. 关于我的英文名字Byron
  20. Paper reading (六十五):Kernel-penalized regression for analysis of microbiome data

热门文章

  1. 哥廷根,世界数学家的摇篮和圣地
  2. 引发外国网友刷屏的“大国重器”到底什么水平?
  3. 看完白鹤滩水电站建造历程 感觉《流浪地球》那些真能造出来
  4. php 文件 不更新,php页面不刷新更新数据
  5. kali 切换图形界面_kali Linux 文本图形界面切换遇到的怪问题
  6. Linux命令sngre,linux gre隧道创建
  7. mysql日期格式转换
  8. UTF-8中的BOM-带还是不带?
  9. AI让边缘更智能 边缘让AI无处不在
  10. 那些年,我在阿里当数据开发