文章目錄
- 一、·首頁搜索功能
- 1. 搜索頁面
- 2. 歷史記錄和熱門搜索組件
- 3. 搜索框提示列表組件
- 4. 綜合-價格-分類
- 5. 搜索出的產(chǎn)品展示
- 6. 異常修復(fù)
- 7. 路由攔截/路由守衛(wèi)
- 二、詳情頁
- 2.1. 效果圖
- 2.2. 詳情api
- 2.3. 配置路由
- 2.4. 詳情頁面
- 2.5. 詳情頁源碼
技術(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.1 | Rem 布局適配 |
| postcss-pxtorem | ^5.1.1 | Rem 布局適配 |
| less | ^4.1.2 | css編譯 |
| less-loader | ^5.0.0 | css編譯 |
| vue/cli | ~4.5.0 | 項(xiàng)目腳手架 |
vue-cli + vant+ less +axios 開發(fā)
一、·首頁搜索功能
1. 搜索頁面
{path:
'/home',//首頁name:
'Home',component:
() => import
('@/views/home/Home'),meta:
{ // 用來判斷該組件對應(yīng)的頁面是否顯示底部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
})
}
//根據(jù)關(guān)鍵字搜索商品
export function GetSearchData
(params
) {return instance
({url:
'/goods/list',method:
'get',params
})
}
2.views /SearchPopup.vue
在views 目錄下創(chuàng)建SearchPopup.vue 頁面,作為點(diǎn)擊搜索后的頁面
<template
><div class
="search-popup-box"><!--搜索框
--><van
-searchshape
="round"v
-model
="value"show
-action
:placeholder
="placeholderVal"@search
="onSearch"@cancel
="onCancel"@input
="onInput"/><!-- 歷史記錄和熱門搜索 組件
--><!-- 接受子組件傳來的數(shù)據(jù)
--><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, sort
: "id", goodsList
: [], filterCategory
: [], historyKeywordList
: [], hotKeywordList
: [], blockShow
: 1, dataList
: [], placeholderVal
: '' };},methods
: {priceChange1(value
) {console
.log("父組件:價格", value
);this
.order
= value
;this
.sort
= "price";this
.onSearch();},categoryChange1(value
) {console
.log("父組件:類別id", value
);this
.sort
= "id";this
.categoryId
= value
;this
.onSearch();},onSearch() {this
.blockShow
= 3let obj
= {keyword
: this
.value
,page
: 1,size
: this
.size
, order
: this
.order
, categoryId
: this
.categoryId
, sort
: this
.sort
, };GetSearchData(obj
).then((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目錄下 創(chuàng)建 HistoryHot.vue(歷史記錄和熱門搜索) 組件,引入到SearchPopup.vue中
<template
><div
class="box"><div
class="history-hot" v-if
="isShowHistory"><h
4>歷史記錄
</h
4><van-icon
name="delete" class="delete-icon" @click
="clearFn" /
><van-tagplain
type="primary"v-for
="(item, index) in historyKeywordList":key
="index"v-if
="item"@click
="goSearch(item)">{{ item
}}</van-tag
></div
><div
class="hot-box"><h
4>熱門搜索
</h
4><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目錄下 創(chuàng)建 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, // //是否處于加載狀態(tài)finished: false, // 是否加載完成
};},props:
["dataList"], 父傳子數(shù)組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-search
shape="round"v-model
="value"show-action:placeholder
="placeholderVal"@search
="onSearch"@cancel
="onCancel"@input
="onInput"/
><!-- 歷史記錄和熱門搜索 組件 --
><!-- 接受子組件傳來的數(shù)據(jù) --
><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, //頁數(shù)size:
20, // 每頁數(shù)據(jù)條數(shù)order:
"desc", // 價格由高到底categoryId:
0, // 商品類別id 全家、居家、餐飲。。。sort:
"id", // 排序字段 price 和id
2種情況goodsList:
[], // 搜索出來的商品數(shù)據(jù)filterCategory:
[], // 商品類別參數(shù)historyKeywordList:
[], //歷史記錄數(shù)據(jù)hotKeywordList:
[], // 熱門搜索 數(shù)據(jù)blockShow:
1, //控制顯示哪個?( 歷史記錄和熱門搜索組件 、 搜索提示組件、下拉菜單組件)dataList:
[], //搜索提示數(shù)據(jù)placeholderVal:
'' // 搜索框的默認(rèn)提示詞
};},methods:
{priceChange1
(value
) {// value為子組件searchProducts傳來的價格排序參數(shù)asc,descconsole.log
("父組件:價格", value
);this.order
= value
;this.sort
= "price";this.onSearch
();// this.
$router.go
(0);},categoryChange1
(value
) {// value為子組件searchProducts傳來的種類參數(shù)console.log
("父組件:類別id", value
);this.sort
= "id";this.categoryId
= value
;this.onSearch
();},// 搜索框搜索功能
onSearch() {this.blockShow
= 3// 關(guān)鍵字搜索接口數(shù)據(jù)
let obj
= {keyword: this.value,page:
1,size: this.size, // 每頁數(shù)據(jù)條數(shù)order: this.order, // 價格由高到底categoryId: this.categoryId, // 商品類別id 全家、居家、餐飲。。。sort: this.sort, // 排序字段
};// 發(fā)送數(shù)據(jù)請求// 搜索框商品搜索功能接口,獲取對應(yīng)的商品列表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 // 展示搜索提示列表組件// 輸入觸發(fā)// 搜索提示數(shù)據(jù)請求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. 搜索出的產(chǎn)品展示
components/Products.vue
在components 下 新建 Products.vue(搜索出的產(chǎn)品展示組件),引入到SearchProducts.vue,作為其子組件使用。
<template
><!-- 數(shù)據(jù)列表渲染 --
><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. 異常修復(fù)
關(guān)于重復(fù)點(diǎn)擊同一個路由出現(xiàn)的報錯問題解決
在新版本的vue-router中,重復(fù)點(diǎn)擊同一個路由會出現(xiàn)以下報錯:
方案1、vue-router降級處理(但不推薦)
npm i vue-router@3.0.7
方案2、直接在push方法最后添加異常捕獲,例如:
<van-search v-model
="SearchVal" shape="round" placeholder="請輸入搜索關(guān)鍵詞" 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. 路由攔截/路由守衛(wèi)
vue-router文檔地址
路由攔截(導(dǎo)航守衛(wèi):前置導(dǎo)航守衛(wèi)和后置導(dǎo)航守衛(wèi))
前置導(dǎo)航守衛(wèi)有三個參數(shù)
to: 表示即將進(jìn)入的路由
from: 表示即將離開的路由
next() :表示執(zhí)行進(jìn)入這個路由
// 路由前置守衛(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') {// 此時必須要有token
if (token
) {next
(); // next
()去到to所對應(yīng)的路由界面
} else {Vue.prototype.
$toast('請先登錄');// 定時器setTimeout
(() => {next
("/user"); // 強(qiáng)制去到
"/user"所對應(yīng)的路由界面
},
1000);}} else {// 如果不是去往購物車的路由,則直接通過守衛(wèi),去到to所對應(yīng)的路由界面next
()}
})
二、詳情頁
2.1. 效果圖
2.2. 詳情api
1.在http.js 文件中定義詳情頁請求接口
//3.詳情頁 ProductDetail
// 產(chǎn)品詳情
export function GoodsDetailApi
(params
) {return instance
({url:
'/goods/detail',method:
'get',params
})
}
//詳情頁相關(guān)產(chǎn)品
export function GetGoodsRelatedData
(params
) {return instance
({url:
'/goods/related',method:
'get',params
})
}
//獲取商品數(shù)量
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 目錄下創(chuàng)建ProductDetail.vue (詳情頁組件),并在路由中配置( 一級路由)
{path:
'/productDetail', //產(chǎn)品詳情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
><h
3>商品參數(shù)
</h
3></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
><!-- 產(chǎn)品詳情 --
><div
class="mytitle"><span
></span
><h
3>產(chǎn)品詳情
</h
3></div
><!-- 產(chǎn)品描述信息 --
><div
class="goods_desc" v-html
="info.goods_desc"></div
><div
class="mytitle"><span
></span
><h
3>常見問題
</h
3></div
><!-- 常見問題 --
><ul
class="issue"><li v-for
="item in issue" :key
="item.id"><h
3>{{ item.question
}}</h
3><p
>{{ item.answer
}}</p
></li
></ul
><!-- 產(chǎn)品列表 --
><Weekproduct :newGoodsList
="goodsList" title="相關(guān)產(chǎn)品"> </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-icon
icon="cart-o"text="購物車":badge
="badge"@click
="$router.push('/cart')"/
><van-goods-action-button
type="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:
[], //參數(shù)show: false,sku:
{tree:
[], //規(guī)格類目 顏色 尺寸 。。。price:
"", // 默認(rèn)價格(單位元)stock_num:
227, // 商品總庫存// 數(shù)據(jù)結(jié)構(gòu)見下方文檔hide_stock: false, //是否隱藏剩余庫存
},goods:
{// 默認(rèn)商品 sku 縮略圖picture:
''},productList:
[], // 當(dāng)前產(chǎn)品信息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
()})// 隱藏 商品規(guī)格面板this.show
= false;},// ?獲取產(chǎn)品明細(xì)數(shù)據(jù)列表
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
});},// ?獲取相關(guān)產(chǎn)品數(shù)據(jù)列表
getRelatedData() {GetGoodsRelatedData
({ id: this.
$route.query.id
}).then
((res
) => {console.log
(3366, res
);this.goodsList
= res.data.goodsList
});},// ?獲取購物車商品數(shù)量
getCartData(){GetCartCountData
().then
((res
)=>{console.log
(7778,res
);this.badge
= res.data.cartTotal.goodsCount
})},
clickFn() {this.star_flag
= !this.star_flag
if (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:
}.info-price
{color: red
;}}.attribute
{ul
{li
{border-bottom: 1px solid 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: 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%
);}}.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
>
總結(jié)
以上是生活随笔為你收集整理的vue+vant 移动端H5 商城项目_03的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。