这是一个基于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

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

【vue】openshopping-vue的更多相关文章

  1. 【Vue】谈Vue的依赖追踪系统 ——搞懂methods watch和compute的区别和联系

    从作用机制和性质上看待methods,watch和computed的关系 图片标题[原创]:<他三个是啥子关系呢?> 首先要说,methods,watch和computed都是以函数为基础 ...

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

    1.(webpack)vue-cli构建的项目如何设置每个页面的title 2.vue项目中使用axios上传图片等文件 3.qs.stringify() 和JSON.stringify()的区别以及 ...

  3. 【转载】 github vue 高星项目

    内容 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 UI组件 element ★13489 - 饿了么出品的Vue2的web UI工具套件 Vux ★8133 - 基于Vue和 ...

  4. 【Vue】转-Vue.js经典开源项目汇总

    版权声明:本文为EnweiTech原创文章,未经博主允许不得转载. https://blog.csdn.net/English0523/article/details/88694219 Vue是什么? ...

  5. 【Vue】---编写Vue插件流程---【巷子】

    一.在Vue中编写插件流程 1.创建组件 components/message.vue <template> <div class="message" v-if= ...

  6. 【Vuejs】350- 学习 Vue 源码的必要知识储备

    前言 我最近在写 Vue 进阶的内容.在这个过程中,有些人问我看 Vue 源码需要有哪些准备吗?所以也就有了这篇计划之外的文章. 当你想学习 Vue 源码的时候,需要有扎实的 JavaScript 基 ...

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

    先了解一些单页面和多页面的区别 mm 多页应用模式MPA 单页应用模式SPA 应用构成 由多个完整页面构成 一个外壳页面和多个页面片段构成 跳转方式 页面之间的跳转是从一个页面跳转到另一个页面 页面片 ...

  8. 【转】从Vue.js源码看异步更新DOM策略及nextTick

    在使用vue.js的时候,有时候因为一些特定的业务场景,不得不去操作DOM,比如这样: <template> <div> <div ref="test" ...

  9. 【转】安装Vue.js的方法

    安装vue.js的方法   一.简介 Vue.js 是什么 Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量 ...

  10. 【整理】解决vue不相关组件之间的数据传递----vuex的学习笔记,解决报错this.$store.commit is not a function

    解决vue不相关组件之间的数据传递----vuex的学习笔记,解决报错this.$store.commit is not a function https://www.cnblogs.com/jaso ...

随机推荐

  1. UNIT对话系统(杂记)

    单轮对话指标: 召回率=机器人能回答的问题数/问题总数 准确率=机器人正确回答的问题数/问题总数 问题解决率=机器成功解决的问题数/问题总数 多轮对话指标: 任务完成率=成功结束的多轮会话数/多轮会话 ...

  2. art-template官方文档

    http://aui.github.io/art-template/zh-cn/docs/

  3. 【10.6NOIP普及模拟】MATH——枚举法

    [10.6NOIP普及模拟]MATH 题目简化 一个数列任意删k个数,是得数列中最大的差+最小的差最小 思路 程序1--时超40 暴搜+剪枝. 用类似排列组合的方式,暴搜删或不删 剪枝就是看看剩下的数 ...

  4. 并发和多线程(二)--启动和中断线程(Interrupt)的正确姿势

    启动线程: 从一个最基本的面试题开始,启动线程到底是start()还是run()? Runnable runnable = () -> System.out.println(Thread.cur ...

  5. Eclipse连接android模拟器

    1.打开eclipse 2.打开MuMu模拟器 3.去到MuMu模拟器 adb_server.exe 文件所在目录:(我的:I:\Android\mumu\emulator\nemu\vmonitor ...

  6. 18多校8th

    a-容斥原理(带限制的不定方程) #include<bits/stdc++.h> using namespace std; #define mod 998244353 #define ll ...

  7. Vue Element 使用 icon 图标 (第三方)

    Vue Element 使用 icon 图标 (第三方) element-ui 自带的图标库还是不够全, 还是需要需要引入第三方 icon, 自己在用的时候一直有些问题, 参考了些教程, 详细地记录补 ...

  8. 网络工程师课程---3、IP与路由器(ip地址的主要作用是什么)

    网络工程师课程---3.IP与路由器(ip地址的主要作用是什么) 一.总结 一句话总结: 用来标识一个节点的网络地址 划分网段 1.如何得到ip地址的网段号? ip和子网掩码  化成二进制后取 与运算 ...

  9. 服务器IP配置功能实现小结

    1. 服务器网卡配置文件 /etc/sysconfig/network/ifcfg-***(eth0) linux-f1s9:/etc/sysconfig/network # cat ifcfg-et ...

  10. 大批量数据导出excel

    有次面试时,老板问我大批量数据一次性导出会有什么问题 感谢度娘提供,感谢原博主提供 https://www.cnblogs.com/zou90512/p/3989450.html