智慧商城项目(ing)

项目概述

智慧商城项目(ing)_第1张图片

1.创建项目

目标:基于VueCli自定义创建项目架子,将目录调整成符合企业规范的目录

智慧商城项目(ing)_第2张图片

  1. 删除多余的文件

  2. 修改路由配置 和App.vue

  3. 新增两个目录 api/utils

①api接口模块:发送ajax请求的接口模块

②utils工具模块:自己封装的一些工具方法模块

2.vant组件库

目标:认识第三方Vue组件库vant-ui
组件库:第三方封装好了很多的组件,整合到一起就是一个组件库
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/

Vue的组件库并不是唯一的,vant-ui也仅仅是组件库的一种
一般会按照不同的平台进行分类
① PC端:element-ui(element-plus) ,ant-design-vue
② 移动端:vant-ui ; Mint UI(饿了么) ; Cube UI(滴滴)

vant全部导入 和 按需导入

目标:明确 全部导入 和 按需导入 的区别

全部导入就相当于把整个工具箱全都放在了项目当中,好处是方便,就是我在任何页面中想要加一个密码框等效果,由于是导入整个,所以在那用都可以.缺点是会把所有组件都导入项目中,会使项目体积非常大
智慧商城项目(ing)_第3张图片

按需导入就相当于有vant这么一个工具箱,但是在项目当中要用的时候,并不是把整个工具箱丢到项目中,而是需要用到哪一个,就导入哪一个来使用
智慧商城项目(ing)_第4张图片

组件库的使用

1.全部导入

① 安装vant-ui

yarn add vant@latest-v2

②main.js中注册

import Vant from 'vant'
import 'vant/lib/index.css'
//把vant中所有组件都导入了
Vue.use(Vant)

③测试使用

主要按钮
信息按钮
2.按需导入

①安装 vant-ui(已安装)

②安装插件

npm i babel-plugin-import -D

③babel.config.js中配置

module.exports = {
	presets: [
		@vue/cli-plugin-babel/preset'
	],
	plugins: [
		['import', {
			libraryName: 'vant',
			libraryDirectory: 'es',
			style: true
		}, 'vant']
	}
}

④main.js按需导入注册

import Vue from 'vue';
import { Button } from 'vant';

Vue.use(Button);

⑤测试使用

主要按钮
信息按钮

为了将来不在main.js中到人很多,然后写的很长
智慧商城项目(ing)_第5张图片
可以在utils文件中建一个vant-ui.js,把要的东西放在这里面
智慧商城项目(ing)_第6张图片
然后再在main.js中导入这个文件
在这里插入图片描述

3.项目中的vw适配

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

Vant 默认使用 px 作为样式单位,如果需要使用 viewport 单位 (vw, vh, vmin, vmax),推荐使用 postcss-px-to-viewport 进行转换。
postcss-px-to-viewport 是一款 PostCSS 插件,用于将 px 单位转化为 vw/vh 单位。

视图中的长度是px,那么就需要把px转换成vw(vant2进阶用法)
①安装插件

yarn add [email protected] -D

②根据目录新建postcss.comfig.js

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 375,
    },
  },
};

4.路由设计配置

①目标分析项目页面,设计路由,配置一级路由

但凡是单个页面,独立展示的,都是一级路由

准备好views中的各个页面,然后开始在index.js中配置路由
智慧商城项目(ing)_第7张图片
配对应的路由规则,路由规则就是数组里面包对象

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'
import store from '@/store'
Vue.use(VueRouter)
const router = new VueRouter({
 routes: [
   { path: '/login', component: Login },
   {
     path: '/',
     component: Layout,// 首页
     redirect: '/home',
     children: [
       { path: '/home', component: Home },
       { path: '/category', component: Category },
       { path: '/cart', component: Cart },
       { path: '/user', component: User }
     ]
   },
   { path: '/search', component: Search },
   { path: '/searchlist', component: SearchList },
   // 动态路由传参,确认将来是哪个商品,路由参数中携带 id  这个是商品详情,所以得知道是哪一个商品,这就需要用到动态路由传参
   { path: '/prodetail/:id', component: ProDetail },
  { path: '/pay', component: Pay },
   { path: '/myorder', component: MyOrder }
 ]
})

基础用法

Icon 的 name 属性支持传入图标名称或图片链接,所有可用的图标名称见右侧示例。



徽标提示

设置 dot 属性后,会在图标右上角展示一个小红点;设置 badge 属性后,会在图标右上角展示相应的徽标。




图标颜色

Icon 的 color 属性用来设置图标的颜色。



图标大小

Icon 的 size 属性用来设置图标的尺寸大小,默认单位为 px。

 

自定义颜色


  标签
  标签
  标签
  标签

//active-color是激活时的颜色,inactive是未激活

智慧商城项目(ing)_第8张图片

②基于底部导航,完成二级路由配置

智慧商城项目(ing)_第9张图片
智慧商城项目(ing)_第10张图片

5.登录静态页面布局

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;
}

(2) main.js 导入common.less

import '@/styles/common.less'

(3)图片素材拷贝到assets目录【备用】

2.登录页静态布局编写

(1)头部组件说明(NavBar)


智慧商城项目(ing)_第11张图片
智慧商城项目(ing)_第12张图片
这里的这个小箭头是蓝色的,希望以后所有的都是蓝色的,就可以把他写到通用样式里
先在检查里找到它的名字
在这里插入图片描述
然后在里面写

在这里插入图片描述

// 添加导航的通用样式
.van-nav-bar {
  .van-nav-bar__arrow {
    color: #333;
  }
}

(2)通用样式覆盖

(3)其他静态结构编写

6.request模块 - axios封装

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

使用axios来请求后端接口,一般都会对axios进行 一些配置(比如:配置基础地址,请求响应拦截器等)
所以项目开发中,都会对axios进行基本的二次封装,单独封装到一个request模块中,便于维护使用
智慧商城项目(ing)_第13张图片

  1. 安装 axios
npm i axios
  1. 新建 utils/request.js 封装 axios 模块

    利用 axios.create 创建一个自定义的 axios 来使用

    http://www.axios-js.com/zh-cn/docs/#axios-create-config

/* 封装axios用于发送请求 */
import axios from 'axios'

// 创建一个新的axios实例,将来对创建出来的实例,进行自定义配置
// 好处:不会污染原始的axios实例
const request = axios.create({
  baseURL: 'http://cba.itlike.com/public/index.php?s=/api/',
  timeout: 5000
})

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

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  return response.data
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error)
})

export default request
  1. 获取图形验证码,请求测试
import request from '@/utils/request'
export default {
  name: 'LoginPage',
  async created () {
    const res = await request.get('/captcha/image')
    console.log(res)
  }
}

智慧商城项目(ing)_第14张图片

7.图形验证码功能完成

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

说明:

  1. 图形验证码,本质就是一个从后台请求回来的一个图片
  2. 用户将来输入图形验证码,用于强制人机交互,可以抵御机器自动化攻击(例如:避免批量请求获取短信)

需求:

  1. 动态将请求回来的base64图片,解析渲染出来
  2. 点击验证码图片盒子,要刷新验证码
  1. 准备数据,获取图形验证码后存储图片路径,存储图片唯一标识

<script>
import { codeLogin, getMsgCode, getPicCode } from '@/api/login'
// import { Toast } from 'vant'

export default {
  name: 'LoginPage',
  data () {
    return {
      picKey: '', // 将来请求传递的图形验证码唯一标识
      picUrl: '', // 存储请求渲染的图片地址
      picCode: '', // 用户输入的图形验证码
    }
  },
  async created () {
    this.getPicCode()
  },
  methods: {
    // 获取图形验证码
    async getPicCode () {
      const { data: { base64, key } } = await getPicCode()
      this.picUrl = base64 // 存储地址
      this.picKey = key // 存储唯一标识
    },
 }
  1. 动态渲染图形验证码,并且点击时要重新刷新验证码

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

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

智慧商城项目(ing)_第15张图片

//login/index.vue
import { getPicCode } from '@/api/login'

methods: {
    // 获取图形验证码
    async getPicCode () {
      const { data: { base64, key } } = await getPicCode()
      this.picUrl = base64 // 存储地址
      this.picKey = key // 存储唯一标识

      // Toast('获取图形验证码成功')
      // this.$toast('获取成功')
      // this.$toast.success('成功文案')
    },

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

// 1. 获取图形验证码
export const getPicCode = () => {
  return request.get('/captcha/image')
}

9.Toast轻提示

智慧商城项目(ing)_第16张图片

①导入调用

引入

import Vue from 'vue';
import { Toast } from 'vant';

Vue.use(Toast);

文字提示

Toast('提示内容');

②通过this直接调用

全局方法

引入 Toast 组件后,会自动在 Vue 的 prototype 上挂载 $toast 方法,便于在组件内调用。

export default {
  mounted() {
    this.$toast('提示文案');
  },
};

方法①
在login/index.vue中导入调用

import { Toast } from 'vant'

那么在合适的位置就可以调用了(在图形验证码的地方)

Toast('获取图形验证码成功')

这种方式组件内或非组件内均可使用(在main.js中也可以)

方法②(this)

//在组件内直接使用
this.$toast('获取成功')

10.短信验证倒计时

步骤分析

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

  2. 倒计时之前的校验处理(手机号,验证码)

  3. 封装短信验证请求接口,发送请求添加提示

1.login/index.vue

export default {
  name: 'LoginPage',
  data () {
    return {
      totalSecond: 60, // 总秒数
      second: 60, // 当前秒数,开定时器对 second--
      timer: null, // 定时器 id
    }
  },
}

2.准备好了就可以注册点击事件了


3.getCode还要写一个methods

 async getCode () {
      if (!this.validFn()) {
        // 如果没通过校验,没必要往下走了
        return
      }

      // 当前目前没有定时器开着,且 totalSecond 和 second 一致 (秒数归位) 才可以倒计时
      if (!this.timer && this.second === this.totalSecond) {
        // 发送请求
        // 预期:希望如果响应的status非200,最好抛出一个promise错误,await只会等待成功的promise
        await getMsgCode(this.picCode, this.picKey, this.mobile)

        this.$toast('短信发送成功,注意查收')

        // 开启倒计时
        this.timer = setInterval(() => {
          this.second--

          if (this.second <= 0) {
            clearInterval(this.timer)
            this.timer = null // 重置定时器 id
            this.second = this.totalSecond // 归位
          }
        }, 1000)
      }
    },

4.应为这些代码,离开页面时还在计时,所以要写一个离开页面清除定时器

  // 离开页面清除定时器
  destroyed () {
    clearInterval(this.timer)
  }

智慧商城项目(ing)_第17张图片


5.还要校验手机号和图形验证码是否合法

先在data中提供两个变量

export default {
  name: 'LoginPage',
  data () {
    return {
      mobile: '', // 手机号
      picCode: '', // 用户输入的图形验证码
    }
  },
}

6.然后给手机号和验证码进行绑定

7.校验 手机号 和 图形验证码 是否合法

 // 校验 手机号 和 图形验证码 是否合法
    // 通过校验,返回true
    // 不通过校验,返回false
    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
    },

8.还需要再在上面的倒计时前面加个判断

智慧商城项目(ing)_第18张图片

  1. 输入框 v-model 绑定变量
data () {
 return {
   mobile: '', // 手机号
   picCode: '' // 图形验证码
 }
},
   


  1. methods中封装校验方法
// 校验输入框内容
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
},
  1. 请求倒计时前进行校验
// 获取短信验证码
async getCode () {
 if (!this.validFn()) {
   return
 }
 ...
}

9.封装短信验证请求接口,发送请求添加提示

api/login.js

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

加这个
智慧商城项目(ing)_第19张图片

11.封装api登录接口,实现登录功能

1.阅读文档,封装登录接口

2.登录前的校验

3.调用方法,发送请求,成功添加提示并跳转

api/login.js

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

登录界面的校验,校验通过后,调用上面的接口
login/index.vue

 // 登录
    async login () {
      if (!this.validFn()) {
        return
      }

      if (!/^\d{6}$/.test(this.msgCode)) {
        this.$toast('请输入正确的手机验证码')
        return
      }

      console.log('发送登录请求')

      const res = await codeLogin(this.mobile, this.msgCode)
      this.$store.commit('user/setUserInfo', res.data)
      this.$toast('登录成功')

      // 进行判断,看地址栏有无回跳地址
      // 1. 如果有   => 说明是其他页面,拦截到登录来的,需要回跳
      // 2. 如果没有 => 正常去首页
      const url = this.$route.query.backUrl || '/'
      this.$router.replace(url)
    }
在data中添加
msgCode: '' // 短信验证码

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

问题:每次请求,都会有可能会有错误,就都需要错误提示

说明:响应拦截器是咱们拿到数据的 第一个 数据流转站,可以在里面统一处理错误
智慧商城项目(ing)_第20张图片
utils/request.js

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)
})

13.登录权证信息存储

目标:vuex构建user模块存储登录权证(token & userld)
补充说明:
1.token存入vuex的好处,易获取,响应式
2.vuex需要分模块 => user模块
构建user模块挂载到vuex提供mutations页面中commit调用

1. 新建 vuex user 模块 store/modules/user.js

export default {
  namespaced: true,
  state () {
    return {
      userInfo: {
        token: '',
        userId: ''
      },
    }
  },
  mutations: {},
  actions: {}
}

2. 挂载到 vuex 上

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    user,
  }
})

3. 提供 mutations

mutations: {
  setUserInfo (state, obj) {
    state.userInfo = obj
  },
},

4. 页面中 commit 调用

// 登录按钮(校验 & 提交)
async login () {
  if (!this.validFn()) {
    return
  }
  ...
  const res = await codeLogin(this.mobile, this.msgCode)
  this.$store.commit('user/setUserInfo', res.data)
  this.$router.push('/')
  this.$toast('登录成功')
}

14.storage存储模块 - vuex持久化处理

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

问题1:vuex刷新多久会丢失,怎么办?

//将token存入本地
localStorage.setItem('hm_shopping_info',JSON.stringify(xxx))

问题2么此存取操作太长怎么办?

把它封装成专门的存储模块storage,把一个一个存储方法封装成函数,到时候页面中只要调用对应的函数就可以了

  1. 新建 utils/storage.js 封装方法
const INFO_KEY = 'hm_shopping_info'

// 获取个人信息
export const getInfo = () => {
  const result = localStorage.getItem(INFO_KEY)
  return result ? JSON.parse(result) : {
    token: '',
    userId: ''
  }
}

// 设置个人信息
export const setInfo = (info) => {
  localStorage.setItem(INFO_KEY, JSON.stringify(info))
}

// 移除个人信息
export const removeInfo = () => {
  localStorage.removeItem(INFO_KEY)
}
  1. vuex user 模块持久化处理
import { getInfo, setInfo } from '@/utils/storage'
export default {
  namespaced: true,
  state () {
    return {
      userInfo: getInfo()
    }
  },
  mutations: {
    setUserInfo (state, obj) {
      state.userInfo = obj
      setInfo(obj)
    }
  },
  actions: {}
}

15.添加请求loading效果

背景:有时候因为网络原因,一次请求的结果可能需要一段时间后才能回来,

此时,需要给用户 添加loading效果

实操步骤:

  1. 请求拦截器中,每次请求,打开loading
  2. 响应拦截器中,每次响应,关闭loading

1. 请求时,打开 loading

// 添加请求拦截器
request.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  Toast.loading({
    message: '请求中...',
    forbidClick: true,
    loadingType: 'spinner',
    duration: 0
  })
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

2. 响应时,关闭 loading

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

16. 登录访问拦截 - 路由前置守卫

目标:基于全局前置守卫,进行页面访问拦截处理

说明:智慧商城项目,大部分页面,游客都可以直接访问, 如遇到需要登录才能进行的操作,提示并跳转到登录

但是:对于支付页,订单页等,必须是登录的用户才能访问的,游客不能进入该页面,需要做拦截处理

智慧商城项目(ing)_第21张图片

路由导航守卫 - 全局前置守卫

1.所有的路由一旦被匹配到,都会先经过全局前置守卫

2.只有全局前置守卫放行,才会真正解析渲染组件,才能看到页面内容

router.beforeEach((to, from, next) => {
  // 1. to   往哪里去, 到哪去的路由信息对象  
  // 2. from 从哪里来, 从哪来的路由信息对象
  // 3. next() 是否放行
  //    如果next()调用,就是放行
  //    next(路径) 拦截到某个路径页面
})

智慧商城项目(ing)_第22张图片

const authUrl = ['/pay', '/myorder']
router.beforeEach((to, from, next) => {
  const token = store.getters.token
  if (!authUrl.includes(to.path)) {
    next()
    return
  }

  if (token) {
    next()
  } else {
    next('/login')
  }
})

17. 首页 - 静态结构准备

智慧商城项目(ing)_第23张图片

  1. 静态结构和样式 layout/home.vue





  1. 新建components/GoodsItem.vue






  1. 组件按需引入
import { Search, Swipe, SwipeItem, Grid, GridItem } from 'vant'

Vue.use(GridItem)
Vue.use(Search)
Vue.use(Swipe)
Vue.use(SwipeItem)
Vue.use(Grid)

18. 首页 - 动态渲染

  1. 封装准备接口 api/home.js
import request from '@/utils/request'

// 获取首页数据
export const getHomeData = () => {
  return request.get('/page/detail', {
    params: {
      pageId: 0
    }
  })
}
  1. 页面中请求调用
import GoodsItem from '@/components/GoodsItem.vue'
import { getHomeData } from '@/api/home'
export default {
  name: 'HomePage',
  components: {
    GoodsItem
  },
  data () {
    return {
      bannerList: [],
      navList: [],
      proList: []
    }
  },
  async created () {
    const { data: { pageData } } = await getHomeData()
    this.bannerList = pageData.items[1].data
    this.navList = pageData.items[3].data
    this.proList = pageData.items[6].data
  }
}
  1. 轮播图、导航、猜你喜欢渲染


  
    
  




  

    

—— 猜你喜欢 ——

  1. 商品组件内,动态渲染



你可能感兴趣的:(前端,前端框架)