购物商城项目

一、功能模块

二、涉及技术

三、项目中的vw适配

项目根目录中, 新建postcss的配置文件`postcss.config.js`

四、路由配置

路由设计:

  • 登录页

  • 首页架子

    • 首页 - 二级

    • 分类页 - 二级

    • 购物车 - 二级

    • 我的 - 二级

  • 搜索页

  • 搜索列表页

  • 商品详情页

  • 结算支付页

  • 我的订单页

4.1 配置一级路由

router/index.js 配置一级路由,新建对应的页面文件

import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/views/layout'
import Search from '@/views/search'
import SearchList from '@/views/search/list'
import ProDetail from '@/views/prodetail'
import Login from '@/views/login'
import Pay from '@/views/pay'
import MyOrder from '@/views/myorder'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    {
      path: '/login',
      component: Login
    },
    {
      path: '/',
      component: Layout
    },
    {
      path: '/search',
      component: Search
    },
    {
      path: '/searchlist',
      component: SearchList
    },
    {
      path: '/prodetail/:id',
      component: ProDetail
    },
    {
      path: '/pay',
      component: Pay
    },
    {
      path: '/myorder',
      component: MyOrder
    }
  ]
})

export default router

4.2 路由配置-tabbar标签页

vant网站地址:Vant 2 - 轻量、可靠的移动端组件库 (gitee.io)

4.2.1 vant-ui.js 引入组件

4.2.2 导航栏配置

  1. layout.vue 复制官方代码

  2. 修改显示文本及显示的图标

  3. 配置高亮颜色   

 4.2.3 router/index.js配置二级路由

4.2.3.1 新建vue页面

4.2.3.2 router/index.js配置二级路由

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '@/views/login'
import Layout from '@/views/layout'
import Search from '@/views/search'
import SearchList from '@/views/search/list'
import ProDetail from '@/views/prodetail'
import Pay from '@/views/pay'
import MyOrder from '@/views/myorder'
import Home from '@/views/layout/home'
import Category from '@/views/layout/category'
import Cart from '@/views/layout/cart'
import User from '@/views/layout/user'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    { path: '/login', component: Login },
    {
      path: '/',
      component: Layout,
      children: [
        {
          path: '/home', component: Home
        },
        {
          path: 'category',
          component: Category
        },
        {
          path: 'cart',
          component: Cart
        },
        {
          path: 'user',
          component: User
        }
      ]

    },
    { path: '/search', component: Search },
    {
      path: '/prodetail/:id',
      component: ProDetail
    },
    {
      path: '/pay',
      component: Pay
    },
    {
      path: '/myorder',
      component: MyOrder
    },
    {
      path: '/searchlist',
      component: SearchList
    }

  ]
})

export default router

4.2.3.3 layout.vue 配置路由出口, 配置 tabbar,点击导航栏的图标可以跳转页面,并且配置了二级路由出口

<template>
<div>
<!--二级路由出口,二级组件展示的位置-->
  <van-tabs route active-color="#ee0a24" inactive-color="#000" >
    <van-tabbar-item to="/home" icon="wap-home-o">首页</van-tabbar-item>
      <van-tabbar-item to="/category" icon="apps-o">分类页</van-tabbar-item>
      <van-tabbar-item to="/cart" icon="shopping-cart-o">购物车</van-tabbar-item>
      <van-tabbar-item to="/user" icon="user-o">我的</van-tabbar-item>
</van-tabs>

</div>
</template>

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

<style>

</style>

五、登录页面布局

5.1组件导入,使用的是

import { NavBar } from 'vant'
Vue.use(NavBar)

5.2 login页面样式

<template>
  <div class="login">
    <van-nav-bar title="会员登录" left-arrow @click-left="$router.go(-1)" />
    <div class="container">
      <div class="title">
        <h3>手机号登录</h3>
        <p>未注册的手机号登录后将自动注册</p>
      </div>

      <div class="form">
        <div class="form-item">
          <input class="inp" maxlength="11" placeholder="请输入手机号码" type="text">
        </div>
        <div class="form-item">
          <input class="inp" maxlength="5" placeholder="请输入图形验证码" type="text">
          <img src="@/assets/code.png" alt="">
        </div>
        <div class="form-item">
          <input class="inp" placeholder="请输入短信验证码" type="text">
          <button>获取验证码</button>
        </div>
      </div>

      <div class="login-btn">登录</div>
    </div>
  </div>
</template>

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

<style lang="less" scoped>
.container {
  padding: 49px 29px;

  .title {
    margin-bottom: 20px;
    h3 {
      font-size: 26px;
      font-weight: normal;
    }
    p {
      line-height: 40px;
      font-size: 14px;
      color: #b8b8b8;
    }
  }

  .form-item {
    border-bottom: 1px solid #f3f1f2;
    padding: 8px;
    margin-bottom: 14px;
    display: flex;
    align-items: center;
    .inp {
      display: block;
      border: none;
      outline: none;
      height: 32px;
      font-size: 14px;
      flex: 1;
    }
    img {
      width: 94px;
      height: 31px;
    }
    button {
      height: 31px;
      border: none;
      font-size: 13px;
      color: #cea26a;
      background-color: transparent;
      padding-right: 9px;
    }
  }

  .login-btn {
    width: 100%;
    height: 42px;
    margin-top: 39px;
    background: linear-gradient(90deg,#ecb53c,#ff9211);
    color: #fff;
    border-radius: 39px;
    box-shadow: 0 10px 20px 0 rgba(0,0,0,.1);
    letter-spacing: 2px;
    display: flex;
    justify-content: center;
    align-items: center;
  }
}
</style>

六、request模块 - axios封装

说明:我们会使用 axios 来请求后端接口, 一般都会对 axios 进行一些配置 (比如: 配置基础地址,请求响应拦截器等等)

一般项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个模块中, 便于使用

6.1 根据起步 | Axios中文文档 | Axios中文网 (axios-http.cn)创建实例

// 创建axios实例,将来对创建出来的实例,进行自定义配置
// 好处:不会污染原始的axios实例
const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: { 'X-Custom-Header': 'foobar' }
})

6.2 自定义配置

// 自定义配置 请求/响应 拦截器

instance.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么(默认axios会多包装一层data)
  return response.data
}, function (error) {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  return Promise.reject(error)
})

6.3 导出配置好的axios实例:instance

// 导出配置好的实例:instance
export default instance

七、图形验证码功能

7.1 准备数据,获取图形验证码后存储图片路径,存储图片唯一标识

async created () {
  this.getPicCode()
},
data () {
  return {
    picUrl: '',
    picKey: ''
  }
},
methods: {
  // 获取图形验证码
  async getPicCode () {
    const { data: { base64, key } } = await request.get('/captcha/image')
    this.picUrl = base64
    this.picKey = key
  }
}

7.2 动态渲染图形验证码,并且点击时要重新刷新验证码

<img v-if="picUrl" :src="picUrl" @click="getPicCode">

八、封装api接口 - 图片验证码接口

目的:

  • 请求与页面逻辑分离

  • 相同的请求可以直接复用请求

  • 进行了统一管理

8.1 新建 api/login.js 提供获取图形验证码 Api 函数(如果不加return,就接受不到返回回来的promise的结果

// 此处用于存放所有登录相关的接口请求
// 1.获取图像验证码
import request from '@/utils/request'
export const getPicCode = () => {
  // 如果不加return,就接受不到返回回来的promise的结果
  return request.get('/captcha/image')
}

九、toast 轻提示(两种使用方式)

记不得看官方文档Vant 2 - 轻量、可靠的移动端组件库 (gitee.io)

十、短信验证倒计时功能

10.1倒计时效果

10.1.1 准备 data 数据

定时器的ID,便于清空定时器,同时也用于判断定时器存不存在

data () {
  return {
    totalSecond: 60, // 总秒数
    second: 60, // 当前秒数,开定时器
    timer: null // 定时器 id
  }
},

10.1.2 给按钮注册点击事件

只有它们两个相等时,才显示获取验证码,否则都是几秒后发送

<button @click="getCode">
  {{ second === totalSecond ? '获取验证码' : second + `秒后重新发送`}}
</button>

10.1.3 开启倒计时

判断:只有没有定时器,并且值相等才会开启

async getCode () {
  if (!this.timer && this.second === this.totalSecond) {
    // 开启倒计时
    this.timer = setInterval(() => {
      this.second--

      if (this.second < 1) {
        clearInterval(this.timer)
        this.timer = null
        this.second = this.totalSecond
      }
    }, 1000)

    // 发送请求,获取验证码
    this.$toast('发送成功,请注意查收')
  }
}

10.1.4 离开页面销毁定时器

destroyed () {
  clearInterval(this.timer)
}

10.2 验证码请求校验处理

10.2.1 准备变量

data () {
  return {
    mobile: '', // 手机号
    picCode: '' // 图形验证码
  }
},
    

10.2.2 双向绑定,以快速获取表单数据

<input v-model="mobile" class="inp" maxlength="11" placeholder="请输入手机号码" type="text">
<input v-model="picCode" class="inp" maxlength="5" placeholder="请输入图形验证码" type="text">

10.2.3 methods中封装校验方法,返回结果为true,则通过校验,可以继续

validFn () {
  if (!/^1[3-9]\d{9}$/.test(this.mobile)) {
    this.$toast('请输入正确的手机号')
    return false
  }
  if (!/^\w{4}$/.test(this.picCode)) {
    this.$toast('请输入正确的图形验证码')
    return false
  }
  return true
},

10.2.4 请求倒计时前进行校验

// 获取短信验证码
async getCode () {
  if (!this.validFn()) {
    return
  }
  ...
}

10.3 封装接口,请求获取验证码

10.3.1 封装接口

// 获取短信验证码
export const getMsgCode = (captchaCode, captchaKey, mobile) => {
  return request.post('/captcha/sendSmsCaptcha', {
    form: {
      captchaCode,
      captchaKey,
      mobile
    }
  })
}

10.3.2 调用接口,添加提示

async getCode () {
  if (!this.validFn()) {
    return
  }

  if (!this.timer && this.second === this.totalSecond) {
    // 发送请求,获取验证码
    await getMsgCode(this.picCode, this.picKey, this.mobile)
    this.$toast('发送成功,请注意查收')
    
    // 开启倒计时
    ...
  }
}

十一、封装api接口 - 登录功能

11.1 提供登录 Api 函数

// 验证码登录
export const codeLogin = (mobile, smsCode) => {
  return request.post('/passport/login', {
    form: {
      isParty: false,
      mobile,
      partyData: {},
      smsCode
    }
  })
}

11.2 login/index.vue 登录功能

<input class="inp" v-model="msgCode" maxlength="6" placeholder="请输入短信验证码" type="text">
<div class="login-btn" @click="login">登录</div>

data () {
  return {
    msgCode: '',
  }
},
methods: {
  async login () {
    if (!this.validFn()) {
      return
    }
    if (!/^\d{6}$/.test(this.msgCode)) {
      this.$toast('请输入正确的手机验证码')
      return
    }
    await codeLogin(this.mobile, this.msgCode)
    this.$router.push('/')
    this.$toast('登录成功')
  }
}

十二、响应拦截器统一处理错误提示

响应拦截器是拿到数据的 第一个 “数据流转站”,可以在里面统一处理错误,只要不是 200 默认给提示,抛出错误。

utils/request.js里toast不能直接使用,需要导入

import { Toast } from 'vant'

...

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  const res = response.data
  if (res.status !== 200) {
    Toast(res.message)
    return Promise.reject(res.message)
  }
  // 对响应数据做点什么
  return res
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error)
})

  • 26
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值