VUE实现商城系统
产品预览
目录
产品预览
准备工作
src目录下创建项目
- api
- assets(fonts, img, js, scss)
- base
- components
- pages
- router
1、引入assets下的字体文件,然后配置scss。(_variables.scss、_mixins.scss、_reset.scss、_icons.scss、_base.scss、index.scss 加入下划线是为了不被编译为css,统一在index中引入 )
//引入 main.js
import 'assets/scss/index.scss';
2、配置mian.js 引入index.scss完成基础配置。sass引入无法识别、需要引入两个插件sass-loader、node-sass
cnpm install sass-loader@7.3.1 --save-dev
cnpm install --save-dev node-sass
3、安装一些包 babel-polyfill fastclick、 然后在mian.js引入
安装
cnpm install --save-dev babel-polyfill
cnpm install --save-dev fastclick
引入 main.js
import 'babel-polyfill'
import fastclick from 'fastclick'
fastclick.attach(document.body);
4、路由下的@符号是别名的意思,我们可以去build下的webpack.base.conf.js文件去修改。修改后路由下就可以用别名作为路径。
//原来别名
alias: {
'@': resolve('src'),
'abc': resolve('src'),
}
//修改后
alias: {
'api': resolve('src/api'),
'assets': resolve('src/assets'),
'base': resolve('src/base'),
'components': resolve('src/components'),
'pages': resolve('src/pages')
}
项目开始
页面结构和根组件 app.vue
<template>
<div id="app" class="g-container">
<div class="g-view-container">
<router-view/>
</div>
<div class="g-footer-container">
</div>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
/*_container.scss*/
@import "mixins";
.g-container {
overflow: hidden;
position: relative;
width: 100%;
max-width: 640px;
min-width: 320px;
height: 100%;
margin: 0 auto;
}
.g-view-container {
height: 100%;
padding-bottom: $tabbar-height;
}
.g-content-container {
height: 100%;
padding-top: $navbar-height;
}
.g-header-container {
position: absolute;
left: 0;
top: 0;
z-index: $navbar-z-index;
width: 100%;
}
.g-footer-container {
position: absolute;
left: 0;
bottom: 0;
z-index: $tabbar-z-index;
width: 100%;
box-shadow: 0 0 10px 0 hsla(0, 6%, 58%, 0.6);
}
.g-backtop-container {
position: absolute;
z-index: $backtop-z-index;
right: 10px;
bottom: $tabbar-height + 10px;
}
如果想要保持占满全屏 不溢出需要全局设置body和html 宽高、溢出隐藏
html, body {
overflow: hidden;
width: 100%;
height: 100%;
}
编写底部导航栏
//app.vue 界面引入自定义底部导航组件
<template>
<div id="app" class="g-container">
<div class="g-view-container">
<router-view/>
</div>
<div class="g-footer-container">
<c-tabber/>
</div>
</div>
</template>
<script>
import CTabber from './components/tabber';
export default {
name: 'App',
components: {
CTabber
}
}
</script>
//commponents下tabber index.vue
<template>
<div class="g-tabbar">
<router-link class="g-tabbar-item" to="/home">
<i class="iconfont icon-home"></i>
<span>首页</span>
</router-link>
<router-link class="g-tabbar-item" to="/category">
<i class="iconfont icon-category"></i>
<span>分类页</span>
</router-link>
<router-link class="g-tabbar-item" to="/cart">
<i class="iconfont icon-cart"></i>
<span>购物车</span>
</router-link>
<router-link class="g-tabbar-item" to="/personal">
<i class="iconfont icon-personal"></i>
<span>个人中心</span>
</router-link>
</div>
</template>
<script>
export default {
name: 'CTabber'
}
</script>
<style lang='scss' scoped>
// 这里引入一定要加波浪线 不然报错
@import "~assets/scss/mixins";
.router-link-active{
color: $link-active-color;
}
</style>
/*_tabber.scss */
@import "mixins";
.g-tabbar {
display: flex;
width: 100%;
height: $tabbar-height;
background-color: #fff;
&-item {
flex: 1;
@include flex-center(column);
.iconfont {
margin-bottom: 4px;
font-size: $icon-font-size;
}
}
}
//_mixins
@import "variables";
// flex-center
@mixin flex-center($direction: row) {
display: flex;
justify-content: center;
align-items: center;
flex-direction: $direction;
}
效果
编写路由
创建页面并配置路由,home、product、category、cart、personal、seach
注意:*通配符 如果上面没有匹配到就导航到home、() => import('pages/home')路由懒加载,只在显示是加载页面信息。
//router配置
import Vue from 'vue'
import Router from 'vue-router'
// import Home from 'pages/home'
// import Category from 'pages/category'
// import Cart from 'pages/cart'
// import Personal from 'pages/personal'
// import Product from 'pages/product'
// import Search from 'pages/search'
Vue.use(Router)
const routes = [
{
name: 'home',
path: '/home',
component: () => import('pages/home'),
children: [
{
name: 'home-product',
path: 'product/:id',
component: () => import('pages/product')
}
]
},
{
name: 'category',
path: '/category',
component: () => import('pages/category')
},
{
name: 'cart',
path: '/cart',
component: () => import('pages/cart')
},
{
name: 'personal',
path: '/personal',
component: () => import('pages/personal')
},
{
name: 'search',
path: '/search',
component: () => import('pages/search')
},
{
path: '*',
redirect: '/home'
}
]
export default new Router({
routes
})
编写Home页面
顶部标题栏
//home下 index.vue
<template>
<div class="home">
<header class="g-header-container">
<home-header></home-header>
</header>
<div></div>
<div class="g-backtop-container"></div>
<router-view></router-view>
</div>
</template>
<script>
import HomeHeader from './header';
export default {
name: 'Home',
components: {
HomeHeader
}
}
</script>
<style lang="scss" scoped>
@import '~assets/scss/mixins';
.home{
overflow: hidden;
width: 100%;
height: 100%;
background-color: $bgc-theme;
}
</style>
//home下 header.vue
<template>
<me-navbar class="header" title='titl'>
<i class="iconfont icon-scan" slot="left"></i>
<div slot="center">搜索框</div>
<i class="iconfont icon-msg" slot="right"></i>
</me-navbar>
</template>
<script>
import MeNavbar from 'base/navbar';
export default {
name: 'HomeHeader',
components: {
MeNavbar
}
}
</script>
<style lang="scss" scoped>
@import "~assets/scss/mixins";
.header{
&.mine-navbar{
//透明背景
background-color: transparent;
// background-color: $header-bgc-translucent;
}
.iconfont{
color: $icon-color-default;
font-size: $icon-font-size;
}
}
</style>
//base下navbar
<template>
<div class="mine-navbar">
<div class="mine-navbar-left" v-if="$slots.left">
<slot name="left"></slot>
</div>
<div class="mine-navbar-center" v-if="$slots.center">
<slot name="center"></slot>
</div>
<div class="mine-navbar-right" v-if="$slots.right">
<slot name="right"></slot>
</div>
<h1 class="mine-navbar-title" v-if="title">
<span class="mine-navbar-text" v-text="title"></span>
</h1>
</div>
</template>
<script>
export default {
name: 'nav',
props: {
title: {
type: String,
default: ''
}
}
}
</script>
<style lang="scss" scoped>
@import "~assets/scss/mixins";
.mine-navbar{
position: relative;
@include flex-between();
height: 50px;
background-color: #fff;
&-left{
margin-left: 10px;
~ .mine-navbar-right{
position: static;
}
}
&-center{
flex: 1;
margin: 0 10px;
~ .mine-navbar-right{
position: static;
}
}
&-right{
position: absolute;
right: 0;
@include flex-center();
height: 100%;
margin-right: 10px;
}
&-title{
position: absolute;
top: 0;
bottom: 0;
left: 20%;
right: 20%;
@include flex-center();
//解决span中 jqjqjqajq 被切掉j前面一段和q下面一段问题 主要因为设置了溢出隐藏
text-align: center;
}
&-text{
font-size: 18px;
@include ellipsis();
//解决span中 jqjqjqajq 被切掉j前面一段和q下面一段问题 主要因为设置了溢出隐藏
line-height: 1.5;
width: 100%;
}
}
</style>
轮播图
安装vue-awesome-swiper
cnpm install --save-dev vue-awesome-swiper@3.1.3
//main.js 全局引入css
import 'swiper/dist/css/swiper.css'
//home 下 index.vue
<template>
<div class="home">
<header class="g-header-container">
<home-header></home-header>
</header>
<div>
<home-slider></home-slider>
</div>
<div class="g-backtop-container"></div>
<router-view></router-view>
</div>
</template>
<script>
import HomeHeader from './header';
import HomeSlider from './slider';
export default {
name: 'Home',
components: {
HomeHeader,
HomeSlider
}
}
</script>
<style lang="scss" scoped>
@import '~assets/scss/mixins';
.home{
overflow: hidden;
width: 100%;
height: 100%;
background-color: $bgc-theme;
}
</style>
//header.vue
<template>
<me-navbar class="header">
<i class="iconfont icon-scan" slot="left"></i>
<div slot="center">搜索框</div>
<i class="iconfont icon-msg" slot="right"></i>
</me-navbar>
</template>
<script>
import MeNavbar from 'base/navbar';
export default {
name: 'HomeHeader',
components: {
MeNavbar
}
}
</script>
<style lang="scss" scoped>
@import "~assets/scss/mixins";
.header{
&.mine-navbar{
//透明背景
background-color: transparent;
// background-color: $header-bgc-translucent;
}
.iconfont{
color: $icon-color-default;
font-size: $icon-font-size;
}
}
</style>
//base下创建 slider 下创建index.vue
<template>
<swiper :options="swiperOption">
<slot></slot>
<div class="swiper-pagination" v-if="pagination" slot="pagination"></div>
</swiper>
</template>
<script>
import { swiper } from 'vue-awesome-swiper'
export default {
name: 'MeSlider',
components: {
swiper
},
props: {
direction: {
type: String,
default: 'horizontal',
validator(value){
return [
'horizontal',
'vertical'
].indexOf(value) > -1
}
},
interval:{
type: Number,
default: 3000,
validator(value){
return value >= 0
}
},
loop:{
type:Boolean,
default: true
},
pagination:{
type:Boolean,
default: true
}
},
data () {
return {
swiperOption: {
watchOverflow: true,
direction: this.direction,
autoplay: this.interval ? {
delay: this.interval,
disableOnInteraction: false
} : false,
slidesPerView: 1,
loop: this.loop,
pagination: {
el: this.pagination ? '.swiper-pagination' : null
}
}
};
}
};
</script>
<style lang="scss" scoped>
@import "~assets/scss/mixins";
.swiper-container{
height: 100%;
width: 100%;
}
</style>
//config.js
export const sliderOptions = {
direction: 'horizontal',
loop: true,
interval: 0,
pagination: true,
}
网络请求轮播图地址
安装axios插件,
cnpm install --save-dev axios
//home 下的 index.vue
<template>
<div class="slider-wrapper">
<me-slider
:direction="direction"
:loop="loop"
:interval="interval"
:pagination="pagination"
v-if="sliders.length"
>
<swiper-slide
v-for="(item, index) in sliders"
:key="index"
>
<a :href="item.linkUrl" class="slider-link">
<img :src="item.picUrl" class="slider-img">
</a>
</swiper-slide>
</me-slider>
</div>
</template>
<script>
import MeSlider from 'base/slider';
import { swiperSlide } from 'vue-awesome-swiper';
import { sliderOptions } from './config';
import { getHomeSlider } from 'api/home';
export default {
name: 'HomeSlider',
components: {
MeSlider,
swiperSlide
},
data () {
return{
direction: sliderOptions.direction,
loop: sliderOptions.loop,
interval: sliderOptions.interval,
pagination: sliderOptions.pagination,
sliders: []
//用v-if之后 可以让组件在加载完数据后 渲染
}
},
created () {
this.getSliders();
},
methods: {
getSliders() {
getHomeSlider().then(data => {
console.log(data)
this.sliders = data
})
}
}
}
</script>
<style lang="scss" scoped>
.slider-wrapper{
height: 183px;
}
.slider-link{
display: block;
}
.slider-link, .slider-img{
width: 100%;
height: 100%;
}
</style>
//api下的 home.js
import axios from 'axios';
import { SUCC_CODE, TIMEOUT } from './config';
// 获取幻灯片数据 -- ajax
export const getHomeSlider = () => {
return axios.get('http://www.imooc.com/api/home/slider', {
//这里是为了延迟10s 让网络超时 执行catch
// timeout: TIMEOUT
}).then(res => {
console.log(res)
if(res.data.code === SUCC_CODE){
return res.data.slider
}
throw new Error('没有成功获取到数据!')
}).catch(err => {
if (err){
console.log(err);
}
return [
{
linkUrl: 'https://www.imooc.com',
picUrl: require('assets/img/404.png')
}
]
}).then(data => {
// 这里是延迟1s返回 为了加 正在加载效果
return new Promise(resolve => {
setTimeout(() => {
resolve(data);
}, 1000);
});
})
};
//api 下的 config.js
export const SUCC_CODE = 0
export const TIMEOUT = 10000;
正在加载组件
//home 下 slider.vue 引入正在加载组件并根据轮播图是否有数据显示
<template>
<div class="slider-wrapper">
<me-loading v-if="!sliders.length"></me-loading>
<me-slider
:direction="direction"
:loop="loop"
:interval="interval"
:pagination="pagination"
v-if="sliders.length"
>
<swiper-slide
v-for="(item, index) in sliders"
:key="index"
>
<a :href="item.linkUrl" class="slider-link">
<img :src="item.picUrl" class="slider-img">
</a>
</swiper-slide>
</me-slider>
</div>
</template>
<script>
import MeSlider from 'base/slider';
import { swiperSlide } from 'vue-awesome-swiper';
import { sliderOptions } from './config';
import { getHomeSlider } from 'api/home';
import MeLoading from 'base/loading';
export default {
name: 'HomeSlider',
components: {
MeSlider,
MeLoading,
swiperSlide
},
data () {
return{
direction: sliderOptions.direction,
loop: sliderOptions.loop,
interval: sliderOptions.interval,
pagination: sliderOptions.pagination,
sliders: []
//用v-if之后 可以让组件在加载完数据后 渲染
}
},
created () {
this.getSliders();
},
methods: {
getSliders() {
getHomeSlider().then(data => {
console.log(data)
this.sliders = data
})
}
}
}
</script>
<style lang="scss" scoped>
.slider-wrapper{
height: 183px;
}
.slider-link{
display: block;
}
.slider-link, .slider-img{
width: 100%;
height: 100%;
}
</style>
//base下 slider组件 实现正在加载
<template>
<div class="mine-loading" :class="{'mine-loading-inline' : inline}">
<span class="mine-loading-indicator" v-if="indicator === 'on'">
<slot><img src="./loading.gif" alt=""></slot>
</span>
<span class="mine-loading-text" v-if="text">{{text}}</span>
</div>
</template>
<script>
export default {
name: 'MeLoading',
props: {
indicator:{
type: String,
default: 'on',
validator(value){
return ['on', 'off'].indexOf(value) > -1;
}
},
text: {
type: String,
default: '加载中...',
},
inline: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss" scoped>
@import "~assets/scss/mixins";
.mine-loading{
overflow: hidden;
width: 100%;
height: 100%;
@include flex-center(column);
&.mine-loading-inline {
flex-direction: row;
.mine-loading-indicator ~ .mine-loading-text{
margin-top: 0px;
margin-left: 6px;
}
}
}
.mine-loading-indicator ~ .mine-loading-text{
margin-top: 6px;
}
</style>
滚动条组件
//home内引入 scroll组件
<template>
<div class="home">
<header class="g-header-container">
<home-header></home-header>
</header>
<me-scroll>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
<home-slider></home-slider>
</me-scroll>
<div class="g-backtop-container"></div>
<router-view></router-view>
</div>
</template>
<script>
import MeScroll from 'base/scroll';
import HomeHeader from './header';
import HomeSlider from './slider';
export default {
name: 'Home',
components: {
MeScroll,
HomeHeader,
HomeSlider
}
}
</script>
<style lang="scss" scoped>
@import '~assets/scss/mixins';
.home{
overflow: hidden;
width: 100%;
height: 100%;
background-color: $bgc-theme;
}
</style>
//scroll组件
<template>
<swiper :options="swiperOption">
<swiper-slide>
<slot></slot>
</swiper-slide>
<div class="swiper-scrollbar" v-if="scrollbar" slot="scrollbar"></div>
</swiper>
</template>
<script>
import {swiper, swiperSlide} from 'vue-awesome-swiper'
export default {
name: 'scroll',
components: {
swiperSlide,
swiper
},
props: {
scrollbar: {
type: Boolean,
default: true
}
},
data() {
return {
swiperOption: {
direction: 'vertical',
slidesPerView: 'auto',
freeMode: true,
setWrapperSize: true,
scrollbar: {
el: this.scrollbar ? '.swiper-scrollbar' : null,
hide: true
}
}
}
}
}
</script>
<style lang="scss" scoped>
.swiper-container{
overflow: hidden;
width: 100%;
height: 100%;
}
.swiper-slide{
height: auto;
}
</style>
导航面板
在home引入 nav.vue
//nav.vue
<template>
<nav class="nav">
<ul class="nav-list">
<li
class="nav-item"
v-for="(item, index) in navs"
:key="index">
<a :href="item.linkUrl" class="nav-link">
<img :src="item.picUrl" class="nav-pic">
<span>{{item.text}}</span>
</a>
</li>
</ul>
</nav>
</template>
<script>
import {navItems} from './config';
export default {
name: 'HomeNav',
created() {
this.navs = navItems;
}
};
</script>
<style lang="scss" scoped>
@import "~assets/scss/mixins";
.nav {
width: 100%;
padding-top: 15px;
background-color: #fff;
&-list {
display: flex;
flex-wrap: wrap;
}
&-item {
width: 20%;
margin-bottom: 15px;
}
&-link {
@include flex-center(column);
}
&-pic {
width: 60%;
margin-bottom: 8px;
}
}
</style>
//config.js
export const sliderOptions = {
direction: 'horizontal',
loop: true,
interval: 0,
pagination: true,
}
export const navItems = [
// 原nav.uve的数据
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-1.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-2.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-3.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-4.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-5.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-6.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-7.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-8.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-9.png'),
text: '拍卖'
},
{
linkUrl: 'https://www.imooc.com',
picUrl: require('./img/nav-item-10.png'),
text: '拍卖'
}
];
热卖推荐组件
安装jsonp、安装vue-lazyload
cnpm install --save-dev jsonp
cnpm install vue-lazyload --save-dev
main.js引入
import VueLazyload from 'vue-lazyload';
Vue.use(VueLazyload, {
preLoad: 1,
error: require('assets/img/error.png'),
loading: require('assets/img/loading.gif'),
attempt: 1
});
//home下index.vue 注意:对组件封装 并且数据加载完成后注意刷新
<template>
<div class="home">
<header class="g-header-container">
<home-header></home-header>
</header>
<me-scroll :data="recommends">
<home-slider></home-slider>
<home-nav></home-nav>
<home-recommend @loaded="getRecommends"></home-recommend>
</me-scroll>
<div class="g-backtop-container"></div>
<router-view></router-view>
</div>
</template>
<script>
import MeScroll from 'base/scroll';
import HomeHeader from './header';
import HomeSlider from './slider';
import HomeNav from './nav';
import HomeRecommend from './recommend'
export default {
name: 'Home',
components: {
MeScroll,
HomeHeader,
HomeSlider,
HomeNav,
HomeRecommend
},
data(){
return {
recommends: []
}
},
methods: {
updateScroll() {
},
getRecommends(recommends) {
this.recommends = recommends
}
}
}
</script>
<style lang="scss" scoped>
@import '~assets/scss/mixins';
.home{
overflow: hidden;
width: 100%;
height: 100%;
background-color: $bgc-theme;
}
</style>
//home下recommend 封装热卖商品组件
<template>
<div class="recommend">
<h3 class="recommend-title">热卖推荐</h3>
<div class="loading-container" v-if="!recommends.length">
<me-loading inline/>
</div>
<ul class="recommend-list" v-else>
<li
class="recommend-item"
v-for="(item, index) in recommends"
:key="index"
>
<router-link
class="recommend-link"
:to="{name: 'home-product', params: {id: item.baseinfo.itemId}}"
>
<p class="recommend-pic"><img class="recommend-img" v-lazy="item.baseinfo.picUrlNew"></p>
<p class="recommend-name">{{item.name.shortName}}</p>
<p class="recommend-origPrice"><del>¥{{item.price.origPrice}}</del></p>
<p class="recommend-info">
<span class="recommend-price">¥<strong class="recommend-price-num">{{item.price.actPrice}}</strong></span>
<span class="recommend-count">{{item.remind.soldCount}}件已售</span>
</p>
</router-link>
</li>
</ul>
</div>
</template>
<script>
import {getHomeRecommend} from 'api/home'
import MeLoading from 'base/loading';
export default {
name: 'HomeRecommend',
components: {
MeLoading
},
data(){
return {
recommends: [],
curPage: 1,
totalPage: 1
}
},
created() {
this.getRecommend()
},
methods: {
getRecommend(){
if(this.curPage > this.totalPage){
return;
}
getHomeRecommend(this.curPage).then(data => {
if(data){
this.curPage++;
this.totalPage = data.totalPage
this.recommends = this.recommends.concat(data.itemList);
this.$emit('loaded', this.recommends);
}
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~assets/scss/mixins";
.recommend {
&-title {
position: relative;
width: 100%;
padding: 10px 0;
font-size: $font-size-l;
text-align: center;
&:before,
&:after {
content: '';
position: absolute;
top: 50%;
width: 40%;
height: 1px;
background-color: #ddd;
}
&:before {
left: 0;
}
&:after {
right: 0;
}
}
&-list {
@include flex-between();
flex-wrap: wrap;
}
&-item {
width: 49%;
background-color: #fff;
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.12);
margin-bottom: 8px;
}
&-link {
display: block;
}
&-pic {
position: relative;
width: 100%;
padding-top: 100%;
margin-bottom: 5px;
}
&-img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
&-name {
height: 36px;
padding: 0 5px;
margin-bottom: 8px;
line-height: 1.5;
@include multiline-ellipsis();
}
&-origPrice {
padding: 0 5px;
margin-bottom: 8px;
color: #ccc;
}
&-info {
@include flex-between();
padding: 0 5px;
margin-bottom: 8px;
}
&-price {
color: #e61414;
}
&-price-num {
font-size: 20px;
}
&-count {
color: #999;
}
}
.loading-container {
padding-top: 100px;
}
</style>
//base下 封装scroll组件
<template>
<swiper :options="swiperOption" ref="swiper">
<swiper-slide>
<slot></slot>
</swiper-slide>
<div class="swiper-scrollbar" v-if="scrollbar" slot="scrollbar"></div>
</swiper>
</template>
<script>
import {swiper, swiperSlide} from 'vue-awesome-swiper'
export default {
name: 'scroll',
components: {
swiperSlide,
swiper
},
props: {
scrollbar: {
type: Boolean,
default: true
},
data: {
type: [Array, Object]
}
},
watch: {
data() {
this.update();
}
},
methods: {
update(){
console.log(this.$refs.swiper)
this.$refs.swiper && this.$refs.swiper.swiper.update();
}
},
data() {
return {
swiperOption: {
direction: 'vertical',
slidesPerView: 'auto',
freeMode: true,
setWrapperSize: true,
scrollbar: {
el: this.scrollbar ? '.swiper-scrollbar' : null,
hide: true
}
}
}
}
}
</script>
<style lang="scss" scoped>
.swiper-container{
overflow: hidden;
width: 100%;
height: 100%;
}
.swiper-slide{
height: auto;
}
</style>
//base下loading组件
<template>
<div class="mine-loading" :class="{'mine-loading-inline' : inline}">
<span class="mine-loading-indicator" v-if="indicator === 'on'">
<slot><img src="./loading.gif" alt=""></slot>
</span>
<span class="mine-loading-text" v-if="text">{{text}}</span>
</div>
</template>
<script>
export default {
name: 'MeLoading',
props: {
indicator:{
type: String,
default: 'on',
validator(value){
return ['on', 'off'].indexOf(value) > -1;
}
},
text: {
type: String,
default: '加载中...',
},
inline: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss" scoped>
@import "~assets/scss/mixins";
.mine-loading{
overflow: hidden;
width: 100%;
height: 100%;
@include flex-center(column);
&.mine-loading-inline {
flex-direction: row;
.mine-loading-indicator ~ .mine-loading-text{
margin-top: 0px;
margin-left: 6px;
}
}
}
.mine-loading-indicator ~ .mine-loading-text{
margin-top: 6px;
}
</style>
jsonp请求
//api下封装 home.js 请求jsonp
import axios from 'axios';
import jsonp from 'assets/js/jsonp';
import {SUCC_CODE, TIMEOUT, HOME_RECOMMEND_PAGE_SIZE, jsonpOptions} from './config';
// 获取幻灯片数据 -- ajax
export const getHomeSlider = () => {
return axios.get('http://www.imooc.com/api/home/slider', {
//这里是为了延迟10s 让网络超时 执行catch
// timeout: TIMEOUT
}).then(res => {
// console.log(res)
if(res.data.code === SUCC_CODE){
return res.data.slider
}
throw new Error('没有成功获取到数据!')
}).catch(err => {
if (err){
console.log(err);
}
return [
{
linkUrl: 'https://www.imooc.com',
picUrl: require('assets/img/404.png')
}
]
}).then(data => {
// 这里是延迟1s返回 为了加 正在加载效果
return new Promise(resolve => {
setTimeout(() => {
resolve(data);
}, 1000);
});
})
};
// 获取热门推荐数据--jsonp
export const getHomeRecommend = (page = 1, psize = HOME_RECOMMEND_PAGE_SIZE) => {
const url = 'https://ju.taobao.com/json/tg/ajaxGetItemsV2.json';
const params = {
page,
psize,
type: 0,
frontCatId: ''
};
return jsonp(url, params, jsonpOptions).then(res => {
console.log('jsonp', res);
if (res.code === '200') {
return res;
}
throw new Error('没有成功获取到数据!');
}).catch(err => {
if (err) {
console.log(err);
}
}).then(res => {
return new Promise(resolve => {
setTimeout(() => {
resolve(res);
}, 1000);
});
});
};
//home下 config.js
export const SUCC_CODE = 0;
export const TIMEOUT = 10000;
export const HOME_RECOMMEND_PAGE_SIZE = 20;
export const jsonpOptions = {
param: 'callback',
timeout: TIMEOUT
};
封装基本jsonp
//assets 下面 js jsonp.js 封装基本jsonp
import jsonp from 'jsonp';
const parseParam = param => {
let params = [];
for (const key in param) {
params.push([key, encodeURIComponent(param[key])]);
}
// [[page, 1], [pszie, 20]]
return params.map(value => value.join('=')).join('&');
// [[page, 1], [pszie, 20]]
// [page=1, psize=20]
// page=1&psize=20
};
export default (url, data, options) => {
url += (url.indexOf('?') < 0 ? '?' : '&') + parseParam(data)
// jsonp(url, opti)
return new Promise((resolve, reject) => {
jsonp(url, options, (err, data) => {
if(err) {
reject(err)
} else {
resolve(data)
}
});
})
}
下拉刷新、上拉加载
下拉提示文字变化、松手刷新、刷新幻灯片。上拉加载更多。
注意:下拉刷新轮播图后需要更新轮播图,但是loop没有改变,拉到最后一页有bug。解决方法:查找重新加载api,但是官方未提供。那么我们采用给组件变换KEY来达到重新加载,这个是VUE特性。
资源地址
更多推荐
所有评论(0)