【vue】openshopping-vue

这是一个基于Vue实现开箱即用H5移动端商城的单页应用
作者的开源地址是:https://github.com/yrinleung/openshopping-vue
我们一起来欣赏页面吧

看看代码有什么乾坤
看了下package.json发现有axios和vue-lazyload

先看代码一层一层进去里面

//app.vue
<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
import './assets/style/common.css';

export default {
  name: 'app'
}
</script>

看main.js中的
由上到下一步一步来

先看config中的router.js
虽说是普通的router页面,不过对路由的处理很有意思

//router.js
import Vue from 'vue';
import Router from 'vue-router';

Vue.use(Router);

const routes = [
  {
    path: '*',
    redirect: '/home'
  },
  {
    name: 'home',
    component: () => import('../page/index'),
    meta: {
      title: '首页'
    }
  },
  {
        path: '/login',
    component: () => import('../page/account/login'),
    meta: {
      title: '登录'
    }
  },
  {
        path: '/login/password',
    component: () => import('../page/account/password'),
    meta: {
      title: '登录'
    }
  },
  {
        path: '/login/phone',
    component: () => import('../page/account/phonelogin'),
    meta: {
      title: '手机号登录'
    }
  },
  {
        path: '/login/register',
    component: () => import('../page/account/register'),
    meta: {
      title: '注册'
    }
  },
  {
        path: '/user/index',
    component: () => import('../page/user/index'),
    name: 'user',
    meta: {
      title: '会员中心'
    }
  },
  {
        path: '/user/info',
    component: () => import('../page/user/info/detail'),
    name: 'user',
    meta: {
      title: '账号管理'
    }
  },
  {
    path: '/user/address',
    component: () => import('../page/user/address/list'),
    meta: {
      title: '我的地址'
    }
  },
  {
    path: '/user/address/edit',
    component: () => import('../page/user/address/edit'),
    meta: {
      title: '修改地址'
    }
  },
  {
    path: '/user/favorite',
    component: () => import('../page/user/favorite/list'),
    meta: {
      title: '我的收藏'
    }
  },
  {
    path: '/user/coupon',
    component: () => import('../page/user/coupon/list'),
    meta: {
      title: '我的优惠券'
    }
  },
  {
    path: '/user/order',
    component: () => import('../page/user/order/list'),
    meta: {
      title: '我的订单'
    }
  },
  {
    path: '/user/order/:id',
    component: () => import('../page/user/order/list'),
    meta: {
      title: '我的订单'
    }
  },
  {
    path: '/user/order/info/:id',
    component: () => import('../page/user/order/info'),
    meta: {
      title: '我的订单'
    }
  },
  {
    path: '/user/order/logistics/:id',
    component: () => import('../page/user/order/logistics'),
    meta: {
      title: '订单追踪'
    }
  },
  {
    path: '/user/aftersale',
    component: () => import('../page/user/aftersale/list'),
    meta: {
      title: '售后'
    }
  },
  {
    path: '/user/aftersale/apply',
    component: () => import('../page/user/aftersale/apply'),
    meta: {
      title: '申请售后'
    }
  },
  {
    path: '/user/aftersale/detail',
    component: () => import('../page/user/aftersale/detail'),
    meta: {
      title: '服务单详情'
    }
  },
  {
    path: '/user/aftersale/track/:id',
    component: () => import('../page/user/aftersale/track'),
    meta: {
      title: '进度详情'
    }
  },
  {
    path: '/product/:id',
    component: () => import('../page/product/detail'),
    meta: {
      title: '商品详情'
    }
  },
  {
    path: '/search',
    component: () => import('../page/product/list'),
    meta: {
      title: '商品列表'
    }
  },
  {
    name: 'cart',
    component: () => import('../page/cart/index'),
    meta: {
      title: '购物车'
    }
  },
  {
    path: '/order',
    component: () => import('../page/shipping/order'),
    meta: {
      title: '确认订单'
    }
  },
  {
    name: 'category',
    component: () => import('../page/category/index'),
    meta: {
      title: '分类'
    }
  },
];

// add route path
routes.forEach(route => {
  route.path = route.path || '/' + (route.name || '');
});

const router = new Router({ routes });
// 这里对title的处理也挺有意思的
router.beforeEach((to, from, next) => {
  const title = to.meta && to.meta.title;
  if (title) {
    document.title = title;
  }
  next();
});

export {
  router
};

我们再看src\config\components.js的components.js
代码如下



import headerNav from '../components/header/nav';

import navigate from '../components/footer/navigate.vue'
import productcard from '../components/common/productcard.vue'
import {
  Tag,
  Col,
  Icon,
  Cell,
  CellGroup,
  Swipe,
  Toast,
  SwipeItem,
  GoodsAction,
  GoodsActionBigBtn,
  GoodsActionMiniBtn,
  Actionsheet,
  Sku,
  Card,Button,SwipeCell,Dialog,Tab, Tabs,Row,Checkbox, CheckboxGroup, SubmitBar,NavBar,Tabbar, TabbarItem,Panel,List,Step, Steps,Field ,
  Badge, BadgeGroup,Popup,Stepper,RadioGroup, Radio,Picker,Uploader,Info
} from 'vant';

const components=[
    Tag,
    Col,
    Icon,
    Cell,
    CellGroup,
    Swipe,
    SwipeItem,
    GoodsAction,
    GoodsActionBigBtn,
    GoodsActionMiniBtn,
    Actionsheet,
    Sku,
    Card,
    Button,
    SwipeCell ,
    Dialog ,
    headerNav,
    Tab, Tabs,Toast,Row,Checkbox, CheckboxGroup, SubmitBar,NavBar ,Tabbar, TabbarItem,navigate,Panel,List ,Step, Steps,Field ,
    Badge, BadgeGroup,Popup,productcard,Stepper,RadioGroup, Radio,Picker,Uploader,Info
]

export default (Vue)=>{
    components.forEach(Component => {
        Vue.component(Component.name, Component)
    });
}

上面的对组件的处理挺有意思的
看一下移动端这边的适配

//rem.js
//src\config\rem.js
(function(d, w) {
    const doc = d.documentElement;
    function rem() {
      const width = Math.min(doc.getBoundingClientRect().width, 768);
      doc.style.fontSize = width / 7.5 + 'px';
    }
    rem();
    w.addEventListener('resize', rem);
  })(document, window);
  

接下来我们来根据router.js,结合路由来看我们的页面内容

//src\page\index.vue
<template>
    <div>
        <page/>
        <navigate />
    </div>
</template>

<script>
import navigate from '../components/footer/navigate.vue'
import page from './page/page.vue'

export default {
    components:{
        page,
        navigate
    }
}
</script>
<style>

</style>

引入了page.vue和navigate.vue

//navigate.vue
<template>
    <div style="height:50px;">
    <van-tabbar >
        <van-tabbar-item icon="wap-home" to="/home">首页</van-tabbar-item>
        <van-tabbar-item icon="wap-nav" to="/category" >分类</van-tabbar-item>
        <van-tabbar-item icon="cart" to="/cart" >购物车</van-tabbar-item>
        <van-tabbar-item icon="contact" to="/user/index">我的</van-tabbar-item>
    </van-tabbar>
    </div>
</template>

<script>
import { Tabbar, TabbarItem } from "vant";
export default {
    name:'navigate',
    components:{
        [Tabbar.name]: Tabbar,
        [TabbarItem.name]: TabbarItem,
    }
}
</script>
//page.vue
<template>
<div :style="'background-color:'+((page.BackgroundColor==undefined||page.BackgroundColor=='')?'#fff':page.BackgroundColor)">
    <div :style="'height:'+topheight+'px'" ></div>
    <div v-for="(item,index) in page.Sections" :key="index">
        <imageAd v-if="item.Code=='ImageAd'" :data="item.ParameterDictionary"></imageAd>

        <imageText v-if="item.Code=='ImageText'" :data="item.ParameterDictionary"></imageText>

        <pageLine v-if="item.Code=='Line'"  :data="item.ParameterDictionary" ></pageLine>

        <whitespace v-if="item.Code=='Line'" :data="item.ParameterDictionary"  />

        <pageText v-if="item.Code=='Text'" :data="item.ParameterDictionary" ></pageText>

        <notice v-if="item.Code=='Notice'" :data="item.ParameterDictionary" ></notice>

        <search v-if="item.Code=='Search'" :data="item.ParameterDictionary" v-on:settopheight="settopheight($event)" ></search>

        <pageTitle v-if="item.Code=='Title'" :data="item.ParameterDictionary" ></pageTitle>

        <cube v-if="item.Code=='Cube'" :data="item.ParameterDictionary" ></cube>

        <product v-if="item.Code=='Product'" :data="item" ></product>
    </div>

</div>
</template>
<script>
import "../../assets/style/index.css";
import whitespace from "../../components/page/whitespace.vue";
import pageLine from "../../components/page/line.vue";
import pageText from "../../components/page/text.vue";
import notice from "../../components/page/notice.vue";
import search from "../../components/page/search.vue";
import pageTitle from "../../components/page/title.vue";
import cube from "../../components/page/cube.vue";
import imageAd from "../../components/page/imageAd.vue";
import imageText from "../../components/page/imageText.vue";
import product from "../../components/page/product.vue";
import { GetPage } from "../../api/page.js";

export default {
    name:"page",
    components:{
        whitespace,
        pageLine,
        pageText,
        notice,
        search,
        pageTitle,
        cube,
        [imageAd.name]:imageAd,
        imageText,
        product
    },
    data:function(){
        return{
            topheight:0,
            page:{},
        }
    },
    created:function(){
        GetPage().then(response=>{
            this.page=response;
        });
    },
    methods:{
        settopheight:function(value){
            this.topheight=value;
        }
    }
}
</script>

上面的page页面实际上是由很多组件构成的,我们可以一个一个去分析组件

//src\components\page\whitespace.vue
<template>
    <div :style="style"></div>
</template>

<script>
export default {
    name: 'whitespace',
    props: {
        data: Object,
    },
    computed: {
        style() {
            return {
                height: (this.data.height==undefined?'30':this.data.height)+"px",
            };
        }
    }
};
</script>
//src\components\page\line.vue
<template>
    <div :style="divstyle" ><div :style="linestyle"></div></div>
</template>

<script>
export default {
    name: 'page-line',
    props: {
        data: Object,
    },
    computed:{
        divstyle(){
            return{
                height:"30px",
                position:'relative',
                margin:'0px '+this.data.margintype+'px'
            };
        },
        linestyle(){
            return{
                position:'absolute',
                top:'14px',
                width:'100%',
                borderTop:'1px '+(this.data.type==undefined?'solid':this.data.type)+" "+(this.data.color==undefined?'#000':this.data.color),
            };
        }
    }
};
</script>
//src\components\page\text.vue
<template>
    <div :style="style">{{data.value}}</div>
</template>

<script>
export default {
    name:'page-text',
    props:{
        data:Object
    },
    computed:{
        style(){
            return{
                position:'relative',
                padding: '10px',
                fontSize:(this.data.fontsize==undefined?'10':this.data.fontsize)+'px',
                color:(this.data.color==undefined?'#000':this.data.color),
                background:this.data.backgroundcolor,
                textAlign:this.data.textalign,
            }
        }
    }
}
</script>
//src\components\page\notice.vue
<template>
    <NoticeBar
    :text="data.value"
    :background="data.background"
    :color="data.color"
    />
</template>

<script>
import { NoticeBar } from "vant";

export default {
    name:'notice',
    components:{
        NoticeBar
    },
    props:{
        data:Object
    },
    computed:{

    }
}
</script>

<style>

</style>
//src\components\page\cube.vue
<template>
<div class="cap-cube" style="font-size:100%;width:100%;">
    <div v-for="(item,index) in data.images" :key="index" class="cap-cube__item" :style="'width:'+item.imgwidth+'rem;'+item.style">
        <a :href="item.link">
            <img :src="item.src+'?w=375'" :style="'width:'+item.imgwidth+'rem;'" />
        </a>
    </div>
    <div style="clear:both;"></div>
</div>
</template>

<script>
export default {
  name: "cube",
  props: {
    data: Object
  },
  created: function() {
    var gap = this.data.imagegap;
    var margin = gap / 2;
    var width = 375;
    var max = 0;
    switch (this.data.type) {
      case "1":
        max = 2;
        width = (width - margin * 2) / 2;
        break;
      case "2":
        max = 3;
        width = (width - margin * 4) / 3;
        break;
      case "3":
        max = 4;
        width = (width - margin * 6) / 4;
        break;
      case "4":
        max = 4;
        width = (width - margin * 2) / 2;
        break;
      case "5":
        max = 3;
        width = (width - margin * 2) / 2;
        break;
      case "6":
        max = 3;
        width = (width - margin * 2) / 2;
        break;
      case "7":
        max = 4;
        width = (width - margin * 2) / 2;
        break;
    }
    margin = margin / 50;
    width = width / 50;
    var imagelist = [];

    for (var i = 0; i < max; i++) {
      var imgwidth = width;
      var item = this.data.imagelist[i];
      var style = "";
      switch (this.data.type) {
        case "1":
          {
            if (i == 0) {
              style = "margin-right:" + margin + "rem;";
            } else {
              style = "margin-left:" + margin + "rem;";
            }
          }
          break;
        case "2":
          {
            if (i == 0) {
              style = "margin-right:" + margin + "rem;";
            } else if (i == 1) {
              style = "margin:0 " + margin + "rem;";
            } else {
              style = "margin-left:" + margin + "rem;";
            }
          }
          break;
        case "3":
          {
            if (i == 0) {
              style = "margin-right:" + margin + "rem;";
            } else if (i == 1 || i == 2) {
              style = "margin:0 " + margin + "rem;";
            } else {
              style = "margin-left:" + margin + "rem;";
            }
          }
          break;
        case "4":
          {
            if (i == 0) {
              style =
                "margin-right:" +
                margin +
                "rem;margin-bottom:" +
                margin +
                "rem;";
            } else if (i == 1) {
              style =
                "margin-left:" +
                margin +
                "rem;margin-bottom:" +
                margin +
                "rem;";
            } else if (i == 2) {
              style =
                "margin-right:" + margin + "rem;margin-top:" + margin + "rem;";
            } else {
              style =
                "margin-left:" + margin + "rem;margin-top:" + margin + "rem;";
            }
          }
          break;
        case "5":
          {
            if (i == 0) {
              style = "margin-right:" + margin + "rem;";
            } else if (i == 1) {
              style =
                "margin-left:" +
                margin +
                "rem;margin-bottom:" +
                margin +
                "rem;";
            } else {
              style =
                "margin-left:" + margin + "rem;margin-top:" + margin + "rem;";
            }
          }
          break;
        case "6":
          {
            if (i == 0) {
              imgwidth = width * 2;
              style = "margin-bottom:" + margin + "rem;";
            } else if (i == 1) {
              style =
                "margin-right:" + margin + "rem;margin-top:" + margin + "rem;";
            } else {
              style =
                "margin-left:" + margin + "rem;margin-top:" + margin + "rem;";
            }
          }
          break;
        case "7":
          {
            if (i == 0) {
              style = "margin-right:" + margin + "rem;";
            } else if (i == 1) {
              style =
                "margin-right:" + margin + "rem;margin-top:" + margin + "rem;";
            } else if (i == 2) {
              imgwidth = width / 2 - margin;
              style =
                "margin-left:" +
                margin +
                "rem;margin-top:" +
                margin +
                "rem;margin-right:" +
                margin +
                "rem";
            } else {
              imgwidth = width / 2 - margin;
              style =
                "margin-left:" + margin + "rem;margin-top:" + margin + "rem;";
            }
          }
          break;
      }
      item.style = style;
      item.imgwidth = imgwidth;
      imagelist.push(item);
    }
    this.data.images = imagelist;
  },
  computed: {}
};
</script>
//src\components\page\imageAd.vue
<template>
<div>
    <van-swipe :autoplay="3000" v-if="data.type=='1'" :style="'height:'+height+'px'"  >
        <van-swipe-item v-for="(image,index) in data.imagelist" :key="index" >
            <a :href="image.url">
                <img  v-lazy="image.src+''" style="width: 100%;" />
            </a>
        </van-swipe-item>
    </van-swipe>
     <ul  v-if="data.type=='2'">
        <li v-for="(image,index) in data.imagelist" :key="index" class="cap-image-ad__content" :style="'margin:'+data.imagegap+'px 0px;'">
            <div class="image-wrapper">
                <a :href="image.url">
                    <img alt="" class="cap-image-ad__image" v-lazy="image.src+''" />
                </a>
            </div>
        </li>
    </ul>

    <div v-if="data.type=='3'||data.type=='4'||data.type=='5'" class='cap-image-ad__image-nav' style='overflow-x:scroll;' >
        <div v-for="(item,index) in data.imagelist" :key="index" class="image-wrapper" :style="'width:'+(data.type=='3'?'80':(data.type=='4'?'40':'20'))+'%;margin-right:'+data.imagegap+'px;'">
            <a :href="item.link" class="cap-image-ad__link cap-image-ad__link--image-nav" >
                <div class="cap-image-ad__image">
                    <img :src="item.src+'?w=640'" style="width: 100%; " />
                </div>
            </a>
        </div>
    </div>
</div>
</template>

<script>

export default {
    name:'imageAd',
    components:{
    },
    props:{
        data:Object
    },
    data(){
        return{
            height:0
        }
    },
    created(){
        if(this.data.imagelist.length==0||this.data.type!='1'){
            return;
        }
        var that=this;
        var image =this.data.imagelist[0];
        let img = new Image()
        img.src = image.src;
        var width= window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
        img.onload = function () {
            that.height= Math.ceil(img.height/img.width * width);
        }
    }
}
</script>

<style>

</style>
//src\components\page\imageText.vue

<template>
    <div :class='(data.type == "1" ? "cap-image-ad__image-nav" : "cap-image-ad__slide cap-image-ad__text-nav")' :style='"overflow-x:"+(data.showtype == "1" ? "hidden" : "scroll")+"; background-color:"+data.backgroundcolor' >
        <div v-if="data.type == '1'" v-for="(item,index)  in data.imagelist" :key="index" class="image-wrapper" :style="'width:'+ data.width+'%; margin-right: 0px;'">
            <a :href="item.link" class="cap-image-ad__link cap-image-ad__link--image-nav" :style="'color:'+data.color">
                <div class="cap-image-ad__image">
                    <img v-lazy="item.src+'?w=320'" style="width: 100%; " />
                </div>
                <h3 v-if="item.title!=''" class="cap-image-ad__nav-title">{{item.title}}</h3>
            </a>
        </div>
        <a v-if="data.type=='2'" v-for="(item,index) in data.imagelist" :key="index" :href="item.link" class="text-nav-wrapper" :style="'width:'+data.width+'%; color: '+data.color+'; background-color: '+data.backgroundcolor"><h3 class="cap-image-ad__nav-title">{{item.title}}</h3></a>
    </div>
</template>

<script>
export default {
    name:'imageText',
    props:{
        data:Object
    },
    created:function(){
        var width = 0;
        if (this.data.showtype == "1") {
            width = 100 / this.data.imagelist.length;
        }
        else {
            width = (100 * 0.95) / (this.data.shownumber - 1);
        }
        this.data.width=width;
    }
}
</script>

<style>

</style>
//src\components\page\product.vue
<template>
<div>
    <ul :class="'cap-goods-list__container cap-goods-list__container--'+data.classname+' cap-goods-list__container--'+data.ParameterDictionary.showtype+'   '+(data.ParameterDictionary.type=='6'?'nowrap':'')" >
        <li v-if="productlist.length==0" style="width:100%;height:150px;border:0px;">
            <div style="width:100%;height:150px;"></div>
        </li>
        <li v-for="(item,index) in productlist" :key="index" :class="'cap-goods-list__wrapper  '+(data.ParameterDictionary.type=='3'?(index%3==0?'cap-goods-list__wrapper--hybrid-big ':'cap-goods-list__wrapper--hybrid-small '):'')">
            <router-link :class="'cap-goods-list__item cap-goods-list__item--'+data.classname+' '+data.ParameterDictionary.showtype+' '+data.aclass" :to="'/product/'+item.id">
                <div class="cap-goods-list__photo">
                    <img class="cap-goods-list__img lazy lazyload" v-lazy="item.imageURL+'?w='+((data.ParameterDictionary.type=='1'||data.ParameterDictionary.type=='3')?'750':'375')" />
                </div>
                <div :class="'cap-goods-list__info has-title has-price '+(data.ParameterDictionary.showtype == 'card'?'has-btn':'')">
                    <h3 class="title">{{item.title}}</h3>
                    <p class="sale-info">
                        <span class="sale-price">¥ {{item.price}}</span>
                    </p>
                </div>
                <div v-if="data.ParameterDictionary.showtype == 'card'" class="cap-goods-list__buy-btn-wrapper cap-goods-list__buy-btn-wrapper--4">
                    <button class="cap-goods-list__buy-btn-4 van-button van-button--default van-button--small">{{data.ParameterDictionary.buttonvalue}}</button>
                </div>
            </router-link>
        </li>
    </ul>
    <div style="clear:both;"></div>
    </div>
</template>

<script>
import {getProduct} from "../../api/page.js";

export default {
    name:'product',
    data () {
       return {
           productlist: []
       }
     },
    props:{
        data:Object
    },
    created:function(){
        var id=this.data.PageSectionId;
        var data=this.data;
        var classname = "big";
        var aclass = "";
        switch (data.ParameterDictionary.type) {
            case "1":
                aclass = "cap-goods-list__item--btn1 cap-goods-list__item--ratio-3-2 cap-goods-list__item--whitespace";
                break;
            case "2":
                classname = "small";
                aclass = "cap-goods-list__item--btn1 cap-goods-list__item--padding";
                break;
            case "3":
                classname = "hybrid";
                aclass = "cap-goods-list__item--big cap-goods-list__item--hybrid-big cap-goods-list__item--btn1 cap-goods-list__item--padding";
                break;
            case "4":
                classname = "list";
                aclass = "cap-goods-list__item--btn4 cap-goods-list__item--padding";
                break;
            case "5":
                classname = "three";
                aclass = "cap-goods-list__item--btn4 cap-goods-list__item--padding";
                break;
            case "6":
                classname = "three";
                break;
        }
        data.classname=classname;
        data.aclass=aclass;
        getProduct(id).then(response => {
            this.productlist=response;
        })
    }
}
</script>

<style>

</style>
//src\api\page.js
import request from "../config/request";

export function GetPage() {
    return request({
      url: '/Page/GetPage',
      method: 'get',
    })
  }

export function getProduct(id) {
    return request({
      url: '/Page/Product',
      method: 'get',
      params: { id }
    })
  }

感觉好神奇,上面的那些就处理完毕数据了吗?
看第二个页面吧

这个页面就跟首页不一样了,所有的东西是写死的可是也暴露出来了,第一个home页面都没有看到axiox

//src\page\category\index.vue
<template>
    <div>
        <van-search
        v-model="value"
        placeholder="请输入搜索关键词"
        show-action
        @search="onSearch"
        >
        <div slot="action" @click="onSearch">搜索</div>
        </van-search>
        <van-badge-group :active-key="activeKey" class="tab" :style="'height:'+fullHeight+'px'">
            <van-badge title="热门推荐" @click="onClick" />
            <van-badge title="手机数码" @click="onClick" />
            <van-badge title="家用电器" @click="onClick" />
            <van-badge title="电脑办公" @click="onClick" />
            <van-badge title="美妆护肤" @click="onClick" />
            <van-badge title="个护清洁" @click="onClick" />
            <van-badge title="汽车用品" @click="onClick" />
            <van-badge title="男装" @click="onClick" />
            <van-badge title="男鞋" @click="onClick" />
            <van-badge title="女装" @click="onClick" />
            <van-badge title="女鞋" @click="onClick" />
            <van-badge title="母婴童装" @click="onClick" />
            <van-badge title="图书音像" @click="onClick" />
            <van-badge title="运动户外" @click="onClick" />
            <van-badge title="食品生鲜" @click="onClick" />
        </van-badge-group>
        <div class="content" :style="'width:'+fullWidth+'px;height:'+(fullHeight-7)+'px'" >
            <img src="https://img11.360buyimg.com/mcoss/jfs/t1/1072/23/3672/95463/5b9a0813E175891fa/e38fc2f7c2ddfec2.jpg" />
            <div class="category-div">
                <h4>常用分类</h4>
                <ul >
                    <li>
                      <router-link to="/search?keyword=xxxx">
                        <img src="//img12.360buyimg.com/focus/jfs/t11824/150/2263801190/3392/8e69e1b3/5a167b8cNdcf71ae5.jpg">
                        <span>蓝牙耳机</span>
                      </router-link>
                    </li>
                    <li>
                        <a >
                            <img src="//img20.360buyimg.com/focus/jfs/t13759/194/897734755/2493/1305d4c4/5a1692ebN8ae73077.jpg">
                            <span>iPhone</span>
                        </a>
                    </li>
                    <div style="clear:both"></div>
                </ul>
            </div>
            <div class="category-div">
                <h4>热门分类</h4>
                <ul>
                    <li><a ><img src="//img11.360buyimg.com/focus/s140x140_jfs/t21388/146/237407622/26923/221da1b3/5b054fedN2ba90518.jpg"><span>手机</span></a></li>
                    <li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t20128/208/216721929/9242/472993da/5b05522dNa2aae1bb.png"><span>耳机</span></a></li>
                    <li><a ><img src="//img30.360buyimg.com/focus/s140x140_jfs/t21655/83/2186874549/15932/c273d29b/5b48802aN13fe73de.png"><span>剃须刀</span></a></li>
                    <li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t21715/149/246679831/16257/ddbf2036/5b0565a7N8dbc0017.png"><span>路由器</span></a></li>
                    <li><a ><img src="//img14.360buyimg.com/focus/s140x140_jfs/t1/4478/16/633/36008/5b923503E39b9dfa9/13b099f187576d8c.png"><span>月饼</span></a></li>
                    <li><a ><img src="//img10.360buyimg.com/focus/s140x140_jfs/t1/1410/32/643/38009/5b9236b2Eb02fbf02/1e7de6987578dcdd.jpg" ><span>牛奶</span></a></li>
                    <li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t1/4674/14/665/25245/5b9236bbE088d5efb/6c7c2f9857736c65.jpg"><span>男士内裤</span></a></li>
                    <li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t1/1710/26/666/26147/5b9236c3E5fd1cd42/86c6bca8f4fe1efa.png"><span>小米8</span></a></li>
                    <li><a ><img src="//img11.360buyimg.com/focus/s140x140_jfs/t1/3653/6/655/42593/5b9236caEfef6235b/9e118f12705f52bb.png"><span>大闸蟹</span></a></li>
                    <li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t23881/349/2204372862/9923/4c62864a/5b7693eeNf6883734.png"><span>三只松鼠</span></a></li>
                    <li><a ><img src="//img20.360buyimg.com/focus/s140x140_jfs/t24253/294/2182777138/4059/429945c9/5b76990bNde226fbc.png"><span>充电宝</span></a></li>
                    <li><a ><img src="//img30.360buyimg.com/focus/s140x140_jfs/t22051/318/235303191/9297/c5ea2761/5b055000N410a7553.png"><span>空调</span></a></li>
                    <li><a ><img src="//img10.360buyimg.com/focus/s140x140_jfs/t19960/243/653029866/38879/91bb398b/5b055555N9245f8aa.jpg"><span>电饭煲</span></a></li>
                    <li><a ><img src="//img12.360buyimg.com/focus/s140x140_jfs/t1/345/33/944/5582/5b9236d2E62d8da2e/99f72d51b8f195ed.jpg"><span>电话手表</span></a></li>
                    <li><a ><img src="//img30.360buyimg.com/focus/s140x140_jfs/t1/1446/14/631/8500/5b9237e5E0d1f9e16/b1a627b92323b5ed.png"><span>华为</span></a></li>
                    <div style="clear:both">
                    </div>
                </ul>
            </div>
        </div>
        <navigate />
    </div>
</template>

<script>
import { Search } from "vant";

export default {
  name: "userindex",
  components: {
    [Search.name]: Search
  },
  data() {
    return {
      value: "",
      activeKey: 0,
      fullHeight: document.documentElement.clientHeight - 93,
      fullWidth: document.documentElement.clientWidth - 99
    };
  },
  methods: {
    onSearch() {
      console.log(this.value);
    },
    onClick(key) {
      this.activeKey = key;
    }
  }
};
</script>

<style lang="less">
.tab {
  float: left;
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
  min-height: 100%;
  .van-badge {
    padding: 15px 12px 15px 9px;
  }
  .van-badge:not(:last-child)::after {
    height: 199%;
  }
}
.content {
  float: left;
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
  min-height: 100%;
  margin: 7px 7px 0;
  font-size: 12px;
  img {
    width: 100%;
  }
  .category-div {
    margin: 19px 0px 0;
    h4 {
      font-size: 14px;
      color: #333;
    }
    ul{
        margin-top: 10px;
    }
    li {
      width: 32.8%;
      float: left;
      text-align: center;
      img {
        width: 60px;
        height: 60px;
      }
      span{
          font-size: 12px;
    height: 36px;
    color: #686868;
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    display: box;
    display: -webkit-box;
    display: -moz-box;
    display: -ms-box;
    display: -o-box;
    line-clamp: 2;
    -webkit-line-clamp: 2;
    -moz-line-clamp: 2;
    -ms-line-clamp: 2;
    -o-line-clamp: 2;
    box-orient: vertical;
    -webkit-box-orient: vertical;
    -ms-box-orient: vertical;
    -o-box-orient: vertical;
    word-break: break-all;
    box-align: center;
    -webkit-box-align: center;
    -moz-box-align: center;
    -ms-box-align: center;
    -o-box-align: center;
    box-pack: center;
    -webkit-box-pack: center;
    -moz-box-pack: center;
    -ms-box-pack: center;
    -o-box-pack: center;
    z-index: 2;
    position: relative;
      }
    }
  }
}
</style>

这个里面就是很普通的布局方式,静态文件,如果要点击进去下一个路由,应该在点击的时候,进行一次数据的请求
看一下对cart的处理

cart页面也是前端写死数据进行渲染的,我们来看看代码

//src\page\cart\index.vue
<template>
  <div class="card">
    <headerNav title="购物车"/>
       <van-cell  value="编辑商品" class="head">
        <template slot="title">
          <van-checkbox v-model="checkedAll" >全选</van-checkbox>
        </template>
      </van-cell>

    <van-checkbox-group class="card-goods" v-model="checkedGoods">

      <div class="promotion-group">
        <div  v-for="(item,index) in goods"
          :key="index" class="card-goods__item">
          <van-checkbox :name="item.id"></van-checkbox>

          <product-card :product='item' :iscard='true' >
            <template slot>
              <van-cell value="修改" >
                  <template slot="title">
                      <van-tag type="danger">促销</van-tag>
                      <span class="van-cell-text" >满60元减5元</span>
                  </template>
              </van-cell>
            </template>
          </product-card>
        </div>
        </div>

      <div class="promotion-group">

       <van-cell  is-link class="head">
        <template slot="title">
          <van-checkbox v-model="checkedAll" >京东自营</van-checkbox>
        </template>
      </van-cell>

      <div  v-for="(item,index) in goods"
        :key="index+10" class="card-goods__item">
        <van-checkbox :name="item.id"></van-checkbox>

        <product-card :product='item' :iscard='true' >
          <template slot>
            <van-cell value="修改" >
                <template slot="title">
                    <van-tag type="danger">促销</van-tag>
                    <span class="van-cell-text" >满60元减5元</span>
                </template>
            </van-cell>
          </template>
        </product-card>
      </div>
      <van-cell value="去凑单" is-link class="promotion">
        <template slot="title">
          <p><van-tag type="danger">满减</van-tag>购满60元,可减5元</p>
        </template>
      </van-cell>
      <div  v-for="(item,index) in goods"
        :key="index+20" class="card-goods__item">
        <van-checkbox :name="item.id"></van-checkbox>

        <product-card :product='item' :iscard='true' >
          <template slot>
            <van-cell value="修改" >
                <template slot="title">
                    <van-tag type="danger">促销</van-tag>
                    <span class="van-cell-text" >满60元减5元</span>
                </template>
            </van-cell>
          </template>
        </product-card>
      </div>
        </div>
    </van-checkbox-group>

    <div style="height:50px;"></div>
    <van-submit-bar
      :price="totalPrice"
      :disabled="!checkedGoods.length"
      :button-text="submitBarText"
      @submit="onSubmit"
    >
    <template slot>
      <van-checkbox v-model="checkedAll" >全选</van-checkbox>
    </template>
    </van-submit-bar>
  </div>
</template>

<script>

export default {
  components: {
  },
  data() {
    return {
      checkedAll:true,
      checkedGoods: ['1', '2', '3'],
      goods: [{
        id: '1',
        title: '星巴克(Starbucks)星冰乐 轻盈香草味 咖啡饮料 281ml*6瓶礼盒装低脂减糖',
        desc: '3.18kg/件',
        price: '200.00',
        quantity: 1,
        imageURL: 'https://img.yzcdn.cn/public_files/2017/10/24/2f9a36046449dafb8608e99990b3c205.jpeg',
        imageTag:'比加入时降5元',
      }, {
        id: '2',
        title: '陕西蜜梨',
        desc: '约600g',
        price: '690.00',
        quantity: 1,
        imageURL: 'https://img.yzcdn.cn/public_files/2017/10/24/f6aabd6ac5521195e01e8e89ee9fc63f.jpeg',
          gift: [
            {
              title: "星巴克(Starbucks)星冰乐小熊吊饰星巴克(Starbucks)星冰乐小熊吊饰",
              quantity: 2
            },
            {
              title: "星巴克(Starbucks)星冰乐小熊吊饰星巴克(Starbucks)星冰乐小熊吊饰",
              quantity: 1
            }
          ]
      }, {
        id: '3',
        title: '美国伽力果',
        desc: '约680g/3个',
        price: '2680.00',
        quantity: 1,
        imageURL: 'https://img.yzcdn.cn/public_files/2017/10/24/320454216bbe9e25c7651e1fa51b31fd.jpeg'
      }]
    };
  },
  computed: {
    submitBarText() {
      const count = this.checkedGoods.length;
      return '结算' + (count ? `(${count})` : '');
    },
    totalPrice() {
      return this.goods.reduce((total, item) => total + (this.checkedGoods.indexOf(item.id) !== -1 ? parseFloat(item.price): 0), 0);
    },
  },
  methods: {
    onSubmit() {

      this.$router.push('/order')
    }
  }
};
</script>

<style lang="less">
.card-goods {
  font-size: 12px;
  &__item {
    position: relative;
    .van-checkbox{
      width: 20px;
      height: 20px;
      top: 40px;
      left: 5px;
      z-index: 1;
      position: absolute;
    }
    .additional{
      width: 100%;
        padding-left: 15px;
    box-sizing: border-box;
    }
  }
}
.head{
      padding-left: 5px;
  border-bottom: 1px solid #eee;
}
.card{
  background: #f7f7f7;
  .van-submit-bar__bar {
      border-top: 1px solid #f7f7f7;
      .van-checkbox{
        padding-left: 5px;
      }
  }
  .promotion{
      .van-tag {
          line-height: 12px;
          margin-right: 5px;
      }
      .van-cell__title{

      flex: 4;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      }
    }
    .promotion-group{
      margin-top: 10px;
      box-shadow: 5px 5px 5px #e5e5e5;
    }
}

</style>

接下来看order页面

<template>
  <div class="order">
     <headerNav title="确认订单"/>
     <van-cell
      center
      :border="false"
      class="contact-card"
      is-link
      to="/user/address?id=2"
    >
      <template v-if="type === 'add'">
        <strong>选择地址</strong>
      </template>
      <template v-else>
        <strong>张三 138****6520</strong>
        <div>广东省深圳市南山区科技园 </div>
      </template>
    </van-cell>
    <div style="height:15px;"></div>
    <div class="card" v-for="(product,i) in products" :key="i">
      <product-card :product='product' />
    </div>
    <div style="height:15px;"></div>
    <van-cell-group>
      <van-field
        label="留言"
        type="textarea"
        placeholder="请输入留言"
        rows="1"
        autosize
      />
    </van-cell-group>
    <div style="height:15px;"></div>
    <van-cell-group class="total">
        <van-cell title="优惠券" is-link value="抵扣¥5.00" />
    </van-cell-group>

    <div style="height:15px;"></div>
    <van-cell-group class="total">
        <van-cell title="商品总额" value="9.99" />
        <van-cell title="运费" value="+ 0.00" />
        <van-cell title="折扣" value="- 5.00" />
        <van-cell title="实付金额" value="4.99" style="font-weight: 700;" />
    </van-cell-group>

    <div style="height:50px;"></div>
    <van-submit-bar
      :price="3050"
      button-text="提交订单"
      label='实付金额:'
      @submit="onSubmit"
    />

  </div>
</template>

<script>
export default {
  data() {
    return {
      type: "add1",
      products: [
        {
          imageURL:
            "https://img10.360buyimg.com/mobilecms/s88x88_jfs/t17572/12/840082281/351445/e1828c58/5aab8dbbNedb77d88.jpg",
          title: "良品铺子 肉肉聚汇猪肉脯 猪蹄卤 辣味小吃520g",
          desc: "0.670kg/件,肉肉聚汇520g",
          price: "59.80",
          quantity: 2
        },
        {
          imageURL:
            "https://img10.360buyimg.com/mobilecms/s88x88_jfs/t22720/128/1410375403/319576/8dbd859f/5b5e69b3Nf4f0e9e7.jpg",
          title: "元朗 鸡蛋卷 饼干糕点 中秋礼盒 广东特产680g",
          desc: "1.320kg/件",
          price: "65.80",
          quantity: 1,
          gift: [
            {
              title: "星巴克(Starbucks)星冰乐小熊吊饰星巴克(Starbucks)星冰乐小熊吊饰",
              quantity: 2
            },
            {
              title: "星巴克(Starbucks)星冰乐小熊吊饰星巴克(Starbucks)星冰乐小熊吊饰",
              quantity: 1
            }
          ]
        },
        {
          imageURL:
            "https://img10.360buyimg.com/mobilecms/s88x88_jfs/t17572/12/840082281/351445/e1828c58/5aab8dbbNedb77d88.jpg",
          title: "良品铺子 肉肉聚汇猪肉脯 猪蹄卤 辣味小吃520g",
          desc: "0.670kg/件,肉肉聚汇520g",
          price: "59.80",
          quantity: 2
        },
      ]
    };
  },
  methods: {
    onSubmit() {
      this.$toast("点击按钮");
    },
  },
  activated(){
    //根据key名获取传递回来的参数,data就是map
    this.$on('selectAddress', function(data){
        //赋值给首页的附近医院数据模型
        console.log(1);
    }.bind(this));
},
};
</script>

<style lang="less">
.order {
  font-size: 14px;
  background: #f7f7f7;
  .contact-card::before {
    content: "";
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    position: absolute;
    background: -webkit-repeating-linear-gradient(
      135deg,
      #ff6c6c 0,
      #ff6c6c 20%,
      transparent 0,
      transparent 25%,
      #3283fa 0,
      #3283fa 45%,
      transparent 0,
      transparent 50%
    );
    background: repeating-linear-gradient(
      -45deg,
      #ff6c6c 0,
      #ff6c6c 20%,
      transparent 0,
      transparent 25%,
      #3283fa 0,
      #3283fa 45%,
      transparent 0,
      transparent 50%
    );
    background-size: 80px;
  }
  .total {
    .van-cell__value {
      color: red;
    }
  }

  .van-submit-bar__bar {
    border-top: 1px solid #f7f7f7;
  }
  .additional {
    .van-cell {
      padding: 0 15px;
      font-size: 12px;
    }
    .van-cell__title {
      flex: 11;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .van-tag {
      line-height: 12px;
      margin-right: 5px;
    }

    .price {
      color: #e93b3d;
      font-size: 10px;
      span {
        font-size: 16px;
      }
    }
  }
}
</style>

//src\page\user\address\list.vue
<template>
<div>

    <headerNav title="我的地址"/>
    <van-address-list
  v-model="chosenAddressId"
  :class="isSelect?'':'hideselect'"
  :list="list"
  @add="onAdd"
  @edit="onEdit"
  @select="onSelect"
/>
</div>
</template>

<script>

import { GetAddressList } from "../../../api/user.js";
import { AddressList } from 'vant';
export default {
    components:{
        [AddressList.name]:AddressList,
    },
    data() {
        return {
        chosenAddressId: '1',
        isSelect:false,
        list: [],
        }
    },

    methods: {
        onAdd() {
            this.$router.push('/user/address/edit')
        },

        onEdit(item, index) {
            this.$router.push('/user/address/edit?id='+item.id);
        },
        onSelect(item,index){
            if(!this.isSelect){
                return;
            }
            this.$emit('selectAddress',item);
            this.$router.go(-1);
        }
    },
    created:function(){
        this.chosenAddressId=this.$route.query.id;
        this.isSelect=this.$route.query.id>0;
        GetAddressList().then(response=>{
            this.list=response;
        })
    }

}
</script>

<style lang="less">
.hideselect{
    .van-radio__input{
        display: none;
    }
}
</style>

//src\page\user\address\edit.vue
<template>
    <div>
     <headerNav title="修改地址"/>
      <van-address-edit
      :area-list="areaList"
      :showDelete="showDelete"
      show-set-default
      @save="onSave"
      @delete="onDelete"
      :addressInfo="info"
      />
    </div>
</template>

<script>
import areaList from '../../../data/area';
import { GetAddressById,SaveAddress,DelAddress } from "../../../api/user.js";

import { AddressEdit } from 'vant';
export default {
    components:{
        [AddressEdit.name]:AddressEdit,
    },
    data() {
    return {
      areaList,
      showDelete:false,
      info:{},
    }
  },

  methods: {
    onSave(data) {
      SaveAddress(data).then(response=>{
        this.$toast('保存成功');
        this.$router.go(-1);
      })
    },
    onDelete(data) {
      DelAddress(data).then(response=>{
        this.$toast('删除成功');
        this.$router.go(-1);
      })
    },
  },
  created:function(){
    var id=this.$route.query.id;
    if(id>0){
      this.showDelete=true;
      GetAddressById(id).then(response=>{
        console.log(response);
        this.info=response;
      })
    }
  }

}
</script>

<style>
.van-picker__toolbar{
  font-size: 16px;
}
</style>

//src\page\user\index.vue
//点击不同的路由跳转到不同的页面
<template>
  <div>
    <div class="user-profile">
      <div class="user-profile-avatar">
        <a href="/#/user/info">
          <img :src="data.Avatar" alt="用户头像">
        </a>
      </div>
      <div class="user-profile-username">
        <a href="/#/user/info">
          <span class="m-nick">{{data.UserName}}</span>
        </a>
      </div>
    </div>

    <van-cell-group class="user-group">
      <van-cell title="我的订单" value="查看全部订单" is-link  to="/user/order"/>
      <van-row class="user-links">
        <router-link  to="/user/order/1">
          <van-col span="6">
            <van-icon name="pending-payment">
              <van-info :info="data.UnPayTotal"  />
            </van-icon>
            <div>待付款</div>
          </van-col>
        </router-link>
        <router-link  to="/user/order/2">
          <van-col span="6">
            <van-icon name="logistics">
              <van-info :info="data.UnRecieveTotal"   />
            </van-icon>
            <div>待发货</div>
          </van-col>
        </router-link>
        <router-link  to="/user/order/2">
          <van-col span="6">
            <van-icon name="point-gift">
            </van-icon>
            <div>已完成</div>
          </van-col>
        </router-link>
        <router-link  to="/user/aftersale">
          <van-col span="6">
            <van-icon name="after-sale" >
              <van-info :info="data.AfterSaleTotal"   />
            </van-icon>
            <div>售后</div>
          </van-col>
        </router-link>
      </van-row>
    </van-cell-group>

    <van-cell-group class="user-group">
      <van-cell title="我的服务" />
      <van-row class="user-links">
        <router-link  to="/user/coupon">
          <van-col span="6">
            <van-icon name="coupon" />
            <div>我的优惠券</div>
          </van-col>
        </router-link>
        <router-link  to="/user/favorite">
          <van-col span="6">
            <van-icon name="like-o" />
            <div>我的收藏</div>
          </van-col>
        </router-link>
        <router-link  to="/user/address">
          <van-col span="6" >
            <van-icon name="location"/>
            <div>收货地址</div>
          </van-col>
        </router-link>
      </van-row>
    </van-cell-group>

    <van-cell-group>
      <van-cell title="切换账号" is-link to="/login" />
    </van-cell-group>
    <navigate />
  </div>
</template>

<script>
import { GetUserIndex } from "../../api/user.js";

export default {
  data(){
    return{
      data:{}
    }
  },
  components: {
  },
  created:function(){
      GetUserIndex().then(response=>{
          this.data=response;
      });
  },
};
</script>

<style lang="less">
.user {
  &-profile {
        text-align: center;
        display: block;
    width: 100%;
    height: 141px;
    background-color: #f1f5fa;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    img{
          width: 100%;
    }
    &-avatar{
          padding-top: 23px;
    padding-bottom: 5px;

      img{

      width: 65px;
      height: 65px;
      border-radius: 50%;
      overflow: hidden;
      }
    }
    &-username{
      font-size: 20px;
    }
  }
  &-group {
    margin-bottom: .3rem;

    .van-cell__value{
      color: #999;
      font-size: 12px;
    }
  }
  &-links {
    padding: 15px 0;
    font-size: 12px;
    text-align: center;
    background-color: #fff;
    .van-icon {
      position: relative;
      width: 24px;
      font-size: 24px;
    }
  }
}
</style>

//login.vue
<template>
    <div>
        <div class="m-logo" style="background:url(//haitao.nos.netease.com/f866dd18-12f0-4bb2-be6d-5cac85cf2627.png) center 73px no-repeat;background-size:161px;    text-align: center;
    padding: 130px 0 0;
    margin: 15px 0;"></div>
    <div style="margin:0 10px;font-size: 14px;">
        <router-link to="/login/phone">
            <van-button size="large" style="font-size: 14px;height: 42px;line-height: 42px;margin-bottom: 15px;">手机号登录</van-button>
        </router-link>
        <router-link to="/login/password">
            <van-button size="large" style="font-size: 14px;height: 42px;line-height: 42px;margin-bottom: 15px;">密码登录</van-button>
        </router-link>
        <router-link to="/login/register">
            <van-button size="large" type="primary" style="font-size: 14px;height: 42px;line-height: 42px;">手机号一键注册</van-button>
        </router-link>
    </div>
    <div class="m-thirdpart">
        <p class="tit"><span class="txt">或用以下方式登录</span></p>
        <div class="lnk">
            <van-icon name="wechat" color="#1AAD16" />
            <van-icon name="alipay" color="#7dcbeb"/>
        </div>
    </div>
   </div>
</template>

<script>
export default {

}
</script>

<style lang="less">
.m-thirdpart{
    padding: 40px 20px 50px;
    font-size: 12px;
    .tit{
            position: relative;
    width: 215px;
    margin: 0 auto;
    border-bottom: 1px solid #eee;
    }
    .txt{
        position: absolute;
    left: 50%;
    top: -12px;
    margin: 0 0 0 -57px;
    width: 114px;
    background: #fff;
    text-align: center;
    color: #999;
    font-size: 12px;
    line-height: 26px;
    }
    .lnk{
        position: relative;
    padding: 27px 0 0;
    width: 280px;
    margin: 0 auto;
    text-align: center;
    .van-icon{
            font-size: 31px;
    margin: 0 29px;
    }
    }
}
</style>

上面的内容也只是一个简单的布局,点击跳转到新的页面

<template>
    <div>
      <headerNav title="注册"/>
      <div style="background:url(https://haitao.nos.netease.com/f866dd18-12f0-4bb2-be6d-5cac85cf2627.png) center 18px no-repeat;background-size:161px;">
          <div style="padding-top: 70px;">
            <van-cell-group>
                <van-field
                    placeholder="请输入手机号"
                />
                 <van-field
                    center
                    clearable
                    placeholder="请输入短信验证码"
                >
                    <van-button slot="button" size="small" type="primary">发送验证码</van-button>
                </van-field>
                <van-field
                    type="password"
                    placeholder="请输入密码"
                />
            </van-cell-group>
            <div style="margin: 10px;">
                <van-button size="large" type="primary" style="height: 45px;line-height:45px;">注册</van-button>
            </div>
          </div>
      </div>
    </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

这里面也是静态页面

<template>
    <div>
      <headerNav title="登录"/>
      <div style="background:url(https://haitao.nos.netease.com/f866dd18-12f0-4bb2-be6d-5cac85cf2627.png) center 18px no-repeat;background-size:161px;">
          <div style="padding-top: 70px;">
            <van-cell-group>
                <van-field
                    placeholder="请输入手机号"
                />
                <van-field
                    type="password"
                    placeholder="请输入密码"
                />
            </van-cell-group>
            <div style="margin: 10px;">
                <van-button size="large" type="primary" style="height: 45px;line-height:45px;">登录</van-button>
            </div>
          </div>
      </div>
    </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

我觉得这个里面的很大的看点来自作者对组件的封装吧

<template>
  <div class="goods">
     <headerNav title="商品详情"/>
    <van-swipe class="goods-swipe" :autoplay="3000">
      <van-swipe-item v-for="thumb in goods.thumb" :key="thumb">
        <img :src="thumb" >
      </van-swipe-item>
    </van-swipe>

    <van-cell-group>
      <van-cell>
        <span class="goods-price">{{ formatPrice(goods.price) }}</span>
        <span class="goods-market-price">{{ formatPrice(goods.market_price) }}</span>
        <div class="goods-title">{{ goods.title }}</div>
        <div class="goods-subtit">{{goods.subtitle}}</div>
      </van-cell>

      <van-cell   @click="onClickShowTag" class="goods-tag" >
        <template slot="title" style="font-size:10px;">
            <img src="https://haitao.nos.netease.com/ba8a4c2fdaa54f82a45261293c116af61419663676663i46n3jlh10028.png"/>
            <span >挪威品牌</span>
            <img src="https://haitao.nosdn2.127.net/13bd59e6e29a4f06b278c586629e690d.png" />
            <span >跨境商品</span>
            <van-icon name="passed" color="red" />
            <span >次日达</span>
            <van-icon name="passed" color="red" />
            <span >自提</span>
            <van-icon name="passed" color="red" />
            <span >闪电退款</span>
            <van-icon name="passed" color="red" />
            <span >前海保税仓</span>
            <van-icon name="passed" color="red" />
            <span >七天无理由退货(拆封后不支持)</span>
        </template>
      </van-cell>
    </van-cell-group>

    <van-cell-group class="goods-cell-group">
      <van-cell is-link  @click="showPromotion" >
        <template slot="title">
            <span style="margin-right: 10px;">领券</span>
            <van-tag type="danger" mark  style="margin-right: 5px;">满180减30</van-tag>
            <van-tag type="danger" mark  style="margin-right: 5px;">满300减100</van-tag>
        </template>
      </van-cell>

      <van-cell  is-link @click="showPromotion" >
        <template slot="title">
            <span style="margin-right: 10px;">促销</span>
            <van-tag type="danger" style="margin-right: 5px;">多买优惠</van-tag>
            <van-tag type="danger" style="margin-right: 5px;">满减</van-tag>
            <van-tag type="danger" style="margin-right: 5px;">限购</van-tag>
        </template>
      </van-cell>
    </van-cell-group>

    <van-cell-group class="goods-cell-group">
      <van-cell  is-link @click="showSku" >
        <template slot="title">
            <span style="margin-right: 10px;">已选</span>
            <span >10件装</span>
        </template>
      </van-cell>

    </van-cell-group>

    <div class="goods-info">
        <p class="goods-info-title" >图文详情</p>
        <div v-html="goods.info"></div>
    </div>
    <van-goods-action>

      <van-goods-action-mini-btn icon="like-o" @click="sorry">
        收藏
      </van-goods-action-mini-btn>
      <van-goods-action-mini-btn icon="cart" @click="onClickCart">
        购物车
      </van-goods-action-mini-btn>
      <van-goods-action-big-btn @click="showSku">
        加入购物车
      </van-goods-action-big-btn>
      <van-goods-action-big-btn primary @click="showSku">
        立即购买
      </van-goods-action-big-btn>
    </van-goods-action>
    <van-actionsheet v-model="show" title="促销" style="font-size:14px;">

            <van-cell  is-link @click="sorry" >
                <template slot="title">
                    <van-tag type="danger">多买优惠</van-tag>
                    <span> 满2件,总价打9折</span>
                </template>
            </van-cell>
            <van-cell  is-link @click="sorry" >
                <template slot="title">
                    <van-tag type="danger">满减</van-tag>
                    <span> 满100元减50元</span>
                </template>
            </van-cell>
            <van-cell  is-link @click="sorry" >
                <template slot="title">
                    <van-tag type="danger">限购</van-tag>
                    <span> 购买不超过5件时享受单件价¥8.00,超出数量以结算价为准</span>
                </template>
            </van-cell>
    </van-actionsheet>

    <van-actionsheet v-model="showTag" title="服务说明" style="font-size:14px;">

            <van-cell>
                <template slot="title">
                    <van-icon name="passed" color="red" style="margin-right: 10px;" />
                    <span >次日达</span>
                    <div style="margin-left: 24px;font-size:10px;color:#7d7d7d;">指定时间前下单,次日送达</div>
                </template>
            </van-cell>
            <van-cell>
                <template slot="title">
                    <van-icon name="passed" color="red" style="margin-right: 10px;" />
                    <span >自提</span>
                    <div style="margin-left: 24px;font-size:10px;color:#7d7d7d;">我们提供多种自提服务,包括自提点、自助提货柜、移动自提车等服务</div>
                </template>
            </van-cell>
            <van-cell>
                <template slot="title">
                    <van-icon name="passed" color="red" style="margin-right: 10px;" />
                    <span >闪电退款</span>
                    <div style="margin-left: 24px;font-size:10px;color:#7d7d7d;">签收7天内退货的,提供先退款后退货服务。</div>
                </template>
            </van-cell>
            <van-cell>
                <template slot="title">
                    <van-icon name="passed" color="red" style="margin-right: 10px;" />
                    <span >七天无理由退货(拆封后不支持)</span>
                    <div style="margin-left: 24px;font-size:10px;color:#7d7d7d;">七天无理由退货(拆封后不支持)</div>
                </template>
            </van-cell>
            <van-cell>
                <template slot="title">
                    <van-icon name="passed" color="red" style="margin-right: 10px;" />
                    <span >前海保税仓</span>
                    <div style="margin-left: 24px;font-size:10px;color:#7d7d7d;">本商品由前海保税仓发货</div>
                </template>
            </van-cell>
    </van-actionsheet>
    <van-sku
          v-model="showBase"
          :sku="skuData.sku"
          :goods="skuData.goods_info"
          :goods-id="skuData.goods_id"
          :hide-stock="skuData.sku.hide_stock"
          :quota="skuData.quota"
          :quota-used="skuData.quota_used"
          reset-stepper-on-hide
          reset-selected-sku-on-hide
          disable-stepper-input
          :close-on-click-overlay="closeOnClickOverlay"
          :message-config="messageConfig"
          :custom-sku-validator="customSkuValidator"
          @buy-clicked="onBuyClicked"
          @add-cart="onAddCartClicked"
        />
  </div>
</template>

<script>
import skuData from '../../data/sku';

export default {
  components: {
  },
  data() {
    this.skuData = skuData;
    return {
      show:false,
      showTag:false,
      goods: {
        title: '【每日一粒益智又长高】 Lifeline Care 儿童果冻鱼油DHA维生素D3聪明长高 软糖 30粒 2件装',
        subtitle:'【品牌直采】Q弹美味,无腥味果冻鱼油,每粒含足量鱼油DHA,帮助视网膜和大脑健康发育,让你的宝宝明眼又聪明,同时补充400国际单位维生素D3,强壮骨骼和牙齿。特含DPA,让宝宝免疫力更强,没病来扰。',
        price: 2680,
        market_price:9999,
        express: '免运费',
        remain: 19,
        thumb: [
          'https://img.yzcdn.cn/public_files/2017/10/24/e5a5a02309a41f9f5def56684808d9ae.jpeg',
          'https://img.yzcdn.cn/public_files/2017/10/24/1791ba14088f9c2be8c610d0a6cc0f93.jpeg'
        ],
        info:'<p style="text-align:center;"><img src="https://haitao.nosdn2.127.net/ac19460151ee4d95a6657202bcfc653c1531470912089jjjq8ml410763.jpg" ></p><p style="text-align:center;"><img src="https://haitao.nos.netease.com/2a91cfad22404e5498d347672b1440301531470912182jjjq8mnq10764.jpg" ></p><p style="text-align:center;"><img src="https://haitao.nos.netease.com/caddd5a213de4c1cb1347c267e8275731531470912412jjjq8mu410765.jpg" ></p>',
      },
      showBase: false,
      showCustom: false,
      showStepper: false,
      closeOnClickOverlay: true,
      initialSku: {
        s1: '30349',
        s2: '1193'
      },
      customSkuValidator: (component) => {
        return '请选择xxx';
      },
      customStepperConfig: {
        quotaText: '单次限购100件',
        stockFormatter: (stock) => `剩余${stock}间`,
        handleOverLimit: (data) => {
          const { action, limitType, quota } = data;
          if (action === 'minus') {
            this.$toast('至少选择一件商品');
          } else if (action === 'plus') {
            if (limitType === LIMIT_TYPE.QUOTA_LIMIT) {
              this.$toast(`限购${quota}件`);
            } else {
              this.$toast('库存不够了~~');
            }
          }
        }
      },
      messageConfig: {
        uploadImg: (file, img) => {
          return new Promise(resolve => {
            setTimeout(() => resolve(img), 1000);
          });
        },
        uploadMaxSize: 3
      }
    };
  },
  methods: {
    formatPrice(data) {
      return '¥' + (data / 100).toFixed(2);
    },
    onClickCart() {
      this.$router.push('/cart');
    },
    sorry() {
      Toast('暂无后续逻辑~');
    },
    showPromotion() {
        this.show=true;
    },
    showSku(){
        this.showBase=true;
    },
    onClickShowTag(){
        this.showTag=true;
    },
    onBuyClicked(data) {
      this.$toast(JSON.stringify(data));
    },
    onAddCartClicked(data) {
      this.$toast(JSON.stringify(data));
    },

  }
};
</script>

<style lang="less">
.goods {
  padding-bottom: 50px;
  &-swipe {
    img {
      width: 7.5rem;
      height: 7.5rem;
      display: block;
    }
  }
  &-tag{
      font-size: 12px;
        border-top: 1px solid #e5e5e5;
        span{
      margin-right: 10px;
        }
        i{
            color: red;
            margin-right: 3px;
        }
        img{
          width: 12px;
          margin-right: 3px;
          margin-top: 6px;
      }
  }
  &-title {
        line-height: 18px;
    padding-top: 10px;
    margin-bottom: 6px;
    font-size: 14px;
    color: #333;
    font-weight: 700;
    border-top: 1px solid #f0f0f0;
  }
  &-subtit{
      font-size: 13px;
    color: #333;
    line-height: 21px;
  }
  &-price {
    color: #f44;font-size: 20px;
  }
  &-market-price {
    text-decoration: line-through;
    margin-left: 8px;
    font-size: 13px;
    color: #999;
  }
  &-cell-group {
    margin: 15px 0;
    .van-cell__value {
      color: #999;
    }
  }
  &-info-title{
      height: 44px;line-height: 44px;text-align: center;font-size: 14px;font-weight: 700;margin: 10px;border-top: 1px solid #e5e5e5;
  }
  &-info p{
          margin: 0;
    padding: 0;
    margin-block-end: 0;
    margin-block-start: 0;
    display: grid;
  }
  &-info img{
      width: 100%;
  }
}
</style>

//user/order
<template>
  <div>
    <div class="user-profile">
      <div class="user-profile-avatar">
        <a href="/#/user/info">
          <img :src="data.Avatar" alt="用户头像">
        </a>
      </div>
      <div class="user-profile-username">
        <a href="/#/user/info">
          <span class="m-nick">{{data.UserName}}</span>
        </a>
      </div>
    </div>

    <van-cell-group class="user-group">
      <van-cell title="我的订单" value="查看全部订单" is-link  to="/user/order"/>
      <van-row class="user-links">
        <router-link  to="/user/order/1">
          <van-col span="6">
            <van-icon name="pending-payment">
              <van-info :info="data.UnPayTotal"  />
            </van-icon>
            <div>待付款</div>
          </van-col>
        </router-link>
        <router-link  to="/user/order/2">
          <van-col span="6">
            <van-icon name="logistics">
              <van-info :info="data.UnRecieveTotal"   />
            </van-icon>
            <div>待发货</div>
          </van-col>
        </router-link>
        <router-link  to="/user/order/2">
          <van-col span="6">
            <van-icon name="point-gift">
            </van-icon>
            <div>已完成</div>
          </van-col>
        </router-link>
        <router-link  to="/user/aftersale">
          <van-col span="6">
            <van-icon name="after-sale" >
              <van-info :info="data.AfterSaleTotal"   />
            </van-icon>
            <div>售后</div>
          </van-col>
        </router-link>
      </van-row>
    </van-cell-group>

    <van-cell-group class="user-group">
      <van-cell title="我的服务" />
      <van-row class="user-links">
        <router-link  to="/user/coupon">
          <van-col span="6">
            <van-icon name="coupon" />
            <div>我的优惠券</div>
          </van-col>
        </router-link>
        <router-link  to="/user/favorite">
          <van-col span="6">
            <van-icon name="like-o" />
            <div>我的收藏</div>
          </van-col>
        </router-link>
        <router-link  to="/user/address">
          <van-col span="6" >
            <van-icon name="location"/>
            <div>收货地址</div>
          </van-col>
        </router-link>
      </van-row>
    </van-cell-group>

    <van-cell-group>
      <van-cell title="切换账号" is-link to="/login" />
    </van-cell-group>
    <navigate />
  </div>
</template>

<script>
import { GetUserIndex } from "../../api/user.js";

export default {
  data(){
    return{
      data:{}
    }
  },
  components: {
  },
  created:function(){
      GetUserIndex().then(response=>{
          this.data=response;
      });
  },
};
</script>

<style lang="less">
.user {
  &-profile {
        text-align: center;
        display: block;
    width: 100%;
    height: 141px;
    background-color: #f1f5fa;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    img{
          width: 100%;
    }
    &-avatar{
          padding-top: 23px;
    padding-bottom: 5px;

      img{

      width: 65px;
      height: 65px;
      border-radius: 50%;
      overflow: hidden;
      }
    }
    &-username{
      font-size: 20px;
    }
  }
  &-group {
    margin-bottom: .3rem;

    .van-cell__value{
      color: #999;
      font-size: 12px;
    }
  }
  &-links {
    padding: 15px 0;
    font-size: 12px;
    text-align: center;
    background-color: #fff;
    .van-icon {
      position: relative;
      width: 24px;
      font-size: 24px;
    }
  }
}
</style>

看了下作者封装的config里面的内容,还有两个觉得很有意思

//src\config\env.js
/**
 * 配置编译环境和线上环境之间的切换
 *
 * baseUrl: 域名地址
 * routerMode: 路由模式
 * dataSources:数据源
 */

let baseUrl = '';
let routerMode = 'hash';
let dataSources='local';//local=本地,其他值代表非本地

if (process.env.NODE_ENV == 'development') {
    baseUrl='';

}else if(process.env.NODE_ENV == 'production'){
    baseUrl = '';
}

export {
    baseUrl,
    routerMode,
    dataSources,
}
//src\config\request.js

import axios from 'axios'
import {baseUrl,dataSources} from './env';
import datas from '../data/data';

const service =axios.create({
  baseURL: baseUrl, // api 的 base_url
  timeout: 5000, // request timeout
});

const servicef =function(parameter){
  if(dataSources=='local'){
    //定义回调函数和axios一致
    const promist = new Promise(function(resolve,reject){
        var data=datas[parameter.url];
        if(typeof data=='string'){
          data= JSON.parse(data);
        }
        resolve(data);
    })
    return promist;
  }
  return service(parameter);
}

  service.interceptors.request.use(
    config => {
      // Do something before request is sent
    //   if (store.getters.token) {
    //     // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
    //     config.headers['X-Token'] = getToken()
    //   }

      return config
    },
    error => {
      // Do something with request error
      console.log(error) // for debug
      Promise.reject(error)
    }
  )

  // response interceptor
service.interceptors.response.use(
    //response => response,
    /**
     * 下面的注释为通过在response里,自定义code来标示请求状态
     * 当code返回如下情况则说明权限有问题,登出并返回到登录页
     * 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
     * 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
     */
    response => {
      const res = response.data;
      if (res.ResultCode !== 200) {
        // Message({
        //   message: res.message,
        //   type: 'error',
        //   duration: 5 * 1000
        // })
        // // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
        // if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        //   // 请自行在引入 MessageBox
        //   // import { Message, MessageBox } from 'element-ui'
        //   MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
        //     confirmButtonText: '重新登录',
        //     cancelButtonText: '取消',
        //     type: 'warning'
        //   }).then(() => {
        //     store.dispatch('FedLogOut').then(() => {
        //       location.reload() // 为了重新实例化vue-router对象 避免bug
        //     })
        //   })
        // }
        console.log(1);
        return Promise.reject('error')
      } else {
        if(typeof response.data.Tag=='string'){
          return JSON.parse(response.data.Tag);
        }else{
          return response.data.Tag;
        }
      }
    },
    error => {

      return Promise.reject(error)
    }
  )

  export default servicef

关于我上面说的数据渲染部分的疑问,我也在向作者提issue,作者大大人很好,很热心回答我
https://github.com/yrinleung/openshopping-vue/issues/1

看了这个项目倒是没有别的感受,只能说,埋着头写,写,写

原文地址:https://www.cnblogs.com/smart-girl/p/11137005.html

时间: 2024-11-08 22:29:47

【vue】openshopping-vue的相关文章

【转】[总结]vue开发常见知识点及问题资料整理(持续更新)

1.(webpack)vue-cli构建的项目如何设置每个页面的title 2.vue项目中使用axios上传图片等文件 3.qs.stringify() 和JSON.stringify()的区别以及在vux中使用post提交表单数据需要qs库序列化 4.vue中实现全局的setCookie,getCookie以及delCookie方法笔记 5.webpack中alias配置中的"@"是什么意思? 6.webpack proxyTable 代理跨域 7.如何在 vue 项目里正确地引用

【转】安装Vue.js的方法

安装vue.js的方法 一.简介 Vue.js 是什么 Vue.js(读音 /vju?/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计.Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合.另一方面,Vue 完全有能力驱动采用单文件组件和Vue生态系统支持的库开发的复杂单页应用. Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件. Vue.js是一个MVVM模式的框

【Vue-01】基础Vue语法+JS复习

Vue学习疑问及总结_SZS 1.变异方法 (重写:重新实现) Vue包含异族观察数组的变异方法,所以他们将会触发视图更新;这些方法如下: push()/pop()/shift()/unshift()/splice()/sort()/reverse() 2.splice 进行删除.增加和替换 methods:{ deleteP(index){ //删除persons中指定的index的p this.persons.splice(index,1) }, updateP(index,newP){ /

【Vue】新版vue解决跨域问题

vue.config.js module.exports = { devServer: { proxy: { "/api": { target: "http://192.168.0.103:9876", changOrigin: true, pathRewrite: {"^/api" : ""} } } } } 原文地址:https://www.cnblogs.com/jxd283465/p/11615163.html

【vue】使用vue构建多页面应用

先了解一些单页面和多页面的区别 0- 多页应用模式MPA 单页应用模式SPA 应用构成 由多个完整页面构成 一个外壳页面和多个页面片段构成 跳转方式 页面之间的跳转是从一个页面跳转到另一个页面 页面片段之间的跳转是把一个页面片段删除或隐藏,加载另一个页面片段并显示出来.这是片段之间的模拟跳转,并没有开壳页面 跳转后公共资源是否重新加载 是 否 URL模式 http://xxx/page1.html http://xxx/page1.html http://xxx/shell.html#page1

【Vue】详解Vue生命周期

Vue实例的生命周期全过程(图) (这里的红边圆角矩形内的都是对应的Vue实例的钩子函数) 在beforeCreate和created钩子函数间的生命周期 在beforeCreate和created之间,进行数据观测(data observer) ,也就是在这个时候开始监控data中的数据变化了,同时初始化事件 created钩子函数和beforeMount间的生命周期 对于created钩子函数和beforeMount间可能会让人感到有些迷惑,下面我就来解释一下: el选项的有无对生命周期过程

【原创】win10下搭建vue开发环境+IIS部署

[原创]win10下搭建vue开发环境 如果要转发,请注明原作者和原产地,谢谢! 特别说明:下面任何命令都是在windows的命令行工具下进行输入,打开命令行工具的快捷方式如下图: 详细的安装步骤如下: 一.安装node.js 说明:安装node.js的windows版本后,会自动安装好node以及包管理工具npm,我们后续的安装将依赖npm工具. node.js的官方地址为:https://nodejs.org/en/download/,如下图所示: 根据自己电脑的具体配置,选择你要下载的安装

【Vue】浅谈Vue不同场景下组件间的数据交流

浅谈Vue不同场景下组件间的数据“交流” Vue的官方文档可以说是很详细了.在我看来,它和react等其他框架文档一样,讲述的方式的更多的是“方法论”,而不是“场景论”,这也就导致了:我们在阅读完文档许多遍后,写起代码还是不免感到有许多困惑,因为我们不知道其中一些知识点的运用场景.这就是我写这篇文章的目的,探讨不同场景下组件间的数据“交流”的Vue实现 父子组件间的数据交流 父子组件间的数据交流可分为两种: 1.父组件传递数据给子组件 2.子组件传递数据给父组件 父组件传递数据给子组件——pro

【Vue】详解Vue组件系统

Vue渲染的两大基础方式 new 一个Vue的实例 这个我们一般会使用在挂载根节点这一初始化操作上: new Vue({ el: '#app' }) 注册组件并使用 通过Vue.component()去注册一个组件,你就可以全局地使用它了,具体体现在每个被new的 Vue 实例/注册组件, 的template选项属性或者对应的DOM模板中,去直接使用 注册组件 全局注册 例如,放在通过new创建的Vue实例当中: Vue.component('my-component', { template:

【Vue】Vue的依赖追踪系统 ——搞懂methods watch和compute

从作用机制和性质上看待methods,watch和computed的关系 图片标题[原创]:<他三个是啥子关系呢?> 首先要说,methods,watch和computed都是以函数为基础的,但各自却都不同 而从作用机制和性质上看,methods和watch/computed不太一样,所以我接下来的介绍主要有两个对比: 1.methods和(watch/computed)的对比 2.watch和computed的对比 作用机制上 1.watch和computed都是以Vue的依赖追踪机制为基础