文章目錄
- 一、專題頁
- 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
- 五、路由守衛和異常處理
技術選型
組件版本說明
| 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
><h
3>{{ currentCategory.name
}}</h
3></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: display: inline-block
;position: absolute
;left:
50%
;top:
50%
;transform: translate
(-50%, -50%
);}h3
{width:
30%
;background-color: 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
<van-stepperv-model
="item.number"@change
="onChange(item.number, item.id)"/
></template
><!-- 自定義標題,刪除按鈕 --
><template
<span
>{{ item.goods_name
}}</span
><van-icon
name="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 你的收貨地址不支持同城送,
<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
}}</h
3><!-- 點擊登錄,顯示模態框 --
><h3 @click
="ljdl" v-else
>點擊登錄
</h
3><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: 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: 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') {// 此時必須要有token
if (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的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。