分类页布局
src/views/List.vue文件代码
<template>
<div class='list'>
<header>
<div class='returns'>
<i class='iconfont icon-fanhui'></i>
</div>
<div class='search'>
<i class='iconfont icon-fangdajing'></i>
<span>搜您喜欢的...</span>
</div>
<div class='go-home'>
<img src="@/assets/images/home.png" alt="">
</div>
</header>
<section>
<div class='list-l'>
<ul class='l-item'>
<li class='active'>推荐</li>
<li>推荐</li>
<li>推荐</li>
<li>推荐</li>
<li>推荐</li>
</ul>
</div>
<div class='list-r'>
<ul>
<li class='shop-list'>
<h2>推荐</h2>
<ul class='r-content'>
<li>
<img src="@/assets/images/list1.jpeg" alt="">
<span>铁观音</span>
</li>
<li>
<img src="@/assets/images/list1.jpeg" alt="">
<span>铁观音</span>
</li>
<li>
<img src="@/assets/images/list1.jpeg" alt="">
<span>铁观音</span>
</li>
<li>
<img src="@/assets/images/list1.jpeg" alt="">
<span>铁观音</span>
</li>
<li>
<img src="@/assets/images/list1.jpeg" alt="">
<span>铁观音</span>
</li>
</ul>
</li>
</ul>
</div>
</section>
<Tabbar></Tabbar>
</div>
</template>
<script>
import Tabbar from '@/components/common/Tabbar.vue'
export default {
name: "List",
components:{
Tabbar
}
};
</script>
<style scoped lang='scss'>
.list{
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
overflow: hidden;
}
header{
display: flex;
justify-content: space-between;
align-items: center;
height: 1.173333rem;
background-color: #b0352f;
.returns{
line-height: 1.173333rem;
padding:0 0.533333rem;
i{
color:#fff;
font-size:0.693333rem;
}
}
.search{
display: flex;
align-items: center;
flex: 1;
padding:0.16rem 0.266666rem;
background-color: #FFFFFF;
border-radius: 0.64rem;
i{
padding-right: 0.16rem;
color:#666;
font-size:0.48rem;
}
span{
color:#666;
font-size:0.373333rem;
}
}
.go-home{
padding: 0 0.266666rem;
line-height: 1.173333rem;
img{
width: 0.48rem;
height: 0.48rem;
}
}
}
section{
display: flex;
flex:1;
overflow:hidden;
}
.list-l{
width: 2.48rem;
background-color: #fff;
border-right: 1px solid #CCCCCC;
overflow: hidden;
.l-item{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
li{
width: 100%;
line-height: 1.333333rem;
text-align: center;
font-size:0.373333rem;
&.active{
color:#b54f4a;
border-left:6px solid #b54f4a;
}
}
}
}
.list-r{
flex:1;
overflow: hidden;
.shop-list{
text-align: center;
h2{
padding:0.533333rem 0;
font-size:0.64rem;
font-weight: 400;
}
.r-content{
display: flex;
flex-wrap: wrap;
li{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 33.33%;
padding:0.266666rem 0;
img{
width: 1.413333rem;
height: 1.413333rem;
}
span{
font-size:0.426666rem;
}
}
}
}
}
::v-deep.tabbar{
border-top:1px solid #CCCCCC;
}
</style>
分类页数据渲染
- server/routes/index.js文件进行接口配置
router.get('/api/goods/list',function(req,res,next){
res.send({
code:0,
data:[
{
}
]
})
})
- src/views/List.vue引入接口,渲染数据
<section>
<div class='list-l'>
<ul class='l-item'>
<li
v-for='(item,index) in leftData'
:key='index'
class='active'>{{item.name}}</li>
</ul>
</div>
<div class='list-r'>
<ul
v-for='(item,index) in rightData'
:key='index'
class='shop-list'
>
<li
v-for="(k,i) in item"
:key='i'
>
<h2>{{k.name}}</h2>
<ul class='r-content'>
<li v-for='(j,idx) in k.list'
:key='idx'>
<img :src="j.imgUrl" alt="">
<span>{{j.name}}</span>
</li>
</ul>
</li>
</ul>
</div>
</section>
import http from '@/common/api/request.js'
export default {
name: "List",
data () {
return {
leftData:[],//左侧数据
rightData:[]//右侧数据
}
},
components:{
Tabbar
},
async created(){
let res = await http.$axios({
url:'/api/goods/list',
})
let leftArr = [];
let rightArr = [];
res.forEach(v=>{
leftArr.push({
id:v.id,
name:v.name
})
rightArr.push( v.data );
})
this.leftData = leftArr;
this.rightData = rightArr;
}
};
</script>
效果图:
实现商品分类左右联动
<section>
<div class='list-l' ref='left'>
<ul class='l-item'>
<li
v-for='(item,index) in leftData'
:key='index'
:class='{active:index==currentIndex}'
@click='goScroll(index)'
>{{item.name}}</li>
</ul>
</div>
<div class='list-r' ref='right'>
<div>
<ul
v-for='(item,index) in rightData'
:key='index'
class='shop-list'
>
<li
v-for="(k,i) in item"
:key='i'
>
<h2>{{k.name}}</h2>
<ul class='r-content'>
<li v-for='(j,idx) in k.list'
:key='idx'>
<img :src="j.imgUrl" alt="">
<span>{{j.name}}</span>
</li>
</ul>
</li>
</ul>
</div>
</div>
</section>
<script>
import BetterScroll from 'better-scroll'
import Tabbar from '@/components/common/Tabbar.vue'
import http from '@/common/api/request.js'
export default {
name: "List",
data () {
return {
leftData:[],//左侧数据
rightData:[],//右侧数据
rightBScroll:'',//右侧滑动BScroll
allHeight:[],//承载右侧每一块的高度值
scrollY:''//右侧滚动距离
}
},
computed:{
currentIndex(){
return this.allHeight.findIndex((item, index)=>{
return this.scrollY >= item-100 && this.scrollY < this.allHeight[index+1]-100
})
}
},
components:{
Tabbar
},
async created(){
let res = await http.$axios({
url:'/api/goods/list',
})
let leftArr = [];
let rightArr = [];
res.forEach(v=>{
leftArr.push({
id:v.id,
name:v.name
})
rightArr.push( v.data );
})
this.leftData = leftArr;
this.rightData = rightArr;
this.$nextTick(()=>{
//左侧滑动
new BetterScroll(this.$refs.left,{
click:true
});
//右侧滑动
this.rightBScroll = new BetterScroll(this.$refs.right,{
click:true,
probeType:3
});
//统计右侧所有板块高度值,并且放入数组中
let height = 0;
this.allHeight.push(height);
//获取右侧每一块高度
let uls = this.$refs.right.getElementsByClassName('shop-list');
//把dom对象转换成功真正的数组
Array.from(uls).forEach(v=>{
height += v.clientHeight
this.allHeight.push( height )
})
//得到右侧滚动的值
this.rightBScroll.on('scroll',(pos)=>{
this.scrollY = Math.abs( pos.y );
})
})
},
methods:{
goScroll( index ){
this.rightBScroll.scrollTo(0,-this.allHeight[index],300);
}
}
};
</script>
进入商品详情页
- 新建src/views/Detail.vue,进行页面布局,并在src/router/index.js进行配置
- server/routes/index.js文件进行接口配置,src/views/Detail.vue中引入接口
//查询商品id的数据接口
router.get('/api/goods/id',function(req,res,next){
let id = req.query.id;
connection.query('select * from goods_list where id='+id+'',function(error,results){
if( error ) throw error;
res.json({
code:0,
data:results[0]
})
})
});
Detail.vue整体代码如下:
<template>
<div class='detail'>
<header>
<div class='header-returns' v-show='isShow'>
<div @click='goBack'>
<i class='iconfont icon-fanhui'></i>
</div>
<div>
<i class='iconfont icon-kefu'></i>
</div>
</div>
<div
class='header-bar'
v-show='!isShow'
:style='styleOption'
>
<div @click='goBack'>
<i class='iconfont icon-fanhui'></i>
</div>
<div>
<span>商品详情</span>
<span>商品评价</span>
</div>
<div>
<i class='iconfont icon-kefu'></i>
</div>
</div>
</header>
<section ref='wrapper'>
<div>
<div class='swiper-main'>
<swiper :options="swiperOption">
<swiper-slide
v-for='(item,index) in swiperList'
:key='index'
>
<img :src="item.imgUrl" alt="">
</swiper-slide>
</swiper>
<div class="swiper-pagination"></div>
</div>
<div class='goods-name'>
<h1>{{goods.name}}</h1>
<div>性价首选,茶感十足、鲜醇耐泡的大众口粮茶</div>
</div>
<div class='goods-price'>
<div class='oprice'>
<span>¥</span>
<b>{{goods.price}}</b>
</div>
<div class='pprice'>
<span>价格:</span>
<del>¥{{goods.price}}</del>
</div>
</div>
<div>
<img style='width:100%;height: 500px;' :src="goods.imgUrl" alt="">
<img style='width:100%;height: 500px;' :src="goods.imgUrl" alt="">
</div>
</div>
</section>
<footer>
<div class='add-cart' @click='addCart'>加入购物车</div>
<div >立即购买</div>
</footer>
</div>
</template>
<script>
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import BetterScroll from 'better-scroll'
import http from '@/common/api/request.js'
import { Toast } from 'mint-ui';
export default{
data(){
return{
id:0,
goods:{},
styleOption:{},
BetterScroll:'',
isShow:true,
swiperOption: {
autoplay: 3000,
speed: 1000,
pagination: {
el: '.swiper-pagination',
type:'fraction'
}
},
swiperList:[
{
imgUrl:'./images/goods1.jpg'
},
{
imgUrl:'./images/goods2.jpg'
},
{
imgUrl:'./images/goods3.jpg'
}
]
}
},
components: {
swiper,
swiperSlide
},
created(){
this.id = this.$route.query.id;
this.getData();
},
mounted(){
this.BetterScroll = new BetterScroll(this.$refs.wrapper, {
probeType: 3,
bounce: false,
click:true
})
this.BetterScroll.on('scroll',(pos)=>{
let posY = Math.abs( pos.y );
let opacity = posY / 180;
opacity = opacity > 1 ? 1 : opacity;
this.styleOption = {
opacity:opacity
}
if( posY >= 70 ){
this.isShow = false;
}else{
this.isShow = true;
}
})
},
activated(){
//判断当前url , id和之前id是否一致
if( this.$route.query.id != this.id ){
this.getData();
this.id = this.$route.query.id;
}
},
methods:{
async getData(){
let id = this.$route.query.id;
let res = await http.$axios({
url:'/api/goods/id',
params:{
id
}
})
this.goods = res;
},
//加入购物车
addCart(){
let id = this.$route.query.id;
http.$axios({
url:'/api/addCart',
method:"post",
data:{
goodsId:id
},
headers:{
token:true
}
}).then(res=>{
if( res.success ){
Toast('添加购物车成功');
}
})
},
//返回上一页
goBack(){
this.$router.back();
}
}
}
</script>
<style scoped lang="scss">
.detail{
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
overflow: hidden;
}
header{
position:fixed;
left:0;
top:0;
z-index: 999;
width: 100%;
height: 1.173333rem;
.header-returns{
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 1.173333rem;
div{
margin:0 0.266666rem;
width: 0.933333rem;
line-height: 0.906666rem;
text-align: center;
background-color: rgba(0,0,0,0.3);
border-radius: 50%;
}
i{
font-size:0.693333rem;
color:#fff;
}
}
.header-bar{
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 1.173333rem;
font-size:0.426666rem;
background-color: #fff;
span{
padding:0 0.266666rem;
}
i{
padding:0 0.266666rem;
font-size:0.586666rem;
}
}
}
section{
flex:1;
overflow: hidden;
}
.swiper-main{
position: relative;
width: 100%;
height:10rem;
overflow: hidden;
}
.swiper-container{
width: 100%;
height: 10rem;
}
.swiper-container img{
width: 100%;
height: 10rem;
}
.swiper-pagination{
bottom:0.266666rem;
width: 100%;
text-align: right;
color:#FFFFFF;
font-size:0.426666rem;
}
.swiper-pagination-fraction, .swiper-pagination-custom, .swiper-container-horizontal > .swiper-pagination-bullets{
left:-0.533333rem;
}
.goods-name{
padding:0.533333rem 0.266666rem;
border-bottom:1px solid #CCCCCC;
h1{
font-size:0.533333rem;
font-weight: 500;
}
div{
padding:0.08rem 0;
font-size:0.373333rem;
color: #999999;
}
}
.goods-price{
padding:0.533333rem 0.266666rem;
.oprice{
color:red;
span{
font-size:0.32rem;
}
}
.pprice{
color:#999999;
font-size:0.373333rem;
}
}
footer{
display: flex;
justify-content: center;
align-items: center;
position: fixed;
bottom:0;
left: 0;
width: 100%;
height: 1.306666rem;
border-top:1px solid #cccccc;
background-color: #fff;
div{
width: 50%;
line-height: 1.306666rem;
font-size: 0.426666rem;
text-align: center;
color:#fff;
background-color: red;
&.add-cart{
background-color: #FF9500;
}
}
}
</style>
效果图: