日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

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

發(fā)布時(shí)間:2024/9/27 vue 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vue+vant 移动端H5 商城项目_04 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.




文章目錄

          • 一、專題頁
            • 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
          • 五、路由守衛(wèi)和異常處理
            • 5.1. 編寫路由守衛(wèi)
            • 5.2. 異常處理

技術(shù)選型

組件版本說明
vue^2.6.11數(shù)據(jù)處理框架
vue-router^3.5.3動態(tài)路由
vant^2.12.37移動端UI
axios^0.24.0前后端交互
amfe-flexible^2.2.1Rem 布局適配
postcss-pxtorem^5.1.1Rem 布局適配
less^4.1.2css編譯
less-loader^5.0.0css編譯
vue/cli~4.5.0項(xiàng)目腳手架

vue-cli + vant+ less +axios 開發(fā)

一、專題頁
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, //當(dāng)前頁pageSize: 10, // 每頁的條數(shù)data: [], //數(shù)據(jù)totalPages: "2", //總頁數(shù)};},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; //當(dāng)前頁this.data = data; //數(shù)據(jù)this.totalPages = totalPages; //總頁數(shù)this.pageSize = pageSize; // 每頁的條數(shù)// 返回頂部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. 效果圖


點(diǎn)擊左側(cè)導(dǎo)航,更換數(shù)據(jù)

2.2. 分類api

在http.js 文件中,定義接口請求

//6. 分類頁 Category // 全部分類數(shù)據(jù)接口 export function GetChannelDataApi(params) {return instance({url: '/catalog/index',method: 'get',params}) } // 獲取當(dāng)前分類數(shù)據(jù) 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="請輸入搜索關(guān)鍵詞" /><div class="fenlei"><!-- 左側(cè)導(dǎo)航 --><van-sidebar v-model="activeKey" @change="onChange"><van-sidebar-item:title="item.name"v-for="item in categoryList":key="item.id"/></van-sidebar><!-- 右側(cè)主體 --><main><!-- 上方圖片 --><div class="pic-area"><img :src="currentCategory.banner_url" alt="" /><p class="desc">{{ currentCategory.front_desc }}</p></div><!-- 標(biāo)題 --><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: [], //導(dǎo)航數(shù)據(jù)currentCategory: {}, //選中的類別數(shù)據(jù),currentId: "0", subCategoryList:[] //子類數(shù)組};},methods: {// 左側(cè)導(dǎo)航被點(diǎn)擊(index為選中的類別的索引值),更換類別onChange(index) {this.activeKey = index;this.currentCategory =this.categoryList[this.activeKey] this.currentId = this.categoryList[this.activeKey].id; //選中的類別的id// 獲取當(dāng)前分類數(shù)據(jù)this.GetCurrentCategory()},// 獲取全部分類數(shù)據(jù)GetcategoryList() {GetChannelDataApi().then((res) => {// console.log("res1", res);this.categoryList = res.data.categoryList; //左側(cè)導(dǎo)航數(shù)據(jù)//選中的類別的id,默認(rèn)第一個(gè)類別被選中this.currentId = this.categoryList[0].id; // 當(dāng)前顯示的類別數(shù)據(jù),圖片和標(biāo)題使用this.currentCategory = res.data.currentCategory; //當(dāng)前顯示的類別數(shù)據(jù) 圖文混排區(qū)域使用this.subCategoryList = res.data.currentCategory.subCategoryList; });},// 獲取當(dāng)前分類數(shù)據(jù)GetCurrentCategory() {GetFenleiDataApi({ id: this.currentId }).then((res) => {// console.log("res12", res);// 當(dāng)前顯示的類別數(shù)據(jù),圖片和標(biāo)題使用this.currentCategory = res.data.currentCategory; //當(dāng)前顯示的類別數(shù)據(jù) 圖文混排區(qū)域使用this.subCategoryList = res.data.currentCategory.subCategoryList;});},},created() {this.GetcategoryList(); // 獲取全部分類數(shù)據(jù)} }; </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"><!-- 每個(gè)商品前的按鈕 --><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><!-- 自定義標(biāo)題,刪除按鈕 --><template #title><span>{{ item.goods_name }}</span><van-iconname="delete-o"class="delete-icon"@click="onDelete(item)"/></template></van-card></div><!-- 按鈕 --><!-- 下方結(jié)算 --><!-- vant顯示的數(shù)字不對,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: {}, //購物車數(shù)據(jù)// price: 0,goodsId: '',number: '',productId: '',id_: '',isChecked: '1',// productIdsList:[],productIds: '',checkedGoodsAmount: 0, //選中的商品的總金額checkedAll: 0,};},methods: {// 獲取數(shù)據(jù)getData() {// 發(fā)送請求,獲取當(dāng)前購物車的數(shù)據(jù)GetCartData().then((res) => {console.log(11111, res);this.cartList = res.data.cartList; //商品總列表this.cartTotal = res.data.cartTotal; //購物車數(shù)據(jù)//選中的商品的總金額this.checkedGoodsAmount = res.data.cartTotal.checkedGoodsAmount // 如果有選中的商品if (this.cartTotal.checkedGoodsCount > 0) {// 選中的商品數(shù)量===購物車內(nèi)的所有商品總數(shù)量 時(shí)候,全選按鈕就會被選中if (this.cartTotal.checkedGoodsCount == this.cartTotal.goodsCount) {this.checkedAll = true} else { //不相等的時(shí)候,全選按鈕就不會被選中this.checkedAll = false}} else { // 如果沒有選中的商品,全選按鈕就不會被選中this.checkedAll = false}});},// 刪除單個(gè)商品的時(shí)候,發(fā)送刪除商品的請求onDelete(item) {DeleteCartData2({ productIds: item.product_id.toString() }).then((res) => {if (res.errno === 0) {this.getData() //重新請求購物車商品數(shù)據(jù),渲染}})},// 按下商品+1或者-1按鈕, 購物車商品數(shù)量變化 ,onChange會接收變化的商品idonChange(value, id_) {this.cartList.forEach(item => {// 找出對應(yīng)的goods_id,numberif (item.id === id_) {this.id_ = id_this.goodsId = item.goods_idthis.number = item.numberthis.productId = item.product_id}})// 發(fā)請求this.updateCartData()},// 購物車商品步進(jìn)器功能接口 按下商品+1或者-1按鈕,updateCartData() {// 直接發(fā)送更新數(shù)據(jù)請求,將當(dāng)前的商品數(shù)量帶著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() //重新請求購物車商品數(shù)據(jù),渲染}})},// 點(diǎn)擊商品單選按鈕,切換購物車商品選中狀態(tài),發(fā)送請求onchxClickFn(item) {this.isChecked = item.checked ? '1' : '0'this.productIds = item.product_id.toString()this.toggleCartCheckedData()},// 切換購物車商品選中狀態(tài),發(fā)送請求toggleCartCheckedData() {console.log(this.isChecked);ToggleCartCheckedData({isChecked: this.isChecked,productIds: this.productIds}).then((res) => {console.log(667, res);if (res.errno === 0) {this.getData() //重新請求購物車商品數(shù)據(jù),渲染}})},// 點(diǎn)擊全選,切換購物車商品選中狀態(tài),發(fā)送請求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>


發(fā)送獲取購物車數(shù)據(jù)列表時(shí)的響應(yīng)數(shù)據(jù)

購物車商品步進(jìn)器功能接口

切換購物車商品選中狀態(tài)功能接口(含全選)響應(yīng)數(shù)據(jù)

四、我的頁
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><!-- 點(diǎn)擊登錄,顯示模態(tài)框 --><h3 @click="ljdl" v-else>點(diǎn)擊登錄</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><!-- 模態(tài)框 --><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: '請?zhí)顚懹脩裘?#39; }]"/><van-fieldv-model="pwd"type="password"name="密碼"label="密碼"placeholder="密碼":rules="[{ required: true, message: '請?zhí)顚懨艽a' }]"/><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"; //默認(rèn)頭像export default {name: "user",data() {return {username: "",pwd: "",avatarSrc: headImg, //頭像ifLogined: false, // 登錄狀態(tài)ifShowModal: false, // 是否顯示模態(tài)框gridArr: [// grid數(shù)組{ id: 0, icon: "label-o", type: "我的訂單" },{ id: 1, icon: "bill-o", type: "優(yōu)惠券" },{ 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: "聯(lián)系客服" },{ 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: {// 點(diǎn)擊立即登錄,顯示登錄模態(tài)框ljdl() {this.ifShowModal = true; },// 提交用戶名,密碼信息onSubmit() {this.getloginData(); //發(fā)送數(shù)據(jù)請求},// 發(fā)送數(shù)據(jù)請求:登錄注冊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; //不顯示模態(tài)框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");// 刷新當(dāng)前頁this.$router.go(0);// 刷新當(dāng)前頁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>
五、路由守衛(wèi)和異常處理

在router 目錄下的index.js 文件中,設(shè)置路由前置守衛(wèi),代碼如下,用來判斷購物車頁面只能在用戶登錄的情況下才能查看。

5.1. 編寫路由守衛(wèi)
// 路由前置守衛(wèi) router.beforeEach((to, from, next) => {// 有token就表示已經(jīng)登錄// 想要進(jìn)入購物車頁面,必須有登錄標(biāo)識token// console.log('to:', to)// console.log('from:', from)let token = localStorage.getItem('token')if (to.path == '/cart') {// 此時(shí)必須要有tokenif (token) {next(); // next()去到to所對應(yīng)的路由界面} else {Vue.prototype.$toast('請先登錄');// 定時(shí)器setTimeout(() => {next("/user"); // 強(qiáng)制去到"/user"所對應(yīng)的路由界面}, 1000);}} else {// 如果不是去往購物車的路由,則直接通過守衛(wèi),去到to所對應(yīng)的路由界面next()} })
5.2. 異常處理

解決刷新頁面,底部tabbar顯示錯(cuò)題。

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.編程式導(dǎo)航在跳轉(zhuǎn)到與當(dāng)前地址一致的URL時(shí)會報(bào)錯(cuò),但這個(gè)報(bào)錯(cuò)不影響功能:

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

3.用戶頁引入頭像

直接在標(biāo)簽中引入相對路徑圖片地址,圖片不顯示,需要使用如下模塊式引入方式。

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

項(xiàng)目優(yōu)化—路由懶加載
當(dāng)打包構(gòu)建應(yīng)用時(shí),JavaScript 包會變得非常大,影響頁面加載。如果我們能把不同路由對應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問的時(shí)候才加載對應(yīng)組件,這樣就更加高效了。

{path: '/home',//首頁name: 'Home',component: () => import('@/views/Home'),meta: { // 用來判斷該組件對應(yīng)的頁面是否顯示底部tabbarisShowTabbar: true}},

總結(jié)

以上是生活随笔為你收集整理的vue+vant 移动端H5 商城项目_04的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。