11 changed files with 503 additions and 36 deletions
-
6src/pages.json
-
2src/subpackage/mall/components/index_classify_bar.vue
-
13src/subpackage/mall/components/index_search_bar.vue
-
2src/subpackage/mall/components/specification_modal.vue
-
2src/subpackage/mall/js/api.js
-
25src/subpackage/mall/js/handle.js
-
441src/subpackage/mall/pages/classify.vue
-
34src/subpackage/mall/pages/goods_info.vue
-
12src/subpackage/mall/pages/index.vue
-
BINsrc/subpackage/mall/static/images/arrow_094.png
-
BINsrc/subpackage/mall/static/images/arrow_9ad_down.png
@ -0,0 +1,25 @@ |
|||
import { MALL_API } from "./api"; |
|||
import server from "./server"; |
|||
|
|||
// 购物车商品列表
|
|||
export function goodsCartList(brand_id){ |
|||
return server.post({ |
|||
url: MALL_API.goodsCartList, |
|||
data: { brand_id }, |
|||
isDefaultGet: false, |
|||
}) |
|||
.then(res => { |
|||
let _data = res?.data || {}; |
|||
if(_data.code === 0){ |
|||
let _ls = _data?.data ?? []; |
|||
//product_invalid 商品状态 0-正常 1-已失效
|
|||
return ({ |
|||
totalList: _ls, |
|||
validList: _ls.filter(item => item.product_invalid === 0), |
|||
invalidList: _ls.filter(item => item.product_invalid === 0), |
|||
}); |
|||
}else{ |
|||
return Promise.reject(_data); |
|||
} |
|||
}) |
|||
} |
@ -0,0 +1,441 @@ |
|||
<template> |
|||
<view class="mall-classify"> |
|||
<view class="mc-search-bar"> |
|||
<index-search-bar shopping-cart :cart-num="goodsCartNum"></index-search-bar> |
|||
</view> |
|||
<view class="mc-section"> |
|||
<scroll-view class="ms-classify" :style="scrollStyle" scroll-y> |
|||
<view class="mc-box"> |
|||
<view :class="['mc-item','defaultOne', curTab == -1 ? 'activeOne' : '',curTab==0?'filletOne':'']" @click="likeChange()">❤️猜你喜欢</view> |
|||
<view |
|||
v-for="(e, i) in goodsCateList" |
|||
:key="i" |
|||
:class="['mc-item', curTab == i ? 'active' : '',curTab == (i + 1)?'filletUnder':'', curTab == (i - 1)?'filletOn':'']" |
|||
@click="tabChange(e,i)" |
|||
> |
|||
{{ e.product_cate_name || '-' }} |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
<scroll-view class="ms-goods" :style="scrollStyle" scroll-y scroll-with-animation> |
|||
<!-- 除猜你喜欢外显示 --> |
|||
<view class="mg-tab" v-if="curTab != -1"> |
|||
<view v-for="(e, i) in goodsKeyList" :key="i" :class="['mt-item', curItemTab == e.id ? 'active' : '']" @click="tabItemChange(e)"> |
|||
{{ e.name }} |
|||
<view v-if="curItemTab == i && curItemTab!=0"> |
|||
<image |
|||
:src="isSort ? '/subpackage/mall/static/images/arrow_9ad_down.png' : '/subpackage/mall/static/images/arrow_094.png'" |
|||
:style="isSort ? 'transform: rotateZ(180deg)' : ''" |
|||
></image> |
|||
<image |
|||
:src="isSort ? '/subpackage/mall/static/images/arrow_094.png' : '/subpackage/mall/static/images/arrow_9ad_down.png'" |
|||
:style="isSort ? 'transform: rotateZ(180deg)' : ''" |
|||
></image> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="mg-list"> |
|||
<view class="ml-item" v-for="(e, i) in goodsList" :key="i" @click="goodsItemChange(e.id)"> |
|||
<image mode="aspectFill" :src="e.product_imgs[0] || ''"></image> |
|||
<view> |
|||
<view>{{e.product_name || ''}}</view> |
|||
<view>{{e.product_text || ''}}</view> |
|||
<!-- 特价标签 现在不做 --> |
|||
<!-- <view class="mi-tags"> |
|||
<view class="mt-b">特价</view> |
|||
</view> --> |
|||
<view class="mi-price"> |
|||
<view class="mp-price"> |
|||
<text>¥</text> |
|||
<text>{{e.product_price || 0}}</text> |
|||
<!-- 单位: /2支 现在不做 --> |
|||
<text>{{''}}</text> |
|||
<text v-if="e.product_spec_multi == 0 && e.product_price_show != 0">¥{{e.product_price_show || 0}}</text> |
|||
</view> |
|||
<view class="mp-btn" @click.stop="addGoodsChange(e)"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="mg-tip">我是有底线的~</view> |
|||
</scroll-view> |
|||
</view> |
|||
<!-- 分类 --> |
|||
<spacification-modal ref="spacificationModal" button-txt="加入购物车"></spacification-modal> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { routeTo, showLoad, hideLoad, showModal, getNodeMes } from "@/utils/util.js"; |
|||
import { MALL_API } from "../js/api"; |
|||
import server from "../js/server"; |
|||
import { goodsCartList } from "../js/handle"; |
|||
import spacificationModal from "../components/specification_modal.vue"; |
|||
import indexSearchBar from "../components/index_search_bar.vue"; |
|||
export default { |
|||
components: { spacificationModal, indexSearchBar }, |
|||
computed: { |
|||
scrollStyle() { |
|||
let { scrollHeight } = this; |
|||
return `height: ${scrollHeight}px;`; |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
scrollHeight: 658, // 658 为ipx的高度 |
|||
brand_id: '', |
|||
curTab: -1, //当前选中的商品分类列表id, 默认固定-1为猜你喜欢 |
|||
curItemTab: 0, |
|||
isSort: true, |
|||
goodsKeyList: [{id: 0, name: '综合'},{id: 1, name: '销量'},{id: 2, name: '价格'}], |
|||
goodsCateList: [], //商品分类列表 |
|||
goodsList: [], //商品列表 |
|||
goodsCartNum: 0, // 购物车数量 |
|||
cateId: -1, |
|||
|
|||
}; |
|||
}, |
|||
async onLoad(opts) { |
|||
let _bid = opts?.brand_id ?? ''; |
|||
this.brand_id = _bid |
|||
this.setScrollHeight(); |
|||
this.getCateList(_bid); |
|||
this.getGoodsList({ brand_id: _bid, recommend: 1 }); |
|||
}, |
|||
async onShow() { |
|||
let { brand_id } = this; |
|||
this.getCartList(brand_id); |
|||
}, |
|||
methods: { |
|||
async setScrollHeight() { |
|||
let searchBar = await getNodeMes('.mc-search-bar'); |
|||
let sysInfo = uni.getWindowInfo(); |
|||
this.scrollHeight = (sysInfo.windowHeight - searchBar.height) || 658; // 658 为ipx的高度 |
|||
}, |
|||
// 商品分类列表 |
|||
tabChange(e,i) { |
|||
let { brand_id } = this; |
|||
this.isSort = true |
|||
this.curTab = i; |
|||
this.cateId = e.id |
|||
this.getGoodsList({ brand_id, cateid: e.id }); |
|||
}, |
|||
// 猜你喜欢 |
|||
likeChange(){ |
|||
let { brand_id } = this; |
|||
this.curTab = -1; |
|||
this.getGoodsList({ brand_id, recommend: 1 }); |
|||
}, |
|||
// 综合 销量 价格 |
|||
tabItemChange(e) { |
|||
let { brand_id } = this; |
|||
this.curItemTab = e.id; |
|||
this.isSort = !this.isSort; |
|||
let sort = ''; |
|||
if(e.id == 1 && !this.isSort) sort = 'product_sale_nums|desc'; //销量从高到低 |
|||
if(e.id == 1 && this.isSort) sort = 'product_sale_nums|asc'; //销量从低到高 |
|||
if(e.id == 2 && !this.isSort) sort = 'product_price|desc'; //价格从高到低 |
|||
if(e.id == 2 && this.isSort) sort = 'product_price|asc'; //价格从低到高 |
|||
|
|||
this.$nextTick(_=>{ |
|||
this.getGoodsList({ |
|||
brand_id, |
|||
cateid: this.cateId, |
|||
show_sort: sort, |
|||
}); |
|||
}) |
|||
|
|||
}, |
|||
goodsItemChange(id) { |
|||
let { brand_id } = this; |
|||
routeTo(`/subpackage/mall/pages/goods_info?id=${id}&brand_id=${brand_id}`,'nT') |
|||
}, |
|||
async addGoodsChange(e) { |
|||
let { brand_id } = this; |
|||
// 如果商品是多个规格属性的,则出现弹窗给用户选择规格加入购物车,如果是单规格的则直接加入购物车 |
|||
// 根据product_spec_multi字段做判断 等于1则是多规格 |
|||
if(e.product_spec_multi == 1){ |
|||
return this.$refs.spacificationModal.alert({ |
|||
id: e?.id ?? '', |
|||
brand_id: brand_id, |
|||
poster: e?.product_imgs?.[0] ?? '', |
|||
name: e?.product_name ?? '', |
|||
price: e?.product_price ?? '', |
|||
specArr: e?.product_spec ?? [], |
|||
addCartComplete: res => { |
|||
this.getCartList(brand_id); |
|||
} |
|||
}); |
|||
} |
|||
// 单规格 |
|||
await this.$refs?.spacificationModal?.goodsCartAdd({ brand_id, id: e?.id, }); |
|||
this.getCartList(brand_id); |
|||
}, |
|||
// 商品分类列表 |
|||
getCateList(brand_id) { |
|||
showLoad(); |
|||
return server.post({ |
|||
url: MALL_API.goodsCateList, |
|||
data: { brand_id}, |
|||
isDefaultGet: false, |
|||
}) |
|||
.then(res => { |
|||
hideLoad(); |
|||
let _data = res?.data || {}; |
|||
if(_data.code === 0){ |
|||
return this.goodsCateList = _data?.data ?? []; |
|||
}else{ |
|||
return Promise.reject(_data); |
|||
} |
|||
}) |
|||
.catch(err => { |
|||
hideLoad(); |
|||
showModal({ content: err.message || '加载失败!' }); |
|||
console.warn('subpackage mall pages classify getCateList err --->', err); |
|||
}) |
|||
}, |
|||
// 商品列表 ps:猜你喜欢分类,recommend固定为1,分类ID传空; 其他分类又分 综合 销量 价格 |
|||
getGoodsList({brand_id, cateid='', show_sort='', recommend='', page = 1, page_size = 20}){ |
|||
// show_sort: 排序,eg:价格高到低(product_price|desc),销量低到高(product_sale_nums|asc) |
|||
// product_price这个表示价格的字段 product_sale_nums这个表示销量的字段 desc这个表示排序方法(desc是倒序asc是正序) |
|||
// show_sort等于空或者不传就是综合的 |
|||
let _query = { brand_id, cateid, show_sort, recommend, page, page_size }; |
|||
showLoad(); |
|||
return server.post({ |
|||
url: MALL_API.goodsList, |
|||
data: _query, |
|||
isDefaultGet: false, |
|||
}) |
|||
.then(res => { |
|||
hideLoad(); |
|||
let _data = res?.data || {}; |
|||
if(_data.code === 0){ |
|||
return this.goodsList = _data?.data?.list ?? []; |
|||
}else{ |
|||
return Promise.reject(_data); |
|||
} |
|||
}) |
|||
.catch(err => { |
|||
hideLoad(); |
|||
showModal({ content: err.message || '加载失败!' }); |
|||
console.warn('subpackage mall pages classify getGoodsList err --->', err); |
|||
}) |
|||
}, |
|||
|
|||
// 购物车-商品列表 |
|||
async getCartList(brand_id) { |
|||
try{ |
|||
let { validList } = await goodsCartList(brand_id); |
|||
let _length = validList?.length ?? 0; |
|||
this.goodsCartNum = _length > 100 ? 99 : _length; |
|||
}catch(err){ |
|||
console.warn('subpackage mall pages classify getCartList err --->', err); |
|||
} |
|||
}, |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.mall-classify { |
|||
.mc-section { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
.ms-classify { |
|||
flex-shrink: 0; |
|||
flex-grow: 0; |
|||
width: 180upx; |
|||
background-color: #ededf5; |
|||
.mc-box{ |
|||
background-color: #fff; |
|||
} |
|||
.mc-item { |
|||
padding: 0 10upx; |
|||
height: 92upx; |
|||
line-height: 92upx; |
|||
text-align: center; |
|||
font-size: 28upx; |
|||
color: #9a9a9d; |
|||
background-color: #ededf5; |
|||
@include tHide(1); |
|||
} |
|||
.active { |
|||
font-weight: 500; |
|||
background-color: #fff; |
|||
} |
|||
.filletOn{ |
|||
border-top-right-radius: 10rpx; |
|||
} |
|||
.filletUnder{ |
|||
border-bottom-right-radius: 10rpx; |
|||
} |
|||
.filletOne{ |
|||
border-bottom-right-radius: 10rpx; |
|||
} |
|||
.defaultOne { |
|||
color: #333; |
|||
} |
|||
.activeOne { |
|||
color: $mColor; |
|||
background-color: #fff; |
|||
} |
|||
} |
|||
.ms-goods { |
|||
// width: 570upx; |
|||
flex-grow: 1; |
|||
.mg-tab { |
|||
flex-grow: 1; |
|||
width: 100%; |
|||
height: 92rpx; |
|||
padding: 0rpx 40rpx; |
|||
background-color: #fbfbff; |
|||
@include ctf(space-between); |
|||
//吸顶 |
|||
/* #ifndef APP-PLUS-NVUE */ |
|||
display: flex; |
|||
position: -webkit-sticky; |
|||
/* #endif */ |
|||
position: sticky; |
|||
top: var(--window-top); |
|||
z-index: 6; |
|||
.mt-item { |
|||
color: #9a9a9d; |
|||
font-size: 24rpx; |
|||
min-width: 80rpx; |
|||
@include ctf(flex-start); |
|||
> view { |
|||
margin-left: 10rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
> image { |
|||
flex-shrink: 0; |
|||
width: 12rpx; |
|||
height: 12rpx; |
|||
} |
|||
} |
|||
} |
|||
.active { |
|||
color: $mColor; |
|||
} |
|||
} |
|||
.mg-list { |
|||
padding: 20upx 20upx 30upx; |
|||
background-color: #fff; |
|||
.ml-item { |
|||
padding-bottom: 28upx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
> image { |
|||
flex-shrink: 0; |
|||
margin-right: 20upx; |
|||
width: 172upx; |
|||
height: 172upx; |
|||
} |
|||
> view { |
|||
flex-grow: 1; |
|||
background-color: #fff; |
|||
> view:first-child { |
|||
margin-bottom: 10upx; |
|||
font-size: 28upx; |
|||
line-height: 40upx; |
|||
@include tHide(2); |
|||
} |
|||
> view:nth-child(2) { |
|||
margin-bottom: 14upx; |
|||
line-height: 32upx; |
|||
min-height: 32rpx; |
|||
font-size: 22upx; |
|||
color: #9a9a9d; |
|||
@include tHide(1); |
|||
} |
|||
.mi-tags { |
|||
margin-bottom: 10upx; |
|||
@include ctf(flex-start); |
|||
.mt-a { |
|||
margin-right: 10upx; |
|||
padding: 0 6upx; |
|||
border-radius: 6upx; |
|||
line-height: 28upx; |
|||
font-size: 20upx; |
|||
color: #ff873d; |
|||
background: rgba(246, 132, 62, 0.28); |
|||
} |
|||
.mt-b { |
|||
padding: 0 8upx; |
|||
font-size: 20upx; |
|||
line-height: 28upx; |
|||
border-radius: 8upx; |
|||
color: #e60012; |
|||
border: 2upx solid rgba($color: #e60213, $alpha: 0.5); |
|||
// background: rgba(230, 0, 18, 0.13); |
|||
} |
|||
} |
|||
.mi-price { |
|||
@include ctf(space-between); |
|||
.mp-price { |
|||
flex-grow: 1; |
|||
@include tHide(1); |
|||
line-height: 40upx; |
|||
color: #ff873d; |
|||
> text { |
|||
&:first-child { |
|||
font-size: 28upx; |
|||
} |
|||
&:nth-child(2) { |
|||
font-size: 36upx; |
|||
} |
|||
&:nth-child(3) { |
|||
font-size: 26upx; |
|||
color: #333; |
|||
} |
|||
&:nth-child(4) { |
|||
font-size: 26upx; |
|||
color: #9a9a9d; |
|||
text-decoration: line-through; |
|||
margin-left: 30upx; |
|||
} |
|||
} |
|||
} |
|||
.mp-btn { |
|||
flex-shrink: 0; |
|||
flex-grow: 0; |
|||
position: relative; |
|||
margin-left: 10upx; |
|||
width: 36upx; |
|||
height: 36upx; |
|||
border-radius: 50%; |
|||
background-color: $mColor; |
|||
&::before, |
|||
&::after { |
|||
content: ''; |
|||
display: block; |
|||
position: absolute; |
|||
left: 50%; |
|||
top: 50%; |
|||
transform: translate(-50%, -50%); |
|||
background-color: #fff; |
|||
border-radius: 2upx; |
|||
width: 20upx; |
|||
height: 4upx; |
|||
} |
|||
&::after { |
|||
height: 20upx; |
|||
width: 4upx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.mg-tip { |
|||
height: 200upx; |
|||
line-height: 200upx; |
|||
text-align: center; |
|||
font-size: 28upx; |
|||
color: #9a9a9d; |
|||
background-color: #f2f2f7; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
After Width: 12 | Height: 14 | Size: 169 B |
After Width: 12 | Height: 14 | Size: 165 B |
Write
Preview
Loading…
Cancel
Save
Reference in new issue