11.构建电商类型的小程序
11.1 准备对应的小程序UI库
所谓的UI库就是第三方创建的一些自定义的组件,我们可以拿来直接使用
11.1.1 配置UI库
安装UI库
$ npm i @vant/weapp -S --production
- 一定要进入miniprogram 目录内 先执行
npm init -y
修改App.json
- 将 app.json 中的
"style": "v2"去除
修改 project.config.json
开发者工具创建的项目,miniprogramRoot 默认为 miniprogram,package.json 在其外部,npm 构建无法正常工作。
需要手动在 project.config.json 内添加如下配置,使开发者工具可以正确索引到 npm 依赖的位置。
{
...
"setting": {
...
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram/"
}
]
}
}
注意: 由于目前新版开发者工具创建的小程序目录文件结构问题,npm构建的文件目录为miniprogram_npm,并且开发工具会默认在当前目录下创建miniprogram_npm的文件名,所以新版本的miniprogramNpmDistDir配置为'./'即可
构建 npm 包
-
此时就可以删除 当前项目文件夹下的 node_modules 文件夹了
-
miniprogram_npm 文件夹就是vantweapp 为我们所提供的自定义的组件库
-
如果大家没有安装过node,也没有yarn,说明没有安装,如果不想安装,那么大家可以直接拷贝 miniprogram_npm 文件夹至你的项目即可
typescript 支持
如果你使用 typescript 开发小程序,还需要做如下操作,以获得顺畅的开发体验。
安装 miniprogram-api-typings
# 通过 npm 安装
npm i -D miniprogram-api-typings
# 通过 yarn 安装
yarn add -D miniprogram-api-typings
在 tsconfig.json 中增加如下配置,以防止 tsc 编译报错。
请将path/to/node_modules/@vant/weapp修改为项目的 node_modules 中 @vant/weapp 所在的目录。
{
...
"compilerOptions": {
...
"baseUrl": ".",
"types": ["miniprogram-api-typings"],
"paths": {
"@vant/weapp/*": ["./node_modules/@vant/weapp/dist/*"]
},
"lib": ["ES6"]
}
}
11.2 构建小程序的首页
准备接口文档:http://121.89.205.189:3000/apidoc/
11.2.1 构建轮播图
// utils/request.js
// 数据请求
// axios.get() axios.post() axios({})
// https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html
// 加载数据显示loading动画
// https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showLoading.html
const baseUrl = "http://121.89.205.189:3000/api"
const request = (config) => {
let { url, method = 'GET', header = {}, data = {} } = config
// method = method || 'GET'
wx.showLoading({
title: '加载中',
})
return new Promise((resolve, reject) => {
wx.request({
url: baseUrl + url,
method: method,
header: header,
data: data,
success: res => {
resolve(res)
},
fail: () => {
reject()
},
complete: () => {
wx.hideLoading()
}
})
})
}
export default request
// utils/request.ts
// https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html
// const request = {
// get () {},
// post () {}
// }
// request.get()
// request.post()
// 继承中间类型,data声明为any
interface AnyResult extends WechatMiniprogram.RequestSuccessCallbackResult {
data: any
}
// 从中间类型继承一个泛型接口,data声明为泛型
export interface SpecResult<T> extends AnyResult {
data: T
}
// 声明业务数据类型
export interface IMyData {
code: string
msg: string
data?: any
}
export default function request (config: WechatMiniprogram.RequestOption) {
// 显示loading动画
wx.showLoading({
title: '加载中'
})
const { url = '', data = {}, method = 'GET', header = {}} = config
// Promise<SpecResult<IMyData>> 声明resolve参数的数据类型
return new Promise<SpecResult<IMyData>>((resolve, reject) => {
wx.request({
url: 'http://121.89.205.189:3000/api' + url,
method,
data,
header,
success: (res: SpecResult<IMyData>) => {
resolve(res)
},
fail: () => {
reject()
},
complete: () => {
// 取消loading动画
wx.hideLoading()
}
})
})
}
// api/home.js
import request from '../utils/request'
// 获取轮播图的数据
export function getBannerListData () {
return request({ url: '/banner/list' })
}
// 获取产品列表分页的数据
export function getProListData (params) {
return request({
url: '/pro/list',
data: params || {}
})
}
// pages/home/home.js
import { getBannerListData } from '../../api/home'
Page({
data: {
bannerList: []
},
onLoad () {
getBannerListData().then(res => {
console.log(res)
this.setData({
bannerList: res.data.data
})
})
}
})
// pages/home/home.ts
import { getBannerList } from '../../api/home'
interface IBanner {
bannerid: string
img: string
alt: string
link: string
}
interface IData {
data: {
bannerList: IBanner[]
}
}
Page<IData, any>({
/**
* 页面的初始数据
*/
data: {
bannerList: []
},
/**
* 生命周期函数--监听页面加载
*/
onLoad() {
getBannerList().then(res => {
console.log(res)
this.setData({
bannerList: res.data.data
})
})
},
})
// home 页面下创建组件 components/banner/banner
{
"navigationBarTitleText": "喜购-首页",
"navigationBarBackgroundColor": "#f00",
"enablePullDownRefresh": true,
"usingComponents": {
"my-banner": "./components/banner/banner"
}
}
<!--pages/home/home.wxml-->
<my-banner list = "{{ bannerList }}"></my-banner>
// pages/home/components/banner/banner.js
Component({
properties: {
list: Array
}
})
<!--pages/home/components/banner/banner.wxml-->
<swiper
indicator-dots
indicator-color="#ffffff"
indicator-active-color="#ff0000"
autoplay
circular
>
<swiper-item wx:for="{{ list }}" wx:key="bannerid">
<image class="bannerImg" src="{{ item.img }}" />
</swiper-item>
</swiper>
/* pages/home/components/banner/banner.wxss */
.bannerImg {
width: 100%;
}
11.2.2 构建nav导航
// home 页面下创建组件 components/nav/Nav
// utils/nav.js
const navList = [
{ navid: 1, title: '嗨购超市', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/125678/35/5947/4868/5efbf28cEbf04a25a/e2bcc411170524f0.png' },
{ navid: 2, title: '数码电器', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/178015/31/13828/6862/60ec0c04Ee2fd63ac/ccf74d805a059a44.png' },
{ navid: 3, title: '嗨购服饰', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/41867/2/15966/7116/60ec0e0dE9f50d596/758babcb4f911bf4.png' },
{ navid: 4, title: '嗨购生鲜', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/177902/16/13776/5658/60ec0e71E801087f2/a0d5a68bf1461e6d.png' },
{ navid: 5, title: '嗨购到家', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/196472/7/12807/7127/60ec0ea3Efe11835b/37c65625d94cae75.png' },
{ navid: 6, title: '充值缴费', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/185733/21/13527/6648/60ec0f31E0fea3e0a/d86d463521140bb6.png' },
{ navid: 7, title: '9.9元拼', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/36069/14/16068/6465/60ec0f67E155f9488/595ff3e606a53f02.png' },
{ navid: 8, title: '领券', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/186080/16/13681/8175/60ec0fcdE032af6cf/c5acd2f8454c40e1.png' },
{ navid: 9, title: '领金贴', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/196711/35/12751/6996/60ec1000E21b5bab4/38077313cb9eac4b.png' },
{ navid: 10, title: 'plus会员', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/37709/6/15279/6118/60ec1046E4b5592c6/a7d6b66354efb141.png' }
]
export default navList
// pages/home/components/nav.json
{
"component": true,
"usingComponents": {
"van-grid": "@vant/weapp/grid/index",
"van-grid-item": "@vant/weapp/grid-item/index"
}
}
// pages/home/home.js
import navList from '../../utils/nav'
import { getBannerListData } from '../../api/home'
Page({
data: {
bannerList: [],
navList
},
onLoad () {
getBannerListData().then(res => {
console.log(res)
this.setData({
bannerList: res.data.data
})
})
}
})
// pages/home/home.json
{
"navigationBarTitleText": "喜购-首页",
"navigationBarBackgroundColor": "#f00",
"enablePullDownRefresh": true,
"usingComponents": {
"my-banner": "./components/banner/banner",
"my-nav": "./components/nav/nav"
}
}
<!--pages/home/home.wxml-->
<my-banner list = "{{ bannerList }}"></my-banner>
<my-nav list = "{{ navList }}"></my-nav>
// pages/home/components/nav/nav.js
Component({
properties: {
list: Array
}
})
<!--pages/home/components/nav/nav.wxml-->
<van-grid column-num="{{5}}">
<van-grid-item wx:for="{{ list }}" wx:key="navid" icon="{{ item.imgurl }}" text="{{ item.title }}" />
</van-grid>
如果项目不出效果,建议清楚缓存之后查看效果
11.2.3 秒杀列表
11.2.4 产品列表展示
home 页面下创建组件 components/proList/ProList
// pages/home/home.js
import navList from '../../utils/nav'
import { getBannerListData, getProListData } from '../../api/home'
Page({
data: {
bannerList: [],
navList,
proList: []
},
onLoad () {
getBannerListData().then(res => {
console.log(res)
this.setData({
bannerList: res.data.data
})
})
getProListData().then(res => {
console.log(res.data.data)
this.setData({
proList: res.data.data
})
})
}
})
{
"navigationBarTitleText": "喜购-首页",
"navigationBarBackgroundColor": "#f00",
"enablePullDownRefresh": true,
"usingComponents": {
"my-banner": "./components/banner/banner",
"my-nav": "./components/nav/nav",
"my-pro-list": "./components/proList/proList"
}
}
<!--pages/home/home.wxml-->
<my-banner list = "{{ bannerList }}"></my-banner>
<my-nav list = "{{ navList }}"></my-nav>
<my-pro-list list = "{{ proList }}"></my-pro-list>
// pages/home/components/proList/proList.js
Component({
properties: {
list: Array
}
})
<!--pages/home/components/proList/proList.wxml-->
<view class="proList">
<view class="proItem" wx:for="{{ list }}" wx:key="proid">
<view class="itemImage">
<image src="{{ item.img1 }}" mode=""/>
</view>
<view class="itemInfo">
<view class="title ">{{ item.proname }}</view>
<view class="price"> ¥{{ item.originprice }}</view>
</view>
</view>
</view>
/**app.wxss**/
page {
background-color: #efefef;
}
/* pages/home/components/proList/proList.wxss */
.proList {
width: 100%;
display: flex;
flex-wrap: wrap;
}
.proList .proItem {
width: 48%;
height: 260px;
background-color: #fff;
border-radius: 10px;
overflow: hidden;
margin: 5px 1%;
}
.proList .proItem .itemImage {
width: 100%;
}
.proList .proItem .itemImage image {
width: 100%;
height: 180px;
}
.proList .proItem .itemInfo {
padding: 8px 5px;
}
.proList .proItem .itemInfo .title {
font-weight: bold;
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.proList .proItem .itemInfo .price {
color: #ff6666;
}
11.2.5 首页实现上拉加载效果
小程序自带一个上拉加载的实现的事件
分析接口文档,发现列表的数据,可以分页 http://121.89.205.189:3000/apidoc/#api-Pro-GetProList
只需要不停的变换 页码即可
// pages/home/home.js
import navList from '../../utils/nav'
import { getBannerListData, getProListData } from '../../api/home'
Page({
data: {
bannerList: [],
navList,
proList: [],
count: 2
},
onLoad () {
getBannerListData().then(res => {
console.log(res)
this.setData({
bannerList: res.data.data
})
})
getProListData().then(res => {
console.log(res.data.data)
this.setData({
proList: res.data.data
})
})
},
onReachBottom () {
getProListData({ count: this.data.count }).then(res => {
if (res.data.data.length === 0) {
// 没有数据了,提示用户
// https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showToast.html
wx.showToast({
title: '没有更多数据了',
icon: 'none'
})
} else {
// 有下一页数据,实现数据的拼接、页码加1
this.setData({
proList: [...this.data.proList, ...res.data.data],
count: this.data.count + 1
})
}
})
}
})
11.2.6下拉刷新
// pages/home/home.json
{
"navigationBarTitleText": "喜购-首页",
"navigationBarBackgroundColor": "#f00",
"enablePullDownRefresh": true,
"usingComponents": {
"my-banner": "./components/banner/banner",
"my-nav": "./components/nav/nav",
"my-pro-list": "./components/proList/ProList"
}
}
// pages/home/home.js
import navList from '../../utils/nav'
import { getBannerListData, getProListData } from '../../api/home'
Page({
data: {
bannerList: [],
navList,
proList: [],
count: 2
},
onLoad () {
getBannerListData().then(res => {
console.log(res)
this.setData({
bannerList: res.data.data
})
})
getProListData().then(res => {
console.log(res.data.data)
this.setData({
proList: res.data.data
})
})
},
// 下拉刷新
// 请求第一页数据 ===》 重置数据
onPullDownRefresh () {
getProListData().then(res => {
console.log(res.data.data)
this.setData({
proList: res.data.data,
count: 2
})
// 停止下拉的动画效果
wx.stopPullDownRefresh()
})
},
// 上拉加载
onReachBottom () {
getProListData({ count: this.data.count }).then(res => {
if (res.data.data.length === 0) {
// 没有数据了,提示用户
// https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showToast.html
wx.showToast({
title: '没有更多数据了',
icon: 'none'
})
} else {
// 有下一页数据,实现数据的拼接、页码加1
this.setData({
proList: [...this.data.proList, ...res.data.data],
count: this.data.count + 1
})
}
})
}
})
11.2.7 返回顶部
// pages/home/home.json
{
"navigationBarTitleText": "喜购-首页",
"navigationBarBackgroundColor": "#f00",
"enablePullDownRefresh": true,
"usingComponents": {
"my-banner": "./components/banner/banner",
"my-nav": "./components/nav/nav",
"my-pro-list": "./components/proList/proList",
"van-icon": "@vant/weapp/icon/index"
}
}
<!--pages/home/home.wxml-->
<my-banner list = "{{ bannerList }}"></my-banner>
<my-nav list = "{{ navList }}"></my-nav>
<my-pro-list list = "{{ proList }}"></my-pro-list>
<view class="backTop" bind:tap="backTop" wx:if="{{ scrollTop > 300 }}">
<van-icon name="arrow-up" />
</view>
// pages/home/home.js
import navList from '../../utils/nav'
import { getBannerListData, getProListData } from '../../api/home'
Page({
data: {
bannerList: [],
navList,
proList: [],
count: 2,
scrollTop: 0
},
onLoad () {
getBannerListData().then(res => {
console.log(res)
this.setData({
bannerList: res.data.data
})
})
getProListData().then(res => {
console.log(res.data.data)
this.setData({
proList: res.data.data
})
})
},
// 下拉刷新
// 请求第一页数据 ===》 重置数据
onPullDownRefresh () {
getProListData().then(res => {
console.log(res.data.data)
this.setData({
proList: res.data.data,
count: 2
})
// 停止下拉的动画效果
wx.stopPullDownRefresh()
})
},
// 上拉加载
onReachBottom () {
getProListData({ count: this.data.count }).then(res => {
if (res.data.data.length === 0) {
// 没有数据了,提示用户
// https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showToast.html
wx.showToast({
title: '没有更多数据了',
icon: 'none'
})
} else {
// 有下一页数据,实现数据的拼接、页码加1
this.setData({
proList: [...this.data.proList, ...res.data.data],
count: this.data.count + 1
})
}
})
},
onPageScroll ({ scrollTop }) {
// console.log(scrollTop)
this.setData({
scrollTop
})
},
backTop () {
// https://developers.weixin.qq.com/miniprogram/dev/api/ui/scroll/wx.pageScrollTo.html
wx.pageScrollTo({
duration: 1000,
scrollTop: 0
})
}
})
/* pages/home/home.wxss */
.backTop {
position: fixed;
right: 10px;
bottom: 10px;
width: 32px;
height: 32px;
background-color: #ccc;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
11.2.8 自定义首页头部 - 作业
/// <reference path="./types/index.d.ts" />
interface IAppOption {
globalData: {
statusBarHeight: number,
pixelRatio: number,
userInfo?: WechatMiniprogram.UserInfo
}
userInfoReadyCallback?: WechatMiniprogram.GetUserInfoSuccessCallback,
}
// app.ts
App<IAppOption>({
globalData: {
statusBarHeight: 20,
pixelRatio: 1
},
onShow() {
console.log('小程序显示')
},
onHide() {
console.log('小程序隐藏')
},
onLaunch() {
// 获取设备信息
const res = wx.getSystemInfoSync()
console.log(res)
this.globalData.statusBarHeight = res.statusBarHeight
this.globalData.pixelRatio = res.pixelRatio
// 展示本地存储能力
const logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
// 登录
wx.login({
success: res => {
console.log(res.code)
// 发送 res.code 到后台换取 openId, sessionKey, unionId
},
})
},
})
<!--pages/home/home.wxml-->
<!-- 自定义头部 -->
<view class="slider-bg" >
</view>
<view class="box" style="height: {{statusBarHeight }}px"></view>
<view class="top" style="top: {{statusBarHeight }}px">首页</view>
<view class="bottom" style="top: {{ 44 + statusBarHeight }}px">搜索框</view>
<view style="height: {{ statusBarHeight + 88}}px;"></view>
<!-- 轮播图 -->
<my-banner list="{{ bannerList }}"></my-banner>
<!-- nav导航 -->
<my-nav list="{{ navList }}"></my-nav>
<!-- 产品列表 -->
<pro-list list="{{ proList }}"></pro-list>
<view
class="backTop"
bindtap="backTop"
wx:if="{{ scrollTop > 300 }}"
>
<van-icon name="arrow-up" size="24"/>
</view>
/* pages/home/home.wxss */
.backTop {
position: fixed;
bottom: 10px;
right: 10px;
width: 32px;
height: 32px;
border: 1rpx solid #ccc;
background-color: #fff;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.slider-bg {
background-image: -webkit-gradient(linear,left bottom,left top,from(#f1503b),color-stop(100%,#c82519));
background-image: -webkit-linear-gradient(bottom,#f1503b,#c82519 100%);
background-image: linear-gradient(0deg,#f1503b,#c82519 100%);
position: absolute;
top: 0;
left: -25%;
height: 400rpx;
width: 150%;
border-bottom-left-radius: 100%;
border-bottom-right-radius: 100%;
z-index:-1;
}
.box {
position: fixed;
z-index: 99;
top: 0;
left: 0;
width: 100%;
background-color: #c82519;
}
.top {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 88rpx;
background-color: #c82519;
z-index: 99;
}
.bottom {
position: fixed;
top: 88rpx;
left: 0;
width: 100%;
height: 88rpx;
background-color: #f1503b;
z-index: 99;
}
11.3 点击列表进入产品的详情页面并且渲染
构建详情页面 "pages/detail/detail"
11.3.1 跳转页面
声明式跳转
编程式跳转
<!--pages/home/components/proList/proList.wxml-->
<view class="proList" wx:if="{{ type === 'grid'}}">
<view class="proItem" wx:for="{{ list }}" wx:key="proid">
<!--
open-type
navigate wx.navigateTo 不能跳转tabbar页面 router.push()
redirect wx.redirectTo 不能跳转tabbar页面 router.replace()
switchTab wx.switchTab 小程序跳转tabbar的专属方法
reLaunch wx.reLaunch 关闭所有的打开的页面,打开下一个页面
navigateBack wx.navigateBack router.back() router.go(-1)
wx.navigateBack({delta: 3}) router.go(-3)
-->
<navigator url="/pages/detail/detail?proid={{item.proid}}">
<view class="itemImage">
<image src="{{ item.img1 }}" mode=""/>
</view>
<view class="itemInfo">
<view class="title ">{{ item.proname }}</view>
<view class="price"> ¥{{ item.originprice }}</view>
</view>
</navigator>
</view>
</view>
<van-card
wx:if="{{ type === 'column'}}"
wx:for="{{ list }}"
wx:key="proid"
price="{{ item.originprice }}"
title="{{ item.proname }}"
bind:tap="toDetail"
data-proid="{{item.proid}}"
thumb="{{ item.img1 }}"
/>
<!--
声明式跳转 a router-link
编程时跳转 window.location router.push
-->
// pages/home/components/proList/proList.js
Component({
properties: {
list: Array,
type: String
},
methods: {
toDetail (event) {
console.log(event)
wx.navigateTo({
url: '/pages/detail/detail?proid=' + event.target.dataset.proid
})
}
}
})
12.3.2 详情页面获取参数并且请求相关数据
当页面跳转到详情时,添加一个新的针对详情的编译模式
请求数据
// api/detail.js
import request from '../utils/request'
export function getProDetail (proid) {
return request({
url: '/pro/detail/' + proid
})
}
// pages/detail/detail.js
import { getProDetail } from '../../api/detail'
Page({
data: {
proid: '',
banners: [],
proname: [],
originprice: 0,
brand: '',
category: ''
},
onLoad(options) {
console.log(options)
getProDetail(options.proid).then(res => {
console.log(res.data.data)
const { proid, banners, proname, originprice, brand, category } = res.data.data
this.setData({
proid,
banners: banners[0].split(','),
proname,
originprice,
brand,
category
})
})
},
})
渲染数据
// pages/detail/detail.json
{
"usingComponents": {
"van-goods-action": "/miniprogram_npm/@vant/weapp/goods-action/index",
"van-goods-action-icon": "/miniprogram_npm/@vant/weapp/goods-action-icon/index",
"van-goods-action-button": "/miniprogram_npm/@vant/weapp/goods-action-button/index"
}
}
<!--pages/detail/detail.wxml-->
<view style="position: relative;">
<swiper
style="height: 300px;"
indicator-dots
current="{{ current }}"
bind:change="changeSwiper"
>
<swiper-item style="display: flex; justify-content: center;align-items: center;" wx:for="{{banners}}" wx:key="*this">
<image src="{{ item }}" mode="" bind:tap="previewImage"/>
</swiper-item>
</swiper>
<view style="position: absolute; bottom: 20px;right: 0px;width: 60px;height: 30px;background-color: #ccc; border-radius: 15px 0 0 15px;text-align: center;line-height: 30px;">
{{ current + 1 }}/ {{ banners.length}}
</view>
</view>
<van-goods-action>
<van-goods-action-icon icon="chat-o" text="客服" />
<van-goods-action-icon icon="cart-o" text="购物车" />
<van-goods-action-button
text="加入购物车"
type="warning"
/>
<van-goods-action-button text="立即购买" />
</van-goods-action>
12.3.3 详情图片预览
// pages/detail/detail.js
import { getProDetail } from '../../api/detail'
Page({
data: {
proid: '',
banners: [],
proname: [],
originprice: 0,
brand: '',
category: '',
current: 0
},
changeSwiper ({ detail: { current }}) { // event.detail.current
this.setData({current})
},
previewImage () {
wx.previewImage({
urls: this.data.banners,
current: this.data.banners[this.data.current]
})
},
onLoad(options) {
console.log(options)
getProDetail(options.proid).then(res => {
console.log(res.data.data)
const { proid, banners, proname, originprice, brand, category } = res.data.data
this.setData({
proid,
banners: banners[0].split(','),
proname,
originprice,
brand,
category
})
})
},
})
12.3.4 自定义头部
// pages/detail/detail.json
{
"usingComponents": {
"van-goods-action": "@vant/weapp/goods-action/index",
"van-goods-action-icon": "@vant/weapp/goods-action-icon/index",
"van-goods-action-button": "@vant/weapp/goods-action-button/index",
"van-icon": "@vant/weapp/icon/index"
},
"navigationStyle": "custom"
}
11.4 登录
{
"usingComponents": {
"van-field": "@vant/weapp/field/index",
"van-button": "@vant/weapp/button/index"
}
}
<!--packageUser/pages/userLogin/login.wxml-->
<van-field
value="{{ loginname }}"
placeholder="请输入用户名"
bind:input="changeLoginName"
/>
<van-field
value="{{ password }}"
placeholder="请输入密码"
bind:input="changePassword"
/>
<van-button disabled="{{ flag }}" block color="linear-gradient(to right, #4bb0ff, #6149f6)">
登录
</van-button>
// packageUser/pages/userLogin/login.js
Page({
data: {
loginname: '18813006814',
password: 'Ty2206',
flag: true
},
changeLoginName (event) {
this.setData({
loginname: event.detail
})
this.checkFlag()
},
changePassword (event) {
this.setData({
password: event.detail
})
this.checkFlag()
},
checkFlag () {
// 正则校验
if (this.data.loginname !== '' && this.data.password !== '') {
this.setData({
flag: false
})
} else {
this.setData({
flag: true
})
}
},
onLoad () {
this.checkFlag()
}
})
// api/user.js
import request from '../utils/request'
export function loginFn (parmas) {
return request({
url: '/user/login',
method: 'POST',
data: parmas
})
}
// packageUser/pages/userLogin/login.js
import { loginFn } from '../../../api/user'
Page({
data: {
loginname: '18813006814',
password: 'Ty2206',
flag: true
},
changeLoginName (event) {
this.setData({
loginname: event.detail
})
this.checkFlag()
},
changePassword (event) {
this.setData({
password: event.detail
})
this.checkFlag()
},
checkFlag () {
// 正则校验
if (this.data.loginname !== '' && this.data.password !== '') {
this.setData({
flag: false
})
} else {
this.setData({
flag: true
})
}
},
onLoad () {
this.checkFlag()
},
login () {
loginFn({
loginname: this.data.loginname,
password: this.data.password
}).then(res => {
console.log(res.data)
if (res.data.code === '10010') {
wx.showToast({
title: '用户名不存在',
icon: 'none'
})
} else if (res.data.code === '10011') {
wx.showToast({
title: '密码错误',
icon: 'none'
})
} else {
wx.showToast({
title: '登录成功',
icon: 'none'
})
// 保存登录凭证到本地
wx.setStorageSync('token', res.data.data.token)
wx.setStorageSync('userid',res.data.data.userid)
// 返回上一页
wx.navigateBack()
}
})
}
})
11.5 加入购物车
首先前端自我校验登录状态,如果已登录,调用加入购物车的接口,如果未登录,直接跳转至登录页面
<!--pages/detail/detail.wxml-->
<view style="position: relative;">
<swiper
style="height: 300px;"
indicator-dots
current="{{ current }}"
bind:change="changeSwiper"
>
<swiper-item style="display: flex; justify-content: center;align-items: center;" wx:for="{{banners}}" wx:key="*this">
<image src="{{ item }}" mode="" bind:tap="previewImage"/>
</swiper-item>
</swiper>
<view style="position: absolute; bottom: 20px;right: 0px;width: 60px;height: 30px;background-color: #ccc; border-radius: 15px 0 0 15px;text-align: center;line-height: 30px;">
{{ current + 1 }}/ {{ banners.length}}
</view>
</view>
<view>
<van-tag type="success">{{ category }}</van-tag>
<van-tag type="danger">{{ brand }}</van-tag>
</view>
<view>{{ proname }}</view>
<view>¥{{ originprice }}</view>
<!--
关于此商品的评论展示
关于此商品带来的推荐商品展示
-->
<van-goods-action>
<van-goods-action-icon icon="chat-o" text="客服" />
<van-goods-action-icon icon="cart-o" text="购物车" />
<van-goods-action-button
text="加入购物车"
type="warning"
bind:tap="addCart"
/>
<van-goods-action-button text="立即购买" />
</van-goods-action>
// api/cart.js
import request from '../utils/request'
export function addCartFn (params) {
return request({
url: '/cart/add',
method: 'POST',
data: params,
header: {
token: wx.getStorageSync('token') || ''
}
})
}
// pages/detail/detail.js
import { getProDetail } from '../../api/detail'
import { addCartFn } from '../../api/cart'
Page({
data: {
proid: '',
banners: [],
proname: [],
originprice: 0,
brand: '',
category: '',
current: 0
},
changeSwiper ({ detail: { current }}) { // event.detail.current
this.setData({current})
},
previewImage () {
wx.previewImage({
urls: this.data.banners,
current: this.data.banners[this.data.current]
})
},
onLoad(options) {
console.log(options)
getProDetail(options.proid).then(res => {
console.log(res.data.data)
const { proid, banners, proname, originprice, brand, category } = res.data.data
this.setData({
proid,
banners: banners[0].split(','),
proname,
originprice,
brand,
category
})
})
},
addCart () {
addCartFn({
userid: wx.getStorageSync('userid'),
proid: this.data.proid,
num: 1
}).then(() => {
wx.showToast({
title: '加入购物车成功',
})
})
}
})
渲染购物车页面,需要注意渲染的是哪一个用户的购物车数据 --- 登录状态
tabBar页面它会默认带有缓存效果,只有第一次打开时会销毁和创建页面,其余时刻不会销毁,所以当要保证数据的实时更新,那么就不要使用 onLoad 函数请求数据,可以使用 小程序页面的 onShow 函数请求数据,这个是实时的