1 初始化
1 目录结构初始化
设置目录别名
vue.config.js >>
module.exports ={
configureWebpack:{
resolve:{
alias:{
'assets':'@/assets',
'components':'@/components',
'network':'@/network',
'utils':'@/utils',
'view':'@/view',
}
}
},
publicPath:'./'
}
home.vue >>
<template>
<div class="home">
<img src="assets/images/1.png" alt="">
<img :src="imgsrc" alt="">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from 'components/HelloWorld.vue'
export default {
name: 'Home',
data(){
return{
imgsrc:require('assets/images/1.png')
}
},
components: {
HelloWorld
}
}
</script>
<style>
</style>
2 初始化公共样式
base.css >>
@import "normalize.css";
:root{
--color-text:#666;
--color-high-text:#42bBaa;
--color-tint:#42b980;
--color-background:#FFFFFF;
--font-size:14px;
--line-height: 1.5;
}
*,*::before,*::after{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body{
user-select: none;
/*background-color: #FFFFFF;*/
background: var(--color-background);
color: var(--color-text);
width: 100vw;
}
a{
color: var(--color-text);
text-decoration: none;
}
.left{
float: left;
}
.right{
float: right;
}
home.vue >>
<template>
<div class="home">
<div class="demo1">this is a test</div>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from 'components/HelloWorld.vue'
export default {
name: 'Home',
data(){
return{
imgsrc:require('assets/images/1.png')
}
},
components: {
HelloWorld
}
}
</script>
<style>
.demo1{
color: var(--color-high-text);
}
</style>
3 初始化项目封装网络请求
(通用的网络请求位置,需要安装aixos)
cnpm i axios -S
request.js >>
import axios from 'axios';
export function request(config) {
const instance = axios.create({
baseURL:'https://api.shop.eduwork.cn',
timeout:5000
})
// 请求拦截
instance.interceptors.request.use(config=>{
//如果有一个接口需要认证才可以访问,就在此统一设置,(token)
//直接放行
return config
},error => {
})
// 响应拦截
instance.interceptors.response.use(res=>{
console.log(res)
return res.data ? res.data : res
},error => {
//如果需要授权才可以访问的接口,统一去login授权
// 如果有错误,在这里处理,根据状态码判断错误类型,
})
return instance(config)
}
home.js >>
import {request} from "./request";
export function getHomeAllData() {
return request({
url:'/api/index',
/*method:'get'//默认是get方法
params:{
}
*/
})
}
export function getBanner() {
}
home.vue >>
<template>
<div class="home">
{{banner}}
</div>
</template>
<script>
import {ref,onMounted} from 'vue'
import {getHomeAllData,getBanner} from 'network/home'
export default {
name: 'Home',
setup(){
const banner = ref([])
onMounted(()=>{
getHomeAllData().then(res=>{
banner.value = res.slides
}).catch(err=>{
})
})
return{
banner
}
}
}
components: {
}
</script>
<style>
</style>
2 项目-首页
1 项目的导航菜单制作 tab-bar
route/index.js >>
import { createRouter, createWebHistory } from 'vue-router'
const Home = () => import('../views/home/Home.vue')
const Category = () => import('../views/category/Category.vue')
const Detail = () => import('../views/detail/Detail.vue')
const Profile = () => import('../views/profile/Profile.vue')
const ShopCart = () => import('../views/shopcart/ShopCart.vue')
//写路由
const routes = [
{
path: '',
name: 'DefaultHome',
component: Home
},
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/category',
name: 'Category',
component: Category
},
{
path: '/detail',
name: 'Detail',
component: Detail
},{
path: '/profile',
name: 'Profile',
component: Profile
},{
path: '/shopcart',
name: 'ShopCart',
component: ShopCart
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
iconfont.css >>
@font-face {
font-family: "iconfont"; /* Project id 2708446 */
src: url('iconfont.woff2?t=1627537087958') format('woff2'),
url('iconfont.woff?t=1627537087958') format('woff'),
url('iconfont.ttf?t=1627537087958') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-gerenzhongxin:before {
content: "\e60b";
}
.icon-fenlei:before {
content: "\ef11";
}
.icon-shouye_huaban1:before {
content: "\e60c";
}
.icon-gouwuche_huaban1:before {
content: "\e614";
}
App.vue >>
<template>
<router-view/>
<div id="nav">
<router-link class="tab-bar-item" to="/">
<div class="icon"><img src="~assets/images/首页.png" height="20" width="20"/></div>
<div>首页</div>
</router-link>
<router-link class="tab-bar-item" to="/category">
<div class="icon"><img src="~assets/images/分类.png" height="20" width="20"/></div>
<div>分类</div>
</router-link>
<router-link class="tab-bar-item" to="/shopcart">
<div class="icon"><img src="~assets/images/购物车.png" height="20" width="20"/></div>
<div>购物车</div>
</router-link>
<router-link class="tab-bar-item" to="/profile">
<div class="icon"><img src="~assets/images/个人中心.png" height="20" width="20"/></div>
<div>我的</div>
</router-link>
</div>
</template>
<style lang="scss">
@import "assets/css/base.css";
@import "assets/css/icon/iconfont.css";
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
background-color: #F6F6F6;
display: flex;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -2px 1px rgba(100,100,100,0.1);
a {
color: var(--color-text);
&.router-link-exact-active {
/*color: #42b983;*/
color: var(--color-high-text);
}
}
.tab-bar-item{
flex: 1;
text-align: center;
height: 50px;
font-size: var(--font-size);
}
.tab-bar-item .icon{
width: 24px;
height: 24px;
margin-top: 5px;
vertical-align: middle;
display: inline-block;
}
}
</style>
2 项目标题栏(通过slot插槽)
其中考虑不同的回退方式和布局
(1)局部导航首位: component/common/navbar/NavBar.vue>>
<template>
<div class="nav-bar">
<div class="left" @click="goback"><slot name="left">
<img src="~assets/images/left.png" alt="">
</slot></div>
<div class="center"><slot>FY-SHOP</slot></div>
<div class="right"><slot name="right"></slot></div>
</div>
</template>
<script>
import {useRouter} from 'vue-router'
export default {
name: "NavBar",
setup(){
const router = useRouter()
const goback = () =>{
router.go(-1)
}
return {
goback
}
}
}
</script>
<style scoped>
.nav-bar{
display: flex;
background-color: var(--color-high-text);
color: #fffdef;
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 9;
height: 45px;
line-height: 45px;
text-align: center;
box-shadow: 0 2px 0px rgba(100,100,100,0.1);
}
.left,.right{
width: 60px;
}
.left img{
width: 45px;
padding: 12px;
}
.center{
flex: 1;
}
</style>
home.vue >>
(其他四个同样)
<template>
<div>
<nav-bar>
<template v-slot:default>图书兄弟</template>
</nav-bar>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
export default {
name: 'Home',
components: {
NavBar
}
}
</script>
(2)全局导航首位 router/index.js >>
import { createRouter, createWebHistory } from 'vue-router'
const Home = () => import('../views/home/Home.vue')
const Category = () => import('../views/category/Category.vue')
const Detail = () => import('../views/detail/Detail.vue')
const Profile = () => import('../views/profile/Profile.vue')
const ShopCart = () => import('../views/shopcart/ShopCart.vue')
//写路由
const routes = [
{
path: '',
name: 'DefaultHome',
component: Home,
meta:{
title:'图书兄弟'
}
},
{
path: '/',
name: 'Home',
component: Home,
meta:{
title:'图书兄弟'
}
},
{
path: '/category',
name: 'Category',
component: Category,
meta:{
title:'图书兄弟-商品分类'
}
},
{
path: '/detail',
name: 'Detail',
component: Detail,
meta:{
title:'图书兄弟-商品详情'
}
},{
path: '/profile',
name: 'Profile',
component: Profile,
meta:{
title:'图书兄弟-个人中心'
}
},{
path: '/shopcart',
name: 'ShopCart',
component: ShopCart,
meta:{
title:'图书兄弟-购物车'
}
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
router.beforeEach((to,from,next)=>{
//如果没有登录,在这里到login
next()
document.title = to.meta.title
}
)
export default router
3 首页推荐商品组件
banner 图和推荐商品
home.js >>
import {request} from "./request";
export function getHomeAllData() {
return request({
url:'/api/index',
})
}
Home.vue >>
<template>
<div>
<nav-bar>
<template v-slot:default>首页</template>
</nav-bar>
<div class="banners">
<img src="~assets/images/img.png" alt="">
</div>
<recommend-view :recommends="recommends"></recommend-view>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import RecommendView from "./ChildComps/RecommendView";
import {getHomeAllData} from "../../network/home";
import {ref,reactive,onMounted} from 'vue';
export default {
name: 'Home',
setup(){
const recommends = ref([])
onMounted(()=>{
getHomeAllData().then(res=>{
console.log(res.goods)
recommends.value = res.goods.data
})
})
return{
recommends
}
},
components: {
NavBar,
RecommendView,
}
}
</script>
<style scoped>
.banners img{
width: 100%;
height: auto;
margin-top: 45px;
}
</style>
RecommendsView.vue >>
<template>
<div class="recommend">
<div class="recommend-item" v-for="item in recommends.slice(0,4)" :key="item.id">
<a href="" @click.prevent="goDetail(item.id)">
<img :src="item.cover_url" alt="">
<div>{{item.title}}</div>
</a>
</div>
</div>
</template>
<script>
import {useRouter} from 'vue-router'
export default {
name: "RecommendView",
props:{
recommends: {
type:Array,
default(){
return[];
}
}
},
setup(){
const router = useRouter()
const goDetail = (id) =>{
router.push({path:'/detail',query:{id}})
}
return{
goDetail
}
}
}
</script>
<style scoped lang="scss">
.recommend{
display: flex;
width: 100%;
text-align: center;
padding: 15px 0 30px;
border-bottom: 8px solid #eeeeee;
font-size: 12px;
}
.recommend-item{
flex: 1;
img{
width: 70px;
height: 50px;
margin-bottom: 10px;
}
}
</style>
最后跳转到Detail.vue >> (注意:点击那本书,对应Detail页面banner显示对应的id号)
<template>
<div>
<nav-bar>
<template v-slot:default>商品详情:{{id}}</template>
</nav-bar>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import {useRoute} from 'vue-router'
import {ref} from 'vue'
export default {
name: "Detail",
components: {
NavBar
},
setup(){
const route = useRoute()
let id = ref(0)
id.value = route.query.id
return{
id
}
}
}
</script>
<style scoped>
</style>
4 首页选项卡组件
TabControl.vue >>
<template>
<div class="tab-control">
<div v-for="(item,index) in titles"
:key="index"
@click="itemClick(index)"
class="tab-control-item" :class="{active:index == currentIndex}">
<span>{{item}}</span>
</div>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name: "TabControl",
props:{
titles:{
type:Array,
default() {
return [];
}
}
},
setup(props,{emit}){
let currentIndex = ref(0)
const itemClick = (index) =>{
currentIndex.value = index
emit('tabClick',index)
}
return{
currentIndex,
itemClick
}
}
}
</script>
<style scoped lang="scss">
.tab-control{
display: flex;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 14px;
background-color: white;
width: 100%;
position: sticky;
top:44px;
.tab-control-item{
flex: 1;
.span{
padding: 6px;
}
}
.active{
color: var(--color-tint);
span{
border-bottom: 3px solid var(--color-tint);
}
}
}
</style>
Home.vue >>
<template>
<div>
<nav-bar>
<template v-slot:default>首页</template>
</nav-bar>
<div class="banners">
<img src="~assets/images/img.png" alt="">
</div>
<recommend-view :recommends="recommends"></recommend-view>
<tab-control @tabClick="tabClick" :titles="['畅销','new','精选']"></tab-control>
{{temid}}<br>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import RecommendView from "./ChildComps/RecommendView";
import TabControl from "../../components/content/tabControl/TabControl";
import {getHomeAllData} from "../../network/home";
import {ref,reactive,onMounted} from 'vue';
export default {
name: 'Home',
setup(){
//临时变量
let temid = ref(0)
const recommends = ref([])
onMounted(()=>{
getHomeAllData().then(res=>{
console.log(res.goods)
recommends.value = res.goods.data
})
})
const tabClick = (index)=>{
temid.value = index
}
return{
recommends,
temid,
tabClick
}
},
components: {
NavBar,
RecommendView,
TabControl,
}
}
</script>
<style scoped>
.banners img{
width: 100%;
height: auto;
margin-top: 45px;
}
</style>
5 列表组件原型图
GoodsListItem.vue >>
<template>
<div class="good-item">
<img src="~assets/images/1.png" alt="">
<div class="goods-info">
<p>标题</p>
<span class="price"><small>$</small>100</span>
<span class="collect">9</span>
</div>
</div>
</template>
<script >
export default {
name: "GoodsListItem"
}
</script>
<style scoped lang="scss">
.good-item{
width: 45%;
padding-bottom: 40px;
position: relative;
img{
width: 100%;
border-radius: 5%;
}
.goods-info{
font-size: 12px;
/*居中*/
position: absolute;
bottom: 5px;
left: 0;
right: 0;
overflow: auto;
text-align: center;
p{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 3px;
}
.price{
color: darkred;
margin-right: 20px;
}
.collect{
position: relative;
}
.collect::before{
center:'';
position: absolute;
left: -15px;
width: 14px;
height: 14px;
top:-1;
background:url('~assets/images/collect.png')0 0 /14px 14px;
}
}
}
</style>
GoodsList.vue >>
<template>
<div class="goods">
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
<goods-list-item></goods-list-item>
</div>
</template>
<script>
import GoodsListItem from "./GoodsListItem";
export default {
name: "GoodsList",
components:{
GoodsListItem
}
}
</script>
<style scoped>
.goods{
display: flex;
flex-wrap: wrap;
justify-content: space-around;
/* 四周环绕*/
padding: 5px;
}
</style>
Home.vue >>
<template>
<div>
<nav-bar>
<template v-slot:default>首页</template>
</nav-bar>
<div class="banners">
<img src="~assets/images/img.png" alt="">
</div>
<recommend-view :recommends="recommends"></recommend-view>
<tab-control @tabClick="tabClick" :titles="['畅销','new','精选']"></tab-control>
<goods-list></goods-list>
{{temid}}<br>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import RecommendView from "./ChildComps/RecommendView";
import TabControl from "../../components/content/tabControl/TabControl";
import GoodsList from "../../components/content/goods/GoodsList";
import {getHomeAllData} from "../../network/home";
import {ref,reactive,onMounted} from 'vue';
export default {
name: 'Home',
setup(){
//临时变量
let temid = ref(0)
const recommends = ref([])
onMounted(()=>{
getHomeAllData().then(res=>{
console.log(res.goods)
recommends.value = res.goods.data
})
})
const tabClick = (index)=>{
temid.value = index
}
return{
recommends,
temid,
tabClick
}
},
components: {
NavBar,
RecommendView,
TabControl,
GoodsList
}
}
</script>
<style scoped>
.banners img{
width: 100%;
height: auto;
margin-top: 45px;
}
</style>
6 商品列表绑定接口数据
Home.vue >>
<template>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import RecommendView from "./ChildComps/RecommendView";
import TabControl from "../../components/content/tabControl/TabControl";
import GoodsList from "../../components/content/goods/GoodsList";
import {getHomeAllData,getHomeGoods} from "../../network/home";
import {ref,reactive,onMounted} from 'vue';
export default {
name: 'Home',
setup(){
//临时变量
let temid = ref(0)
const recommends = ref([])
//商品列表数据模型
const goods = reactive({
sales:{page:0,list:[]},
recommend:{page:0,list:[]},
new:{page:0,list:[]},
})
onMounted(()=>{
getHomeAllData().then(res=>{
// console.log(res.goods)
recommends.value = res.goods.data
})
getHomeGoods('sales').then(res=>{
goods.sales.list = res.goods.data
})
getHomeGoods('recommend').then(res=>{
goods.recommend.list = res.goods.data
})
getHomeGoods('new').then(res=>{
goods.new.list = res.goods.data
})
console.log(goods)
})
const tabClick = (index)=>{
temid.value = index
}
return{
recommends,
temid,
tabClick,
goods
}
},
components: {
NavBar,
RecommendView,
TabControl,
GoodsList
}
}
获得了数据:
GoodsListItem.vue >>
<template>
<div class="good-item">
<img :src="product.cover_url" alt="">
<div class="goods-info">
<p>{{product.title}}</p>
<span class="price"><small>¥</small>{{product.price}}</span>
<span class="collect">{{product.collects_count}}</span>
</div>
</div>
</template>
<script >
export default {
name: "GoodsListItem",
props:{
product:Object,
default(){
return{}
}
}
}
</script>
<style scoped lang="scss">
.good-item{
width: 45%;
padding-bottom: 40px;
position: relative;
img{
width: 100%;
border-radius: 5%;
}
.goods-info{
font-size: 12px;
/*居中*/
position: absolute;
bottom: 5px;
left: 0;
right: 0;
overflow: auto;
text-align: center;
p{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 3px;
}
.price{
color: darkred;
margin-right: 20px;
}
.collect{
position: relative;
}
.collect::before{
center:'';
position: absolute;
left: -15px;
width: 14px;
height: 14px;
top:-1;
background:url('~assets/images/collect.png')0 0 /14px 14px;
}
}
}
</style>
GoodsList.vue >>
<template>
<div class="goods">
<goods-list-item v-for="item in goods" :product='item' :key="item.id"></goods-list-item>
</div>
</template>
<script>
import GoodsListItem from "./GoodsListItem";
export default {
name: "GoodsList",
props:{
goods:{
type:Array,
default(){
return []
}
}
},
components:{
GoodsListItem
}
}
</script>
<style scoped>
.goods{
display: flex;
flex-wrap: wrap;
justify-content: space-around;
/* 四周环绕*/
padding: 5px;
}
</style>
Home.vue >>
<template>
<div>
<nav-bar>
<template v-slot:default>首页</template>
</nav-bar>
<div class="banners">
<img src="~assets/images/img.png" alt="">
</div>
<recommend-view :recommends="recommends"></recommend-view>
<tab-control @tabClick="tabClick" :titles="['畅销','new','精选']"></tab-control>
<goods-list :goods="showGoods"></goods-list>
<!-- {{temid}}-->
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import RecommendView from "./ChildComps/RecommendView";
import TabControl from "../../components/content/tabControl/TabControl";
import GoodsList from "../../components/content/goods/GoodsList";
import {getHomeAllData, getHomeGoods} from "../../network/home";
import {ref, reactive, onMounted, computed} from 'vue';
export default {
name: 'Home',
setup() {
const recommends = ref([])
//商品列表数据模型
const goods = reactive({
sales: {page: 0, list: []},
recommend: {page: 0, list: []},
new: {page: 0, list: []},
})
let currentType = ref('sales')
const showGoods = computed(() => {
return goods[currentType.value].list
})
onMounted(() => {
getHomeAllData().then(res => {
// console.log(res.goods)
recommends.value = res.goods.data
})
getHomeGoods('sales').then(res => {
goods.sales.list = res.goods.data
})
getHomeGoods('recommend').then(res => {
goods.recommend.list = res.goods.data
})
getHomeGoods('new').then(res => {
goods.new.list = res.goods.data
})
console.log(goods)
})
const tabClick = (index) => {
// 切换 '畅销','new','精选'
let types = ['sales', 'new', 'recommend']
currentType.value = types[index]
}
return {
recommends,
// temid,
tabClick,
goods,
showGoods
}
},
components: {
NavBar,
RecommendView,
TabControl,
GoodsList
}
}
</script>
<style scoped>
.banners img {
width: 100%;
height: auto;
margin-top: 45px;
}
</style>
7 上拉数据加载更多数据
安装 better-scroll
cnpm i better-scroll -S
8 回到顶部组件
Home.vue>>
<template>
<!-- 2 添加 id="home" -->
<div id="home">
<!-- 1-3 引入组件-->
<nav-bar>
<template v-slot:default>图书商城</template>
</nav-bar>
<!-- 11 上拉页面时,固定顶部 -->
<tab-control
v-show="isTabFixed"
@tabClick="tabClick"
:titles="['畅销', '新书', '精选']"
/>
<!-- 1、开发上拉加载更多数据 固定 class="wrapper" class="content" -->
<div class="wrapper">
<div class="content">
<!-- 12 添加外层 div 和 ref -->
<div ref="banref">
<!-- ⚪️ 3-3 轮播图组件: 把数据传给子组件 :banners="banners" -->
<home-swiper :banners="banners"></home-swiper>
<!-- 加入推荐组件;-->
<!-- 6、给子组件传数据:以变量的方式 :recommend="recommend" -->
<recommend-view :recommends="recommends"/>
</div>
<!-- 3、使用内容选项卡组件, 并定义数组与值传给子组件 -->
<!-- 3 @tabClick 子组件传过来的事件,tabClick 父组件自定义的事件 -->
<tab-control @tabClick="tabClick" :titles="['畅销', '新书', '精选']"/>
<!-- 6 输出商品列表数据 -->
<!-- 8、把当前类型的数据,用属性方式传给子组件,默认是销量 -->
<goods-list :goods="showGoods"/>
</div>
</div>
<!-- 2-3 回到顶部组件;接收子组件传递事件,定义自己的方法 -->
<back-top @bTop="bTop" v-show="isShowBackTop"></back-top>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";// 1-1 引入顶部导航组件
import RecommendView from "./ChildComps/RecommendView";// 引入推荐组件
import TabControl from "../../components/content/tabControl/TabControl";// 1 引入内容选项卡组件
import GoodsList from "../../components/content/goods/GoodsList";
import BackTop from "../../components/common/backtop/BackTop";// ️ 2-1 回到顶部
import {getHomeAllData, getHomeGoods} from "../../network/home";//️ 1、 调用 api方法 getHomeGoods
// ref 空数组; reactive 空对象;
import {ref, reactive, onMounted, computed, watchEffect, nextTick} from "vue"; // 8 watchEffect 监听所有数据;nextTick 当 DOM渲染完执行的方法 ; 2、引入 reactive 引用代理对象,ref 数组,computed 计算属性、onMounted 生命周期
import BScroll from "better-scroll"; // 4 引入上拉加载数据插件
import HomeSwiper from "./ChildComps/HomeSwiper";
export default {
name: "Home",
setup() {
let isTabFixed = ref(false); // 默认不显示
let isShowBackTop = ref(false); // ️ 2-4 回到顶部按钮
let banref = ref(null); // 13
let banners = ref([]); // ⚪️ 3-4 轮播图
// let temid = ref(0) // 4 声明临时变量
// ref 空数组 引用
const recommends = ref([]); // 3、声明数据引用对象为空数组
// const demo = reactive({}) // reactive 空对象 代理
// 商品列表数据模型
const goods = reactive({
// 3
sales: {page: 1, list: []},
new: {page: 1, list: []},
recommend: {page: 1, list: []},
});
// 6、当前类型数据
let currentType = ref("sales");
const showGoods = computed(() => {
// computed 计算属性
return goods[currentType.value].list;
});
let bscroll = reactive({}); // 5 声明在外层 共用
onMounted(() => {
getHomeAllData().then((res) => {
recommends.value = res.goods.data; // 4、调用api方法,把获取到的数据赋值给定义的空数组
banners.value = res.slides; // ⚪️ 3-6 获取轮播图数据
// console.log(res.slides)
});
// 5、按销量查询
getHomeGoods("sales").then((res) => {
goods.sales.list = res.goods.data;
});
// 5、按最新查询
getHomeGoods("new").then((res) => {
goods.new.list = res.goods.data;
});
// 5、按推荐查询
getHomeGoods("recommend").then((res) => {
goods.recommend.list = res.goods.data;
});
// console.log(goods)
// 6 创建 BetterScroll 对象
bscroll = new BScroll(document.querySelector(".wrapper"), {
// 获取到最外层元素
probeType: 3, // 0,1,2,3, 3 只要在运行就触发 scroll 事件
click: true, // 是否允许点击
pullUpLoad: true, // 上拉加载更多,默认 false
});
// 7 触发滚动事件 - scroll 事件 - 位置 position
bscroll.on("scroll", (position) => {
// console.log(banref.value.offsetHeight) // 15 offsetHeight 偏移量的高度
// console.log(-position.y) // 打印滚动的距离
isShowBackTop.value = isTabFixed.value = -position.y > banref.value.offsetHeight; // ️ 2-6 isShowBackTop.value =
});
// 10 上拉加载更多数据,触发 pullingUp
bscroll.on("pullingUp", () => {
console.log('上拉加载更多......')
console.log('centerHeight:' + document.querySelector('.content').clientHeight)
const page = goods[currentType.value].page + 1;
getHomeGoods(currentType.value, page).then((res) => {
goods[currentType.value].list.push(...res.goods.data);
goods[currentType.value].page += 1;
});
// 完成上拉,等数据请求完成,要将新数据展示出来
bscroll.refresh(); // 刷新 重新计算高度
bscroll.finishPullUp();
console.log('当前类型:' + currentType.value + ',当前页:' + page)
});
});
const tabClick = (index) => {
// 5
// temid.value = index // 等于点击选项卡传过来的 索引
let types = ["sales", "new", "recommend"];
currentType.value = types[index];
nextTick(() => {
// 当 DOM 渲染完了执行方法
// 重新计算高度
bscroll && bscroll.refresh();
});
};
// 9 监听 任何一个变量有变化就会被触发
watchEffect(() => {
nextTick(() => {
// 当 DOM 渲染完了执行方法
// 重新计算高度
bscroll && bscroll.refresh();
});
});
const bTop = () => {
// ️ 2-7 回到顶部方法
// console.log('1111111111')
bscroll.scrollTo(0, 0, 500); // 前两个参数是x,y的位置,延迟 500 毫秒回到顶部
};
return {
recommends, // 5、 返回数据
// temid, // 6 临时的,现在不用了
tabClick, // 7
goods, // 4
showGoods, // 7
isTabFixed,
banref, // 14
isShowBackTop, // ️ 2-5 回到顶部
bTop, // 2-8
banners, // ⚪️ 3-5 轮播图数据
};
},
components: {
NavBar, // 1-2 注册组件
RecommendView,
TabControl, // 2、注册内容选项卡组件
GoodsList, // 5
BackTop, // ️ 2-2 注册回到顶部组件
HomeSwiper, // ⚪️ 3-2 注册轮播图组件
},
};
</script>
<style lang="scss" scoped>
#home {
// 3 这一整块 固定高度
height: 100vh; // 占领整个屏幕区域高度
position: relative;
.wrapper {
position: absolute;
top: 45px;
bottom: 50px;
left: 0; // 左右为0 内容可居中
right: 0;
overflow: hidden; // 超出部分隐藏
}
}
</style>
9 keep-alife(最好最后写!!)
路由跳转后(切换界面后),用keep-alife 组件保持操作之前的记忆。 注: 项目完成后在加上
App.vue >>
<route-view v-slot="{Component}">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</route-view>
10.vint组件swiper
1.安装vint组件
Vue 3 项目,安装 Vant 3:
cnpm i vant@next -S
2.安装插件babel-plugin-import
是一款 babel 插件,它会在编译过程中将 import 的写法自动转换为按需引入的方式。
cnpm i babel-plugin-import -D
- babel.config.js >>
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
}
4.main.js >>
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'vant/lib/index.css'
import { Swipe, SwipeItem } from 'vant'
createApp(App)
.use(Swipe).use(SwipeItem)
.use(store).use(router).mount('#app')
5.Home.vue >>
<template>
<!-- 2 添加 id="home" -->
<div id="home">
<!-- 1-3 引入组件-->
<nav-bar>
<template v-slot:default>首页</template>
</nav-bar>
<!-- 11 上拉页面时,固定顶部 -->
<tab-control v-show="isTabFixed" @tabClick="tabClick" :titles="['畅销','new','精选']"></tab-control>
<!-- 1、开发上拉加载更多数据 固定 class="wrapper" class="content" -->
<div class="wrapper">
<div class="content">
<!-- 12 添加外层 div 和 ref -->
<div ref="banref">
<div class="banners">
<home-swiper style="margin-top:45px" :banners="banners"></home-swiper>
</div>
<!-- 加入推荐组件;-->
<!-- 6、给子组件传数据:以变量的方式 :recommend="recommend" -->
<recommend-view :recommends="recommends"/>
</div>
<!-- 3、使用内容选项卡组件, 并定义数组与值传给子组件 -->
<!-- 3 @tabClick 子组件传过来的事件,tabClick 父组件自定义的事件 -->
<tab-control @tabClick="tabClick" :titles="['畅销','new','精选']"></tab-control>
<!-- 6 输出商品列表数据 -->
<!-- 8、把当前类型的数据,用属性方式传给子组件,默认是销量 -->
<goods-list :goods="showGoods"/>
</div>
</div>
<!-- 2-3 回到顶部组件;接收子组件传递事件,定义自己的方法 -->
<back-top @bTop="bTop" v-show="isShowBackTop"/>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";// 1-1 引入顶部导航组件
import RecommendView from "./ChildComps/RecommendView";// 引入推荐组件
import TabControl from "../../components/content/tabControl/TabControl";// 1 引入内容选项卡组件
import GoodsList from "../../components/content/goods/GoodsList";
import BackTop from "../../components/common/backtop/BackTop";// ️ 2-1 回到顶部
import {getHomeAllData, getHomeGoods} from "../../network/home";//️ 1、 调用 api方法 getHomeGoods
// ref 空数组; reactive 空对象;
import {ref, reactive, onMounted, computed,watchEffect,nextTick} from 'vue';
import BScroll from "better-scroll"; // 4 引入上拉加载数据插件
import HomeSwiper from "./ChildComps/HomeSwiper";
export default {
name: 'Home',
setup() {
let isShowBackTop = ref(false) // ️ 2-4 回到顶部按钮
let banref = ref(null) // 13
let isTabFixed = ref(false)// 默认不显示
const recommends = ref([])// 3、声明数据引用对象为空数组
// const demo = reactive({}) // reactive 空对象 代理
let banners = ref([])
//商品列表数据模型
const goods = reactive({
//3
sales: {page: 1, list:[]},
new: {page: 1, list:[]},
recommend: {page: 1, list:[]},
})
// 6、当前类型数据
let currentType = ref('sales')
// computed 计算属性
const showGoods = computed(() => {
return goods[currentType.value].list
})
let bscroll = reactive({})// 5 声明在外层 共用
onMounted(() => {
getHomeAllData().then(res => {
recommends.value = res.goods.data//4、调用api方法,把获取到的数据赋值给定义的空数组
banners.value = res.slides
})
// 5、按销量查询
getHomeGoods('sales').then(res => {
goods.sales.list = res.goods.data
})
// 5、按推荐查询
getHomeGoods('recommend').then(res => {
goods.recommend.list = res.goods.data
})
// 5、按最新查询
getHomeGoods('new').then(res => {
goods.new.list = res.goods.data
})
// console.log(goods)
//6 创建BetterScroll 对象
bscroll = new BScroll(document.querySelector('.wrapper'),{
// 获取到最外层元素
probeType:3,//0,1,2,3 3只要运动就触发scroll事件
click:true,//是否运行点击
pullUpLoad:true//上拉加载更多,默认是false
})
//7触发滚动事件- scroll 事件 - 位置 position
bscroll.on('scroll',(postion)=>{
console.log(banref.value.offsetHeight)// 15 offsetHeight 偏移量的高度
console.log(-position.y)// 打印滚动的距离
isShowBackTop.value = isTabFixed.value = (-position.y) > banref.value.offsetHeight
})
//上拉加载更多
bscroll.on('pullingUp',()=>{
console.log('上拉加载更多...')
const page = goods[currentType.value].page+1
getHomeGoods(currentType.value,page).then(res=>{
goods[currentType.value].list.push(...res.goods.data)
goods[currentType.value].page += 1
})
//完成上拉,等数据请求加载完,得将数据展示出来
bscroll.finishPullUp()
//重新计算高度
bscroll.refresh()
console.log('content-height'+document.querySelector('.content').clientHeight)
console.log('当前类型'+ currentType.value +',当前页'+page)
})
})
const tabClick = (index) => {
// 切换 '畅销','new','精选'
let types = ['sales', 'new', 'recommend']
currentType.value = types[index]
nextTick(()=>{
//重新计算高度
bscroll && bscroll.refresh()
})
}
//9监听 任何一个变量有变化
watchEffect(()=>{
nextTick(()=>{
// 当 DOM 渲染完了执行方法
//重新计算高度
bscroll && bscroll.refresh()
})
})
//回到顶部按钮
const bTop = () =>{
// ️ 2-7 回到顶部方法
bscroll.scrollTo(0,0,500)// 前两个参数是x,y的位置,延迟 500 毫秒回到顶部
}
return {
recommends,// 5、 返回数据
// temid, // 6 临时的,现在不用了
tabClick,//7
goods,//4
showGoods,//7
isShowBackTop,// ️ 2-5 回到顶部
isTabFixed,
banref,//14
bTop,// 2-8
banners
}
},
components: {
NavBar,// 1-2 注册组件
RecommendView,
TabControl,// 2、注册内容选项卡组件
GoodsList,// 5
BackTop,// ️ 2-2 注册回到顶部组件
HomeSwiper
},
}
</script>
<style scoped>
.banners img {
width: 100%;
height: auto;
}
#home{
/* 3 这一整块 固定高度*/
height: 100vh;/*占领整个屏幕区域高度*/
position: relative;
}
.wrapper{
position: absolute;
top: 45px;
bottom: 50px;
left: 0;/*左右为0 内容可居中*/
right: 0;
overflow: hidden;/*超出部分隐藏*/
}
.content{
}
</style>
- HomeSwiper.vue >>
<template>
<van-swipe v-if="banners.length" class="my-swipe" :autoplay="3000" indicator-color="white">
<van-swipe-item v-for="(item,index) in banners" :key="index">
<img :src="item.img_url" alt="">
</van-swipe-item>
</van-swipe>
</template>
<script>
export default {
name: "HomeSwiper",
props:{
banners:Array,
default(){
return[]
}
}
}
</script>
<style scoped>
.img{
width: 100%;
height: auto !important;
}
</style>
11.Vant组件库图片懒加载和徽章
1.main.js >>
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'vant/lib/index.css'
import { Swipe, SwipeItem, Lazyload,Badge} from 'vant'
createApp(App)
.use(Swipe).use(SwipeItem).use(Lazyload,{
loading:require('./assets/images/default.png')
}).use(Badge)
.use(store).use(router).mount('#app')
将 v-lazy 指令的值设置为你需要懒加载的图片。
<img v-for="img in imageList" v-lazy="img" />
徽标
<router-link class="tab-bar-item" to="/shopcart">
<div class="icon">
<van-badge :content="0" max="9">
<img src="~assets/images/购物车.png" height="20" width="20"/>
</van-badge>
</div>
<div>购物车</div>
</router-link>
3 项目 - 分类页面
1 分类页面布局和菜单
Sidebar 侧边导航
引入
import Vue from 'vue';
import { Sidebar, SidebarItem } from 'vant';
Vue.use(Sidebar);
Vue.use(SidebarItem);
Collapse 折叠面板, 手风琴
引入
import Vue from 'vue';
import { Collapse, CollapseItem } from 'vant';
Vue.use(Collapse);
Vue.use(CollapseItem);
通过 accordion 可以设置为手风琴模式,最多展开一个面板,此时 activeName 为字符串格式。
<van-collapse v-model="activeName" accordion>
<van-collapse-item title="标题1" name="1">内容</van-collapse-item>
<van-collapse-item title="标题2" name="2">内容</van-collapse-item>
<van-collapse-item title="标题3" name="3">内容</van-collapse-item>
</van-collapse>
注册
main.js>>
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'vant/lib/index.css'
import { Swipe, SwipeItem, Lazyload,Badge,Sidebar, SidebarItem,Collapse, CollapseItem} from 'vant'
createApp(App)
.use(Swipe).use(SwipeItem).use(Lazyload,{
loading:require('./assets/images/default.png')
}).use(Badge).use(Sidebar).use(SidebarItem).use(Collapse).use(CollapseItem)
.use(store).use(router).mount('#app')
Category.js>>
import {request} from "./request";
export function getCategory() {
return request({
url:'/api/goods',
})
}
Category.vue>>
<template>
<div>
<nav-bar>
<template v-slot:default>分类详情</template>
</nav-bar>
<div id="mainbox">
<div class="ordertab">
</div>
<!--折叠-->
<van-sidebar class="leftmenu" v-model="activeKey">
<van-collapse v-model="activeName" accordion><!--手风琴折叠-->
<van-collapse-item
v-for="item in categories" :key="item.id"
:title="item.name"
:name="item.name">
<!-- 4.变量categories.value中数据-->
<van-sidebar-item
v-for="sub in item.children"
:title="sub.name" />
</van-collapse-item>
</van-collapse>
</van-sidebar>
<div class="goodslist">
</div>
</div>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import {ref,reactive,onMounted} from 'vue';
import {getCategory} from "../../network/category";
export default {
name: "Category",
setup(){
let activeKay = ref(0)
let activeName = ref(1)
//2.申明数组
let categories = ref([])
onMounted(()=>{
//1 获取分类
getCategory().then(res=>{
// console.log(res)
// 3.获取res.categories的值,并赋给categories.value
categories.value = res.categories
console.log(categories.value)
})
})
return{
activeKay,
categories,
activeName
}
},
components: {
NavBar
}
}
</script>
<style scoped lang="scss">
#mainbox{
margin-top: 45px;
display: flex;
.ordertab{
width: 100%;
height: 50px;
flex: 1;
float: right;
z-index: 9;
position: fixed;
top: 45px;
right: 0;
left: 130px;
}
.leftmenu{
width: 130px;
position: fixed;
top: 95px;
left: 0;
width: 130px;
}
.goodslist{
flex: 1;
position: absolute;
top: 100px;
left: 130px;
right: 0;
height: 100vh; /* 占领整个可用大小*/
}
}
</style>
2 分类页面的交互模式(下拉加载更多)
Card 卡片
引入
import Vue from 'vue';
import { Card } from 'vant';
Vue.use(Card);
营销信息
通过 origin-price 设置商品原价,通过 tag 设置商品左上角标签。
<van-card
num="2"
tag="标签"
price="2.00"
desc="描述信息"
title="商品标题"
thumb="https://img01.yzcdn.cn/vant/ipad.jpeg"
origin-price="10.00"
/>
Category.vue>>
<template>
<div>
<nav-bar>
<template v-slot:default>分类详情</template>
</nav-bar>
<div id="mainbox">
<!--6 不同排序方式-->
<div class="ordertab">
<van-tabs v-model="active" @click="tabClick">
<van-tab title="销量排序"></van-tab>
<van-tab title="价格排序"></van-tab>
<van-tab title="评价排序"></van-tab>
</van-tabs>
</div>
<!--5 折叠-->
<van-sidebar class="leftmenu" v-model="activeKey">
<van-collapse v-model="activeName" accordion><!--手风琴折叠-->
<van-collapse-item
v-for="item in categories" :key="item.id"
:title="item.name"
:name="item.name">
<!-- 子菜单-->
<van-sidebar-item
v-for="sub in item.children"
:title="sub.name"
:key="sub.id"
@click="getGoods(sub.id)"
/><!-- 4.变量categories.value中数据-->
</van-collapse-item>
</van-collapse>
</van-sidebar>
<!--商品列表-->
<div class="goodslist">
<div class="content">
<van-card
num="2"
tag="标签"
price="2.00"
desc="描述信息"
title="商品标题"
thumb="https://img01.yzcdn.cn/vant/ipad.jpeg"
origin-price="10.00"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import {ref,reactive,onMounted} from 'vue';
import {getCategory} from "../../network/category";
export default {
name: "Category",
setup(){
let active = ref(1)
let activeKay = ref(0)
let activeName = ref(1)
//2.申明数组
let categories = ref([])
//8当前的排序条件
let currentOrder = ref(['sales'])
//11 当前的分类id
let currentCid = ref(0)
onMounted(()=>{
//1 获取分类
getCategory().then(res=>{
// console.log(res)
// 3.获取res.categories的值,并赋给categories.value
categories.value = res.categories
console.log(categories.value)
})
})
//7 排序选项卡
const tabClick = (index)=>{
let orders = ['sales','price','comment_count']
//9
currentOrder.value = orders[index]
console.log('排序的序号'+ orders[index])
console.log(currentCid.value)
}
//10 通过分类得到商品
const getGoods = (cid) =>{
currentCid.value = cid
console.log('当前分类Id'+currentCid.value)
console.log('排序的序号'+ currentCid.value)
}
return{
activeKay,
categories,
activeName,
active,
tabClick,
getGoods,
currentCid,
}
},
components: {
NavBar
}
}
</script>
<style scoped lang="scss">
#mainbox{
margin-top: 45px;
display: flex;
.ordertab{
height: 50px;
flex: 1;
float: right;
z-index: 9;
position: fixed;
top: 45px;
right: 0;
left: 130px;
}
.leftmenu{
width: 130px;
position: fixed;
top: 95px;
left: 0;
width: 130px;
}
.goodslist{
flex: 1;
position: absolute;
top: 100px;
left: 130px;
right: 0;
height: 100vh; /* 占领整个可用大小*/
padding: 10px;
}
}
</style>
3 从接口中获取分类数据
category.vue >>
<template>
<div>
<nav-bar>
<template v-slot:default>分类详情</template>
</nav-bar>
<div id="mainbox">
<!--6 不同排序方式-->
<div class="ordertab">
<van-tabs v-model="active" @click="tabClick">
<van-tab title="销量排序"></van-tab>
<van-tab title="价格排序"></van-tab>
<van-tab title="评价排序"></van-tab>
</van-tabs>
</div>
<!--5 折叠-->
<van-sidebar class="leftmenu" v-model="activeKey">
<van-collapse v-model="activeName" accordion><!--手风琴折叠-->
<van-collapse-item
v-for="item in categories" :key="item.id"
:title="item.name"
:name="item.name">
<!-- 子菜单-->
<van-sidebar-item
v-for="sub in item.children"
:title="sub.name"
:key="sub.id"
@click="getGoods(sub.id)"
/><!-- 4.变量categories.value中数据-->
</van-collapse-item>
</van-collapse>
</van-sidebar>
<!--商品列表-->
<div class="goodslist">
<div class="content">
<van-card
v-for="item in showGoods" :key="item.id"
:num="item.comments_count"
:tag="item.comments_count >=0 ? '流行':'标签'"
:price="item.price"
:desc="item.update_at"
:title="item.title"
:thumb="item.cover_url"
:lazy-load="true"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import {ref,reactive,onMounted,computed} from 'vue';
import {getCategory,getCategoryGoods} from "../../network/category";
export default {
name: "Category",
setup(){
let active = ref(1)
let activeKay = ref(0)
let activeName = ref(1)
//2.申明数组
let categories = ref([])
//8当前的排序条件
let currentOrder = ref(['sales'])
//11 当前的分类id
let currentCid = ref(0)
//12数据模型
const goods = reactive({
sales:{page:1,list:[]},
price:{page:1,list:[]},
comments_count:{page:1,list:[]}
})
//13响应式的,更新数据
const showGoods = computed(()=>{
return goods[currentOrder.value].list
})
//13 初始化数据,将接口里的数据放到数据模型中去
const init =()=> {
getCategoryGoods('sales',currentCid.value).then(res=>{
goods.sales.list = res.goods.data
})
getCategoryGoods('price',currentCid.value).then(res=>{
goods.price.list = res.goods.data
})
getCategoryGoods('comments_count',currentCid.value).then(res=>{
goods.comments_count.list = res.goods.data
})
}
onMounted(()=>{
//1 获取分类
getCategory().then(res=>{
// console.log(res)
// 3.获取res.categories的值,并赋给categories.value
categories.value = res.categories
console.log(categories.value)
})
//只需一个,因为默认就是「sales」
getCategoryGoods('sales',currentCid.value).then(res=>{
goods.sales.list = res.goods.data
})
})
//7 排序选项卡
const tabClick = (index)=>{
let orders = ['sales','price','comment_count']
//9
currentOrder.value = orders[index]
//14 更新,重新排序
getCategoryGoods(currentOrder.value ,currentCid.value).then(res=>{
goods[currentOrder.value].list = res.goods.data
})
console.log('当前分类id'+ currentCid.value)
console.log('排序的序号'+currentOrder.value)
}
//10 通过分类得到商品
const getGoods = (cid) =>{
currentCid.value = cid
init()//重新赋值
console.log('当前分类Id'+currentCid.value)
console.log('排序的序号'+ currentCid.value)
}
return{
activeKay,
categories,
activeName,
active,
tabClick,
getGoods,
currentCid,
goods,
showGoods
}
},
components: {
NavBar
}
}
</script>
<style scoped lang="scss">
#mainbox{
margin-top: 45px;
display: flex;
.ordertab{
height: 50px;
flex: 1;
float: right;
z-index: 9;
position: fixed;
top: 45px;
right: 0;
left: 130px;
}
.leftmenu{
width: 130px;
position: fixed;
top: 95px;
left: 0;
width: 130px;
}
.goodslist{
flex: 1;
position: absolute;
top: 100px;
left: 130px;
right: 0;
height: 100vh; /* 占领整个可用大小*/
padding: 10px;
text-align: left !important;
}
}
.van-card__thumb{
width: 68px !important;
}
</style>
4 better_scroll 上拉加载更多数据
category.vue>>
<template>
<div>
<nav-bar>
<template v-slot:default>分类详情</template>
</nav-bar>
<div id="mainbox">
<!--6 不同排序方式-->
<div class="ordertab">
<van-tabs v-model="active" @click="tabClick">
<van-tab title="销量排序"></van-tab>
<van-tab title="价格排序"></van-tab>
<van-tab title="评价排序"></van-tab>
</van-tabs>
</div>
<!--5 折叠-->
<van-sidebar class="leftmenu" v-model="activeKey">
<van-collapse v-model="activeName" accordion><!--手风琴折叠-->
<van-collapse-item
v-for="item in categories" :key="item.id"
:title="item.name"
:name="item.name">
<!-- 子菜单-->
<van-sidebar-item
v-for="sub in item.children"
:title="sub.name"
:key="sub.id"
@click="getGoods(sub.id)"
/><!-- 4.变量categories.value中数据-->
</van-collapse-item>
</van-collapse>
</van-sidebar>
<!--商品列表-->
<div class="goodslist">
<div class="content">
<van-card
v-for="item in showGoods" :key="item.id"
:num="item.comments_count"
:tag="item.comments_count >=0 ? '流行':'标签'"
:price="item.price"
:desc="item.update_at"
:title="item.title"
:thumb="item.cover_url"
:lazy-load="true"
/>
</div>
</div>
</div>
<back-top @bTop="bTop" v-show="isShowBackTop"></back-top>
</div>
</template>
<script>
import NavBar from "../../components/common/navbar/NavBar";
import BackTop from "../../components/common/backtop/BackTop";// ️ 2-1 回到顶部
import {ref, reactive, onMounted, computed, watchEffect, nextTick} from 'vue';
import {getCategory,getCategoryGoods} from "../../network/category";
import BScroll from "better-scroll";
export default {
name: "Category",
setup(){
let active = ref(1)
let activeKay = ref(0)
let activeName = ref(1)
//2.申明数组
let categories = ref([])
let isShowBackTop =ref(false)
//8当前的排序条件
let currentOrder = ref(['sales'])
//11 当前的分类id
let currentCid = ref(0)
//12数据模型
const goods = reactive({
sales:{page:1,list:[]},
price:{page:1,list:[]},
comments_count:{page:1,list:[]}
})
//13响应式的,更新数据
const showGoods = computed(()=>{
return goods[currentOrder.value].list
})
//13 初始化数据,将接口里的数据放到数据模型中去
const init =()=> {
getCategoryGoods('sales',currentCid.value).then(res=>{
goods.sales.list = res.goods.data
})
getCategoryGoods('price',currentCid.value).then(res=>{
goods.price.list = res.goods.data
})
getCategoryGoods('comments_count',currentCid.value).then(res=>{
goods.comments_count.list = res.goods.data
})
}
let bscroll = reactive({}); // 声明在外层 共用
onMounted(()=>{
//1 获取分类
getCategory().then(res=>{
// console.log(res)
// 3.获取res.categories的值,并赋给categories.value
categories.value = res.categories
// console.log(categories.value)
})
//只需一个,因为默认就是「sales」
getCategoryGoods('sales',currentCid.value).then(res=>{
goods.sales.list = res.goods.data
})
// 6 创建 BetterScroll 对象
bscroll = new BScroll(document.querySelector(".goodslist"), {
// 获取到最外层元素
probeType: 3, // 0,1,2,3, 3 只要在运行就触发 scroll 事件
click: true, // 是否允许点击
pullUpLoad: true, // 上拉加载更多,默认 false
});
//注册滚动事件
bscroll.on('scroll',(position)=>{
isShowBackTop.value = (-position.y)>30
})
// 10 上拉加载更多数据,触发 pullingUp
bscroll.on("pullingUp", () => {
console.log('上拉加载更多......')
const pages = goods[currentOrder.value].page +1
getCategoryGoods(currentOrder.value ,currentCid.value).then(res=>{
goods[currentOrder.value].list.push(...res.goods.data)
goods[currentOrder.value].page += 1
})
// 完成上拉,等数据请求完成,要将新数据展示出来
bscroll.finishPullUp();
//延迟效果
nextTick(() => {
// 当 DOM 渲染完了执行方法
// 重新计算高度
bscroll && bscroll.refresh();
});
// 刷新 重新计算高度
bscroll.refresh();
console.log('centerHeight:' + document.querySelector('.content').clientHeight)
console.log('当前类型:' + currentType.value + ',当前页:' + page)
});
});
//7 排序选项卡
const tabClick = (index)=>{
let orders = ['sales','price','comment_count']
//9
currentOrder.value = orders[index]
//14 更新,重新排序
getCategoryGoods(currentOrder.value ,currentCid.value).then(res=>{
goods[currentOrder.value].list = res.goods.data
//延迟效果
nextTick(() => {
// 当 DOM 渲染完了执行方法
// 重新计算高度
bscroll && bscroll.refresh();
});
})
console.log('当前分类id'+ currentCid.value)
console.log('排序的序号'+currentOrder.value)
}
//10 通过分类得到商品
const getGoods = (cid) =>{
currentCid.value = cid
init()//重新赋值
console.log('当前分类Id'+currentCid.value)
console.log('排序的序号'+ currentCid.value)
}
// 监听 任何一个变量有变化就会被触发
watchEffect(() => {
nextTick(() => {
// 当 DOM 渲染完了执行方法
// 重新计算高度
bscroll && bscroll.refresh();
});
});
const bTop =()=>{
bscroll.scrollTo(0,0,300)
}
return{
activeKay,
categories,
activeName,
active,
tabClick,
getGoods,
currentCid,
goods,
showGoods,
bscroll,
isShowBackTop,
bTop
}
},
components: {
NavBar,
BackTop
}
}
</script>
<style scoped lang="scss">
#mainbox{
margin-top: 45px;
display: flex;
.ordertab{
height: 50px;
flex: 1;
float: right;
z-index: 9;
position: fixed;
top: 45px;
right: 0;
left: 130px;
}
.leftmenu{
width: 130px;
position: fixed;
top: 95px;
left: 0;
width: 130px;
}
.goodslist{
flex: 1;
position: absolute;
top: 100px;
left: 130px;
right: 0;
height: 100vh; /* 占领整个可用大小*/
padding: 10px;
text-align: left !important;
/*.content{*/
/* !*background-color: darkred;*!*/
/* padding-top: 10px;*/
/*}*/
}
}
.van-card__thumb{
width: 68px !important;
}
</style>
(这里报错了,下次修改)