Fork me on GitHub
余鸢

微信公众号开发(五):页面开发

定义测试接口

通过RAP对主要的页面定义测试接口,这里不多讲,有兴趣的同学看文档即可操作。进入RAP主页搜索团队:稻米影视,就可以看到相关的接口及参数,点击链接查看相关接口。

pug模板

使用标签的形式比较繁琐,这里我选择使用Pug,它是基于缩进嵌套的dom结构而不是以标签的形式。Pug原名不叫Pug,原来是jade,后来由于商标的原因,改为Pug,其实只是换个名字,语法都与jade一样。

安装pug

1
yarn add pug

在vue中使用pug模板,需要在template标签中设置lang="pug"即可。

首页

使用pug模板,同时填充数据。修改page/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template lang="pug">
.container
.house(ref='house')
.items(v-for='(item, index) in houses' :key='index' @click='showHouse(item)')
.desc
.words {{item.words}}
.cname {{item.name}}
.name {{item.cname}}
.character
.title 主要人物
.section
.items(v-for='(item, index) in characters' :key='index' @click='showCharacter(item)')
img(:src='item.profile')
.desc
.playedBy {{item.playedBy}}
.cname {{item.name}}
.name {{item.cname}}
.city
.title 维斯特洛
.intro 坐落于已知世界的最西端,狭长的维斯特洛大陆由北部的极地冰盖起向南延绵约3,000英里。绝境长城是一座巍峨挺立的不可逾越之物,横跨300英里,将最北的塞外地区与七大王国相互分离。一个统一的政治实体领导着南方的广阔土地,并形成九块相互联系又相互割据的区域。
.items(v-for='(item, index) in cities' :key='index')
.city-item-title {{ item.title }}
.city-item-body {{ item.body }}
</template>

首页名称

1
2
3
4
5
head() {
return {
title: '稻米影视'
}
},

创建showHouse(item)showCharacter(item)点击事件,点击后跳转页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
methods: {
showHouse(item) {
this.$router.push({
path: '/house',
query: {id: item._id}
})
},
showCharacter(item) {
this.$router.push({
path: '/character',
query: {id: item._id}
})
}
},

在页面加载之前请求到数据

1
2
3
4
5
beforeCreate() {
this.$store.dispatch('fetchHouses')
this.$store.dispatch('fetchCharacters')
this.$store.dispatch('fetchCities')
}

获取数据

state中定义数据,修改store/index.js

1
2
3
4
5
6
7
8
9
const createStore = () => {
return new Vuex.Store({
state: {
houses: [],
characters: [],
cities: []
}
})
}

添加获取数据的services,修改store/actions.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async fetchHouses({ state }) {
const res = await Services.fetchHouses()
state.houses = res.data.data
return res
},
async fetchCharacters({ state }) {
const res = await Services.fetchCharacters()
state.characters = res.data.data
return res
},
async fetchCities({ state }) {
const res = await Services.fetchCities()
state.cities = res.data.data
return res
}

复制RAP中定义的接口地址,拼接请求地址通过axios获取数据。修改store/services.js

1
2
3
4
5
6
7
8
9
10
11
const apiUrl = 'http://rapapi.org/mockjsdata/34647'
fetchHouses() {
return axios.get(`${apiUrl}/wiki/houses`)
}
fetchCharacters() {
return axios.get(`${apiUrl}/wiki/characters`)
}
fetchCities() {
return axios.get(`${apiUrl}/wiki/cities`)
}

通过辅助函数mapState获取数据。

1
2
3
4
5
6
7
computed: {
...mapState([
'houses',
'characters',
'cities'
])
},

数据获取成功,如图:

wx39

样式

这个项目使用sass,具体不多讲了,直接上过程。

安装sass

1
yarn add node-sass sass-loader sass

sass文件全都放在static/sass目录下,依次添加基础样式文件

1
2
3
4
5
base.sass
color.sass
icon.sass
minxin.sass
var.sass

编辑index.sass,具体代码不贴了,在项目看源码就成。

配置sass,修改nuxt.config.js

1
2
3
4
5
6
css: [
{
src: 'static/sass/base.sass',
lang: 'sass?indentedSyntax=true'
}
],

团队主页

pages/house/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template lang="pug">
.container
.house-media
.desc
.words {{house.words}}
.name {{house.name}}
.house-body
.title {{house.cname}}
.body {{house.intro}}
.title 主要角色
.body(v-for='(item, index) in house.swornMembers' :key="index")
.members
img(:src='item.profile')
.desc
.cname {{item.cname}}
.intro {{item.text}}
.house-history(v-for='(item, index) in house.sections' :key='index')
.title {{item.title}}
.content(v-for='text in item.content') {{text}}
</template>

主页名称

1
2
3
4
5
head() {
return {
title: '团队详情'
}
},

页面加载之前请求数据,通过 this.$route.query.id获取上个页面传过来的id值,通过id获取相应的数据。

1
2
3
4
beforeCreate() {
let id = this.$route.query.id
this.$store.dispatch('showHouse', id)
}

获取数据

同理,通过mapState获取house数据

1
2
3
4
5
computed: {
...mapState({
house: 'currentHouse'
})
},

定义点击当前的house,修改store/index.js

1
currentHouse: {}

获取数据,修改store/actions.js

1
2
3
4
5
6
async showHouse({ state }, _id) {
if (_id === state.currentHouse._id) return
const res = await Services.fetchHouse(_id)
state.currentHouse = res.data.data
return res
}

store/service.js

1
2
3
fetchHouse(id) {
return axios.get(`${apiUrl}/wiki/houses/${id}`)
}

页面效果如图:

主页跳转

团队角色主页

个人主页主要分两大部分:一是头像及个人信息,二是个人技能。

pages/character/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template lang="pug">
.container
.character-header
img.background(v-if='character.images', :src='character.images[character.images.length - 1]')
.media
img(v-if='character.profile', :src='character.profile')
.desc
.names
p.cname {{character.cname}}
p.name {{character.name}}
.character-body
.intro
p(v-for='item in character.intro') {{item}}
.stills
img(v-for='(item, index) in character.images', :src='item', :key='index')
.items(v-for='item in character.sections')
.title {{item.title}}
.body(v-for='text in item.content') {{text}}
</template>

主页名称

1
2
3
4
5
head() {
return {
title: '团队成员详情'
}
},

获取id值

1
2
3
4
beforeCreate() {
let id = this.$route.query.id
this.$store.dispatch('showCharacter', id)
}

获取数据

同理,通过mapState获取character数据

1
2
3
4
5
computed: {
...mapState({
character: 'currentCharacter'
})
},

定义点击当前的character,修改store/index.js

1
currentCharacter: {}

获取数据,修改store/actions.js

1
2
3
4
5
6
async showCharacter({ state }, _id) {
if (_id === state.currentCharacter._id) return
const res = await Services.fetchCharacter(_id)
state.currentCharacter = res.data.data
return res
}

store/service.js

1
2
3
fetchCharacter(id) {
return axios.get(`${apiUrl}/wiki/characters/${id}`)
}

页面效果如图:

个人详情页跳转

导航

导航分为三部分:首页、周边、我的账户。

components/nav.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template lang="pug">
nav#nav(v-if='navVisible')
nuxt-link(v-for='(item, index) in navList' :to='item.path' :key='index')
div(v-if='index === 0')
img(v-if='activeRoute !== item.name' src='../static/img/home.png')
img(v-else src='../static/img/home-selected.png')
div(v-else-if='index === 1')
img(v-if='activeRoute !== item.name' src='../static/img/shopping.png')
img(v-else src='../static/img/shopping-selected.png')
div(v-else)
img(v-if='activeRoute !== item.name' src='../static/img/user.png')
img(v-else src='../static/img/user-selected.png')
p {{ item.text }}
</template>

添加导航数据名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
data() {
return {
navList: [
{
'path': '/',
'name': 'index',
'text': '稻米首页'
},
{
'path': '/shopping',
'name': 'shopping',
'text': '周边手边'
},
{
'path': '/user',
'name': 'user',
'text': '个人中心'
}
]
}
},

动态设置route名称

1
2
3
4
5
6
7
8
methods: {
activeRoute() {
return this.$route.name
},
navVisible() {
return ['index', 'shopping', 'user'].indexOf(this.activeRouter) > -1
}
}

页面效果:
wx40

周边手办商城页

pages/shopping/index.vue

1
2
3
4
5
6
7
8
9
10
11
<template lang="pug">
.container
.shopping
.title 商城周边
.list
.items(v-for='(item, index) in products' :key='index' @click='showProduct(item)')
img(:src='item.images[0]')
.body
.title {{item.title}}
.content {{item.intro}}
</template>

主页名称

1
2
3
4
5
head() {
return {
title: '周边手办'
}
},

在页面加载之前请求数据

1
2
3
beforeCreate() {
this.$store.dispatch('fetchProducts')
},

获取数据

获取products数据

1
2
3
4
5
computed: {
...mapState([
'products'
])
},

定义products,修改store/index.js

1
products: []

store/actions.js

1
2
3
4
5
async fetchProducts({ state }) {
const res = await Services.fetchProducts()
state.products = res.data.data
return res
}

store/service.js

1
2
3
fetchProducts() {
return axios.get(`${apiUrl}/wiki/products`)
}

页面展示如图:

wx41

商品详情页

商品详情页会用到轮播图展示,这里使用vue-awesome-swiper插件。

安装vue-awesome-swiper

1
yarn add vue-awesome-swiper

修改nuxt.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
head: {
meta: [
{ charset: 'utf-8' },
{ hid: 'description', name: 'description', content: 'Nuxt.js project' }
]
},
css: [
{
src: 'swiper/dist/css/swiper.css'
}
],
plugins: [
{
src: '~plugins/swiper.js', ssr: false
}
]

pages/deal/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template lang="pug">
.container
.product
.swiper(v-swiper:mySwiper='swiperConfig')
.swiper-wrapper
.swiper-slide(v-for='item in product.images')
img(:src='item')
.swiper-pagination.swiper-pagination-bullets
.content
.price(v-if='product.price')
span.main-price {{product.price.toFixed(2) - product.price.toFixed(2).substr(-3)}}
span.other-price {{product.price.toFixed(2).substr(-3)}}
.name {{product.name}}
.intro {{product.intro}}
.info
cell(v-for='(item, index) in product.parameters' :key='index' :title='item.key' :content='item.value')
.attentions
.title 购物提示
ol
li(v-for='item in attentions') {{item}}
.footer
span(@click='buyProduct') 购买
</template>

添加swiper配置

1
2
3
4
5
6
7
8
9
10
11
.swiper(v-swiper:mySwiper='swiperConfig')
data() {
return {
swiperConfig: {
autoplay: 4000,
direction: 'horizontal',
loop: true,
pagination: '.swiper-pagination'
}
}

请求数据

1
2
3
4
beforeCreate() {
const id = this.$route.query.id
this.$store.dispatch('showProduct', id)
},

获取数据

1
2
3
4
5
computed: {
...mapState({
'product': 'currentProduct'
})
},

store/index.js

1
currentProduct: [],

store/actions.js

1
2
3
4
5
6
7
async showProduct({ state }, _id) {
if (_id === state.currentProduct._id) return
const res = await Services.fetchProduct(_id)
state.currentProduct = res.data.data
return res
},

store/service.js

1
2
3
fetchProduct(id) {
return axios.get(`${apiUrl}/wiki/products/${id}`)
}

个人中心

pages/user/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template lang="pug">
.container
.user(v-if='user')
.user-header
.text {{user.nickname}}
img(:src='user.avatar')
.user-address
cell(title='收获地址')
.user-content {{user.address}}
.user-phone
cell(title='电话')
.user-content {{user.phoneNumber}}
.user-name
cell(title='姓名')
.user-content {{user.name}}
.user-order(v-if='user.orders')
cell(title='我的订单')
.user-order-items(v-for='item in user.orders')
img(:src='item.image')
.user-order-intro
.title {{item.title}}
.content {{item.intro}}
.user-order-price
span ¥{{item.price}}
</template>

请求数据

1
2
3
beforeCreate() {
this.$store.dispatch('fetchUserAndOrders')
},

获取数据

1
2
3
4
5
computed: {
...mapState([
'user'
])
},

store/index.js

1
user: null

store/action.js

1
2
3
4
5
6
async fetchUserAndOrders({ state }) {
const res = await Services.fetchUserAndOrders()
state.user = res.data.data
return res
}

store/serivce.js

1
2
3
fetchUserAndOrders() {
return axios.get(`${apiUrl}/api/user`)
}

页面展示:

wx42