likes
comments
collection
share

Vue智慧商城案例—登录页面

作者站长头像
站长
· 阅读数 4

智慧商城

Vue智慧商城案例—登录页面

知识点目录

Vue智慧商城案例—登录页面

实现——part1:登陆页面

Vue智慧商城案例—登录页面

按照结构创建vue-cli脚手架,创建项目

初始化设置一下文件

(有部分修改和增加)

Vue智慧商城案例—登录页面

  1. 增加两个文件夹——api 和 utils
  2. 删除之前关于vue页面样式的一些文件
  3. 修改路由配置
const router = new VueRouter({
  // 默认路由是一个空数组
  routes: []
})
  1. 修改app.vue,把之前的样式删掉,只留一个路由出口即可

认识第三方Vuet组件库vant-ui

组件库:

第三方封装好了很多很多的组件,整合到一起就是一个组件库

Vant 2 - 轻量、可靠的移动端组件库 (gitee.io)

Vue智慧商城案例—登录页面

vant全部导入和按需导入

全部:

Vue智慧商城案例—登录页面

按需:

Vue智慧商城案例—登录页面

按需导入这里需要在根目录再创建一个js文件:babel.config.js

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
}
  • 但是之后我们用到的组件增多时,全部写在main.js里就不太合适了
  • 所以我们现在在utils文件夹里写一个新的vant-ui.js文件用来存放这些组件

项目中的vw适配

目标:基于postcss插件实现项目vw适配

Vue智慧商城案例—登录页面

在根目录(不是src)创建一个新的js文件:postcss.config.js

module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      // vw适配的标准屏的宽度(iPhoneX)
      // 设计图 750 ,调成1倍 =》适配375标准屏幕
      // 设计图 640 ,调成1倍 =》适配320标准屏幕
      viewportWidth: 375
    }
  }
}

路由设计配置

Vue智慧商城案例—登录页面

  1. 先搞清楚一级路由和二级路由,并完成文件的准备

比如:

Vue智慧商城案例—登录页面
  1. 配路由规则(数组里面包对象)
//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'

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

  1. 路由设计配置
  • 目标1:阅读vant组件库文档,实现底部导航tabbar
Vue智慧商城案例—登录页面
  • 目标2:基于底部导航,完成二级路由配置
Vue智慧商城案例—登录页面
  1. 配相关的二级组件
Vue智慧商城案例—登录页面
  1. 配置二级路由:在相关的一级路由里嵌套,用children
import Home from '@/views/layout/home'
import Category from '@/views/layout/category'
import Cart from '@/views/layout/cart'
import User from '@/views/layout/user'

 {
      path: '/',
      component: Layout,
      redirect: '/home',//重定向:一进页面,默认首页路由
      children: [
        { path: '/home', component: Home },
        { path: '/category', component: Category },
        { path: '/cart', component: Cart },
        { path: '/user', component: User }

      ]
    },
  1. 配置导航链接

配合route和to路径可以实现导航栏点击高亮转换

<van-tabbar 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-tabbar>
  1. 配置路由出口,在app.vue
<router-view></router-view>

登录静态布局

Vue智慧商城案例—登录页面

1. 准备工作

  1. 以后所有的 公共通用样式 都可以写在styles文件夹里
//common.less
// 重置默认样式
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

// 文字溢出省略号
.text-ellipsis-2 {
  overflow: hidden;
  -webkit-line-clamp: 2;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-box-orient: vertical;
}

// 添加导航的通用样式
.van-nav-bar {
  .van-nav-bar__arrow {
    color: #333;
  }
}
  1. 在main.js里导入common.less
import '@/styles/common.less'
  1. 图片素材拷贝到assets目录中
Vue智慧商城案例—登录页面

2. 登录页静态布局编写

  1. 头部组件 NavBar
    • 引入
    • 页面
  2. 左箭头通用样式
  3. 静态未渲染的页面结构编写

utils/vant-ui.js

import Vue from 'vue'
import { NavBar } from 'vant'

Vue.use(NavBar)

views/login/index.js


        <!-- 头部,用到了vant插件NavBar -->
        <!-- 返回上一页 @click-left="$router.go(-1)" -->
        <van-nav-bar
           title="会员登录"
           left-arrow
           @click-left="$router.go(-1)"
           />

在这里给头部导航的返回箭头加了一个通用样式

  //添加导航的通用样式
  //不把这个样式在行内写,而写在通用样式里,是因为不仅这一个导航栏需要
  //写两层是为了增加它的权重
  .van-nav-bar{
    .van-nav-bar__arrow{
        color: #333;
    }
}

request模块-axios封装-发请求

目标:将axios请求方法,封装到request模块

  • 获取图像验证码
  • 获取短信验证码
  • 登录

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

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

创建request模块

Vue智慧商城案例—登录页面

  1. 安装axios: yarn add axios

  2. 新建request模块

  • 基于原本的axios,创建了一个新的用于请求的实例
  • 之后所有内容都是往新建的这个实例中去添加的,它并不会污染最原始的axios

utils/request.js

其中有部分修改,看注释

import axios from 'axios'
// 创建axios实例,将来对创建出来的实例进行自定义配置
const instance = axios.create({
// 基地址记得修改成你自己的
  baseURL: 'http://cba.itlike.com/public/index.php?s=/api/',
  timeout: 5000
  // headers: { 'X-Custom-Header': 'foobar' }
})

// !!!!这里需要注意的是:
// 你直接从官网上拦截器处cv过来的是配置在最原始的axios上的
// 你需要把下面的axios换成比如你在这里创建的新的instance

// 自定义配置 - 请求/响应 拦截器
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

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

// 导出配置好的实例
export default instance
  1. 创建实例&配置,导出实例

views/login/index.vue

import request from '@/utils/request'
export default {
  name: 'LoginPage',
  async created () {
  //在智慧商城接口文档处找——获取图形验证码
    const res = await request.get('/captcha/image')
    console.log(res)
  }
}

图形验证码功能完成

目标:基于请求回来的base64图片,实现图形验证码功能

Vue智慧商城案例—登录页面

Vue智慧商城案例—登录页面

export default {
  name: 'LoginPage',
  data () {
    return {
      picCode: '', // 请求用户输入的图形验证码
      picKey: '', // 将来请求传递的图形验证码的唯一标识
      picUrl: '' // 存储请求渲染的图片地址
    }
  },
  async created () {
    // 整个这个发请求的部分也可以封装成一个方法methods
    this.getPicCode()
    // const res = await request.get('/captcha/image')
    // console.log(res)
  },
  methods: {
    // 获取图形验证码
    async getPicCode () {
      // 基于返回的结果进行解析
      const { data: { base64, key } } = await request.get('/captcha/image')
      this.picUrl = base64 // 存储地址,设置给img src
      this.picKey = key // 存储唯一标识,将来验证需要携带
    }
  }
}
</script>

然后在页面:src="picUrl"渲染一下

//这里还加了一个v-if表示有图片的时候才会渲染,避免了刷新出现显示图片损坏
//把标签自带的alt=""删掉,不然点击事件不生效
<img v-if="picUrl" :src="picUrl" @click="getPicCode">

这样就可以得到图形验证码了,并且每次刷新都会更换,每次点击也会更换

api接口模块-封装图片验证码接口

目标:将请求封装成方法,统一存放到api模块,与页面分离

以前的模式: Vue智慧商城案例—登录页面

采用封装api模块的模式: Vue智慧商城案例—登录页面

就举我们刚刚写的的这个请求图片验证码的请求,把红色框部分封装到api里的login.js里(这里面可以存放所有有关login界面的请求),然后这一部分就可以替换成调用了 Vue智慧商城案例—登录页面

api/login.js

// 此处用于存放所有登录相关的接口请求
import request from '@/utils/request'

// 需要export导出(这里是按需导出,所以页面中也需要按需导入)
// 1. 获取图形验证码请求
export const getPicCode = () => {
  // 必须加return,如果不加的话,将来在login的vue里就接收不到你返回来的promise结果了
  return request.get('/captcha/image')
}

views/login/index.vue

const { data: { base64, key } } = await getPicCode()

—————————————————效果跟刚才一样^^成功

Toast轻提示

toast默认采用单例模式,一个时间只会存在一个Toast

Vue智慧商城案例—登录页面

  1. 组件库导入
import Vue from 'vue'
import { Toast } from 'vant'

Vue.use(Toast)
  1. 调用
import { Toast } from 'vant';
Toast('获取图形验证码成功')// 组件内/非组件都可以,必须写导入

this.$toast('获取图形验证码成功')//只能必须在组件内,但是可以不写上面导入那条

短信验证倒计时

Vue智慧商城案例—登录页面

  1. 点击按钮,实现倒计时效果

从变量出发,在data里写

totalSecond: 60, // 总秒数
second: 60// 当前描述,开定时器,对second--
timer:null //定时器id
 <button @click="getCode">
     {{ second === totalSecond ? '获取验证码' : second + '秒后重新发送'}}
  </button>
// 在methods中写
 getCode () {
      // 当前目前没有定时器开着,且totalSecond和second一致(也就是秒数归位)才可以倒计时
      if (!this.timer && this.second === this.totalSecond) {
      // 开启倒计时
        this.timer = setInterval(() => {
          console.log('正在倒计时...')
          this.second--
          // 避免减到负数
          if (this.second <= 1) {
            clearInterval(this.timer)
            this.timer = null// 重置定时器
            this.second = this.totalSecond// 归位
          }
        }, 1000)
      }
    },
    // 这个是为了当你离开这个页面后把定时器停掉,不然你离开页面如果它没执行完就还会倒计时继续执行
    destroyed () {
    clearInterval(this.timer)
  }
  1. 倒计时之前的校验处理(手机号、验证码)

由于校验这个功能比较通用,所以我们可以把它封装成一个方法

  • 首先需要校验,那就需要先拿到这两个数据
  • 所以我们需要在data中准备变量
  • 准备了变量就记得在页面相应处v-model
  • 还可以优化比如用个toast轻提示
mobile: '', // 手机号
picCode: '' // 请求用户输入的图形验证码
   //methods中
   // 校验 手机号 和 图形验证码 是否合法
    validFn () {
      // 以1开头,第二位3-9,再后面\d{9},即再有9个数字
      if (!/^1[3-9]\d{9}$/.test(this.mobile)) {
        this.$toast('请输入正确的手机号')
        return false
      }
      if (!/^\w{4}$/.test(this.picCode)) {
        this.$toast('请输入正确的图形验证码')
        return false
      }
      // 只有上面两个都通过了校验,才return ture
      return true
    },
  • 写完上面这两个校验,就可以跟下面的短信验证码联动了
  • 如果上面没通过,就不用执行短信验证码了
// 获取短信验证码
getCode () {
      if (!this.validFn()) {
        // 如果没通过校验,就不需要往下获取验短信证码了
        return
      }
 }
  1. 封装短信验证请求接口,发送请求添加提示

通过智慧商城的接口文档(登录-获取短信验证码),我们在自己的 api的login.js 里写上这个接口,之后页面调用一下就可以

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

调用这个接口(在定时器倒计时方法处):

Vue智慧商城案例—登录页面

 // 发送请求
const res = await getMsgCode(this.picCode, this.picKey, this.mobile)
onsole.log(res)

————————成功 \^o^/ Vue智慧商城案例—登录页面

看到 status: 200 就说明成功了!

在控制台看到成功后,就可以稍微修改一下代码,把log换成toast轻提示

 await getMsgCode(this.picCode, this.picKey, this.mobile)
 this.$toast('短信验证成功!')

登录功能

目标:封装api登录接口,实现登录功能

Vue智慧商城案例—登录页面

  1. 通过智慧商城接口文档,封装登录接口
// 3. 登录接口
export const codeLogin = (mobile, smsCode) => {
  return request.post('/passport/login', {
    form: {
      isParty: false,
      partyData: {},
      mobile,
      smsCode
    }
  })
}
  1. 登录前的校验
  2. 调用方法,发送请求,成功添加提示并跳转👇
//data提供变量
msgCode:''// 短信验证码
// 输入框绑定
<input v-model="msgCode" class="inp" placeholder="请输入短信验证码" type="text">
// 先注册触发校验方法的点击事件
<div @click="login" class="login-btn">登录</div>
import { codeLogin, getMsgCode, getPicCode } from '@/api/login'

 // 登录
    async login () {
      if (!this.validFn()) {
        return
      }
      if (!/^\d{6}$/.test(this.msgCode)) {
        this.$toast('请输入正确的短信验证码')
        return
      }
      // 如果这里的codeLogin显示没定义,就看上面import那里有没有从api那里导入这部分
      // 但其实你在拼写的时候,系统提示你然后你回车不全后,它上面会自动帮你加上
      const res = await codeLogin(this.mobile, this.msgCode)
      console.log(res)
      // 提示登录成功,并且跳转首页
      this.$toast('登录成功')
      this.$router.push('/')
    }
  },

默认密码:246810,成功\^o^/ Vue智慧商城案例—登录页面

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

目的:我们在开发的时候只需考虑正确的情况,到时候用响应拦截器统一处理错误即可 Vue智慧商城案例—登录页面

首先我们先打印一下这个图片验证的请求,就可以看到status:500,告诉你验证不正确,但是只是能打印出来,它并不能阻止什么

const res = await getMsgCode(this.picCode, this.picKey, this.mobile)
this.$toast('图片码验证成功')
console.log(res)

Vue智慧商城案例—登录页面

  • 我们预期的是:希望响应的status如果不是200,最好就不要往下走了,抛出promise错误
  • 意思就是 使await只会等待成功的promise

效果比如如图^^

Vue智慧商城案例—登录页面

import { Toast } from 'vant'
import axios from 'axios'
// 创建axios实例,将来对创建出来的实例进行自定义配置
const instance = axios.create({
// 基地址记得修改成你自己的
  baseURL: 'http://cba.itlike.com/public/index.php?s=/api/',
  timeout: 5000
  // headers: { 'X-Custom-Header': 'foobar' }
})

// !!!!这里需要注意的是:
// 你直接从官网上拦截器处cv过来的是配置在最原始的axios上的
// 你需要把下面的axios换成比如你在这里创建的新的instance

// 自定义配置 - 请求/响应 拦截器
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么
  // return response
  //  (默认axios会多包装一层data,所以我们这里打点一下data)

  // ——————————————————————————————————这是我们的相应拦截器
  const res = response.data
  if (res.status !== 200) {
    // 给提示
    Toast(res.message)// 这里就不写死了,用后台返回回来的提示
    // 抛出一个错误的promise
    return Promise.reject(res.message)
  }
  return res
  // ——————————————————————————————————————————
}, function (error) {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  return Promise.reject(error)
})

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

登录权证信息存储

Vue智慧商城案例—登录页面

  1. 构建user模块,并挂载

Vue智慧商城案例—登录页面 user.js

export default {
  namespaced: true,
  // 状态
  state () {
    return {
      // 个人权证相关
      userInfo: {
        token: '',
        userId: ''
      }
    }
  },
  mutations: {
    // 提供一个方法,去设置上面的state里的变量
    setUserInfo (state, obj) {
      state.userInfo = obj
    }
  },
  actions: {

  },
  getters: {

  }
}

index.js补充

import user from './modules/user'

  modules: {
    user
  }

在methods的login登录方法中加上commit调用

this.$store.commit('user/setUserInfo',res.data)

storage存储模块-vuex持续化处理

在utils文件下建立

目标:封装storage存储模块,利用本地存储,进行vuex持续化处理

问题来源:vuex刷新会丢失,怎么办

Vue智慧商城案例—登录页面

utils/storage.js

// 约定一个通用的键名
const INFO_KEY = 'hm_shopping_info'

// 获取个人信息
// get需要返回值,所以有return
export const getInfo = () => {
  // result不一定有值
  // 如果有值就取本地的值,如果没有就给它一个默认值
  const defaultObj = { token: '', userId: '' }
  const result = localStorage.getItem(INFO_KEY)
  return result ? JSON.parse(JSON.stringify(result)) : defaultObj
}

// 设置个人信息
export const setInfo = (obj) => {
  // 对象不能直接往本地存,所以需要JSON.stringify序列化一下
  localStorage.setItem(INFO_KEY, JSON.stringify(obj))
}

// 移除个人信息
export const removeInfo = () => {
  localStorage.removeItem(INFO_KEY)
}

store/modules/user.js

意思是存到vuex的同时,存一份到本地

import { getInfo, setInfo } from '@/utils/storage'

export default {
  namespaced: true,
  // 状态
  state () {
    return {
      // 个人权证相关
      // 不像之前那样写死了,直接从本地拿
      userInfo: getInfo()
    //   userInfo: {
    //     token: '',
    //     userId: ''
    //   }
    }
  },
  mutations: {
    // 提供一个方法,去设置上面的state里的变量
    setUserInfo (state, obj) {
      state.userInfo = obj
      setInfo()
    }

  },
  actions: {

  },
  getters: {

  }
}

添加请求loading效果

Vue智慧商城案例—登录页面

  1. 请求拦截器 这里,添加一个loading的轻提示,并且禁止点击
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么

  // ——————开启loading,禁止背景点击(节流处理,防止多次无效触发)
  Toast.loading({
    message: '加载中...',
    forbidClick: true,
    duration: 0 // 设置为0,就不会在两秒后自己关,会一直有,然后我们之后再通过响应拦截器响应回来的设置它的关
  })

  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})
  1. 响应拦截器 里去关
if (res.status !== 200) {
    // 给提示
    Toast(res.message)// 这里就不写死了,用后台返回回来的提示
    // 抛出一个错误的promise
    return Promise.reject(res.message)
  } else {
    // 正确情况,直接走业务核心逻辑,清除loading
    Toast.clear()
  }

页面访问拦截

Vue智慧商城案例—登录页面

Vue智慧商城案例—登录页面 打开vue官方,找到导航守卫处,导航守卫 | Vue Router (vuejs.org)

  1. 配一个全局的获取token信息的getters,这样找起来也比较方便 Vue智慧商城案例—登录页面
// 定义一个数组,专门用户存放所有需要权限访问的页面
const authUrls = ['/pay', '/myorder']

router.beforeEach((to, from, next) => {
  // console.log(to, from, next)
  // 看 to.path 是否在 authUrls 中出现过
  if (!authUrls.includes(to.path)) {
    // 非权限页面,直接放行
    next()
    return
  }

  // 如果是权限页面,需要判断token,也就是看看是否有用户信息
  import store from '@/store'
  // const token = store.state.user.userInfo.token
  const token = store.getters.token
  if (token) {
    next()
  } else {
    next('/login')
  }
})