Vue2案例

一、自定义创建项目

1、基于 VueCli 自定义创建项目

  • Babel / Router / Vuex / CSS / Linter
  • Vue2.x
  • VueRouter hash模式
  • CSS预处理 Less
  • ESlint: Standard config
  • Lint on Save
  • In dedicated config files (配置文件所在位置)
  • Npm

2、ESlint 代码规范

1. 认识代码规范

  • 代码规范:一套写代码的约定规则。
    • 赋值符号的左右是否需要空格
    • 一句结束是否是要加;
  • JavaScript Standard Style 规范说明: https://standardjs.com/rules-zhcn.html
    • 字符串使用单引号:‘abc’
    • 无分号:const name = ‘zs’
    • 关键字后加空格:if (name = ‘ls’) { … }
    • 函数名后加空格:function name (arg) { … }
    • 坚持使用全等 === 摒弃 ==

2. 代码规范错误

  • 目标:学会解决代码规范错误
  • 如果你的代码不符合 standard 的要求,ESlint 会跳出来刀子嘴,豆腐心地提示你。
  • 比如:在main.js中随意做一些改动,添加一些分号,空行。
  • 两种解决方案:
    • 手动修正
      • 根据错误提示来一项一项手动修改纠正。
      • 如果你不认识命令行中的语法报错是什么意思,根据错误代码去 [ESLint 规则表] 中查找其具体含义。
    • 自动修正
      • 基于 vscode 插件 ESLint 高亮错误,并通过配置 自动 帮助我们修复错误。

二、Vant-ui

  • 开发手册:https://vant-contrib.gitee.io/vant/v2/#/zh-CN/

1、Vue 组件库

  • 一般会按照不同平台进行分类:
    • PC端: element-ui(element-plus)、ant-design-vue
    • 移动端:vant-ui、Mint UI (饿了么)、Cube UI (滴滴

2、vant 全部导入

1. 安装 vant-ui

yarn add vant@latest-v2

2. 导入注册

  • main.js
import Vant from 'vant'
import 'vant/lib/index.css'
// 把vant中所有的组件都导入、注册
Vue.use(Vant)

3. 测试使用

主要按钮
信息按钮

3、按需导入

1. 安装 vant-ui

yarn add vant@latest-v2

2. 安装插件

yarn add babel-plugin-import -D

3. 配置信息

  • babel.config.js
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  // 需要添加的配置
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
}

4. 按需导入注册

  • @/plugins/vant-ui.js
import Vue from 'vue'
import { Button, Switch, Step, Steps, Tabbar, TabbarItem, NavBar, Toast, Search, Swipe, SwipeItem, Grid, GridItem, Icon, Lazyload, Rate, ActionSheet, Dialog, Checkbox, CheckboxGroup, Tab, Tabs } from 'vant'

// 按钮
Vue.use(Button)
// 开关
Vue.use(Switch)
// 步骤条
Vue.use(Step)
Vue.use(Steps)
// 标签栏
Vue.use(Tabbar)
Vue.use(TabbarItem)
// 导航栏
Vue.use(NavBar)
// 轻提示
Vue.use(Toast)
// 搜索框
Vue.use(Search)
// 轮播图
Vue.use(Swipe)
Vue.use(SwipeItem)
// 布局
Vue.use(Grid)
Vue.use(GridItem)
// Vant 图标
Vue.use(Icon)
// 懒加载
Vue.use(Lazyload)
//  评分
Vue.use(Rate)
// 反馈组件
Vue.use(ActionSheet)
// 弹窗组件
Vue.use(Dialog)
// 复选框
Vue.use(Checkbox)
Vue.use(CheckboxGroup)
// Tab 标签页
Vue.use(Tab)
Vue.use(Tabs)

5. 导入配置文件

  • main.js
import '@/plugins/vant-ui'

6. 测试使用

主要按钮
信息按钮

4、VW适配

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

1. 安装插件

yarn add [email protected] -D

2. 添加配置

  • 根目录新建 postcss.config.js 文件,填入配置
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 375
    }
  }
}

5、配置底部导航

1. 按需引入导航组件

  • @/plugins/vant-ui.js
import { Tabbar, TabbarItem } from 'vant'
Vue.use(Tabbar)
Vue.use(TabbarItem)

2. 修改文字、图标、颜色

  • @/views/layout/index.vue





6、Toast 轻提示

  • Toast 默认单例模式,后面 Toast 调用会将前一个覆盖,同一时间只能存在一个 Toast

1. 注册安装

  • @/plugins/vant-ui.js
import Vue from 'vue'
import { Button, Toast } from 'vant'

Vue.use(Button)
Vue.use(Toast)

2. 调用方式

  • 导入调用:组件内 或 非组件中均可





  • this直接调用 本质:将方法,注册挂载到了Vue原型上 Vue.prototype.$toast = xxx





三、Router

1、配置路由

  • 目标:分析项目页面,设计路由,配置一级路由
  • 但凡是单个页面,独立展示的,都是一级路由
  • @router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

import Layout from '@/views/layout'
import Login from '@/views/login'
import MyOrder from '@/views/myOrder'
import Pay from '@/views/pay'
import ProDetail from '@/views/proDetail'
import Search from '@/views/search'
import SearchList from '@/views/search/list'

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: '/',
      component: Layout,
      redirect: '/home',
      children: [
        { path: '/home', component: Home },
        { path: '/category', component: Category },
        { path: '/cart', component: Cart },
        { path: '/user', component: User }
      ]
    },
    { path: '/login', component: Login },
    { path: '/myOrder', component: MyOrder },
    { path: '/pay', component: Pay },
    { path: '/proDetail/:id', component: ProDetail },
    { path: '/search', component: Search },
    { path: '/searchList', component: SearchList }
  ]
})

export default router

2、导入路由

  • main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

3、页面访问拦截

  • 目标:基于全局前置守卫,进行页面访问拦截处理
  • 所有的路由一旦被匹配到,都会先经过全局前置守卫
  • 只有全局前置守卫放行,才会真正解析渲染组件,才能看到页面内容
  • 访问权限页面时,拦截或放行的关键点? → 用户是否有登录权证 token

Vue2案例_第1张图片

1. 获取 token

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

Vue.use(Vuex)

export default new Vuex.Store({
  getters: {
    token (state) {
      return state.user.userInfo.token
    }
  },
  modules: {
    user
  }
})

2. 全局前置导航守卫

import Vue from 'vue'
import VueRouter from 'vue-router'

import store from '@/store'

Vue.use(VueRouter)

// 定义一个数组,存放所以需要权限的页面
const authUrls = ['/user', '/myOrder', '/pay']

// 全局前置导航守卫
// to:往哪里去, 到哪去的路由信息对象
// from:从哪里来, 从哪来的路由信息对象
// 3. next() 是否放行
// 如果next()调用,就是放行
// next(路径) 拦截到某个路径页面, 跳转到该页面
router.beforeEach((to, from, next) => {
  console.log(to, from, next)
  // 获取 to.path 判断是否存在于 需要权限的页面
  if (!authUrls.includes(to.path)) {
    // 放行
    next()
  } else {
    const token = store.getters.token
    if (token) {
      // 已经登录,放行
      next()
    } else {
      // 未登录,跳转登陆页
      next('/login')
    }
  }
})

export default router

四、Axios 封装

  • 使用 axios 来请求后端接口, 一般都会对 axios 进行 一些配置 (比如: 配置基础地址,请求响应拦截器等)
  • 所以项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个 request 模块中, 便于维护使用
  • 中文文档:https://www.axios-http.cn/docs/intro

1、安装 Axios

yarn add axios

2、新建 request 模块

  • @/utils/request.js
  • 创建 axios 实例
  • 配置导出势力 instance
import axios from 'axios'

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

// 自定义配置:请求/响应 拦截器
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么
  return response.data
}, function (error) {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  return Promise.reject(error)
})

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

3、添加响应拦截器

import axios from 'axios'
import { Toast } from 'vant'

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

// 自定义配置:请求/响应 拦截器
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 开启loading,禁止背景点击(节流处理,防止多次无效触发)
  Toast.loading({
    message: '加载中...',
    forbidClick: true, // 禁止背景点击
    loadingType: 'spinner', // 加载图标类型,支持 spinner loading circle
    duration: 0 // 持续展示时间(ms),0 表示永久展示
  })
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么
  const res = response.data
  if (res.status !== 200) {
    // 获取提示
    Toast(res.message)
    // 抛出错误提示
    return Promise.reject(res.message)
  } else {
    // 正确情况,走业务核心逻辑,清除 loading 效果
    Toast.clear()
  }
  return res
}, function (error) {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  return Promise.reject(error)
})

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

4、封装 api 接口模块

  • @/api/login.js
import request from '@/utils/request'
// 用于存放所有登录相关的接口请求
// 1. 获取图形验证码
export const getPicCode = () => {
  return request({
    url: '/captcha/image',
    method: 'get'
  })
}

5、添加请求 loading 效果

  • 添加 loading 提示的好处:
    • 节流处理:防止用户在一次请求还没回来之前,多次进行点击,发送无效请求
    • 友好提示:告知用户,目前是在加载中,请耐心等待,用户体验会更好

1. 添加 loading 效果

  • 请求拦截器中,每次请求,打开 loading
// 自定义配置:请求/响应 拦截器
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 开启loading,禁止背景点击(节流处理,防止多次无效触发)
  Toast.loading({
    message: '加载中...',
    forbidClick: true, // 禁止背景点击
    loadingType: 'spinner', // 加载图标类型,支持 spinner loading circle
    duration: 0 // 持续展示时间(ms),0 表示永久展示
  })
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

2. 关闭 loading 效果

  • 响应拦截器中,每次响应,关闭 loading
  • Toast 默认单例模式,后面 Toast 调用会将前一个覆盖,同一时间只能存在一个 Toast
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么
  const res = response.data
  if (res.status !== 200) {
    // 获取提示
    Toast(res.message)
    // 抛出错误提示
    return Promise.reject(res.message)
  } else {
    // 正确情况,走业务核心逻辑,清除 loading 效果
    Toast.clear()
  }
  return res
}, function (error) {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  return Promise.reject(error)
})

6、统一携带请求头

// 自定义配置:请求/响应 拦截器
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 开启loading,禁止背景点击(节流处理,防止多次无效触发)
  Toast.loading({
    message: '加载中...',
    forbidClick: true, // 禁止背景点击
    loadingType: 'spinner', // 加载图标类型,支持 spinner loading circle
    duration: 0 // 持续展示时间(ms),0 表示永久展示
  })

  // 只要有 token, 就在请求的时候携带, 便于 请求需要授权的接口
  const token = store.getters.token
  if (token) {
    config.headers['Access-Token'] = token
    // 设置平台
    config.headers.platform = 'H5'
  }

  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

五、Vuex

1、登录权证信息存储

1. 持久化存储

  • 封装 storage 存储模块,利用本地存储,进行 vuex 持久化处理:storage.js
// 约定一个通用的键名
const INFO_KEY = 'shopping_info'

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

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

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

2. 构建 user 模块

  • @/store/modules/user.js
import { getUserInfo, setUserInfo } from '@/utils/storage'

export default {
  namespaced: true,
  state () {
    return {
      userInfo: getUserInfo()
    }
  },
  mutations: {
    setUserInfo (state, userInfo) {
      state.userInfo = userInfo
      setUserInfo(userInfo)
    }
  },
  actions: {},
  getters: {}
}

3. 引入模块

  • @/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    user
  }
})

2、购物车信息存储

1. 构建 cart 模块

import { getCartList, updateCartNum, deleteCartGoods } from '@/api/cart'
import { Toast } from 'vant'
export default {
  namespaced: true,
  state () {
    return {
      cartTotal: 0, // 购物车总数
      cartList: [] // 购物车列表
    }
  },
  mutations: {
    // 设置购物车总数
    setcartTotal (state, cartTotal) {
      state.cartTotal = cartTotal
    },
    // 设置购物车列表
    setCartList (state, cartList) {
      state.cartList = cartList
    },
    // 切换商品选中状态
    toggleCheck (state, goodsId) {
      state.cartList.forEach(item => {
        if (item.goods_id === goodsId) {
          item.is_checked = !item.is_checked
        }
      })
    },
    // 切换全选状态
    toggleAllCheck (state, flag) {
      state.cartList.forEach(item => {
        item.is_checked = flag
      })
    }
  },
  actions: {
    // 获取购物车列表
    async getCartListAction (context) {
      const { data: { cartTotal, list } } = await getCartList()
      // 默认选中
      list.forEach(item => {
        item.is_checked = true
      })
      context.commit('setCartList', list)
      context.commit('setcartTotal', cartTotal)
    },
    // 修改商品数量
    async changeCountAction (context, obj) {
      const { goodsNum, goodsId, goodsSkuId } = obj
      // 修改后台数量
      await updateCartNum(goodsNum, goodsId, goodsSkuId)
      // 更新数据
      context.dispatch('getCartListAction')
    },
    // 删除选中的商品
    async deleteCheckedAction (context) {
      const cartIds = context.getters.getCheckedList.map(item => item.id)
      await deleteCartGoods(cartIds)
      // 更新数据
      await context.dispatch('getCartListAction')
      context.commit('toggleAllCheck', false)
      Toast.success('删除成功!')
    }
  },
  getters: {
    // 获取选中商品总数
    getCheckedTotal (state) {
      return state.cartList.reduce((sum, item) => sum + (item.is_checked ? item.goods_num : 0), 0)
    },
    // 获取选中商品列表
    getCheckedList (state) {
      return state.cartList.filter(item => item.is_checked)
    },
    // 获取选中商品总价
    getCheckedTotalPrice (state, getters) {
      return getters.getCheckedList.reduce((sum, item) => {
        return sum + item.goods.goods_price_min * item.goods_num
      }, 0).toFixed(2)
    },
    // 判断是否全选
    isAllChecked (state) {
      // return getters.getCheckedList.length === state.cartList.length && getters.getCheckedList.length !== 0
      return state.cartList.every(item => item.is_checked) && state.cartList.length !== 0
    }
  }
}

2. 引入模块

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

Vue.use(Vuex)

export default new Vuex.Store({
  strict: true,
  state: {
  },
  getters: {
    token (state) {
      return state.user.userInfo.token
    }
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    user,
    cart
  }
})

六、登陆页面

1、获取图形验证码

1. 接口封装

  • @/api/login.js
import request from '@/utils/request'
// 用于存放所有登录相关的接口请求
// 1. 获取图形验证码
export const getPicCode = () => {
  return request({
    url: '/captcha/image',
    method: 'get'
  })
}

2. 页面渲染






2、短信验证倒计时

1. 接口封装

import request from '@/utils/request'

// 2. 获取短信验证码
export const getSmsCode = (captchaCode, captchaKey, mobile) => {
  return request.post('/captcha/sendSmsCaptcha', {
    form: {
      captchaCode, // 图形验证码
      captchaKey, // 图形验证码key
      mobile // 手机号
    }
  })
}

2. 接口渲染






3、登陆接口

1. 接口封装

import request from '@/utils/request'

// 3. 登录
export const login = (mobile, smsCode) => {
  return request.post('/passport/login', {
    form: {
      mobile, // 手机号 必需
      smsCode, // 短信验证码 必需
      isParty: false, // 是否存在第三方用户信息 必需
      partyData: {} // 三方登录信息,默认为:{} 可选
    }
  })
}

2. 接口渲染






七、首页渲染

1、首页静态结构

1. 静态结构

  • 引入组件:@/plugins/vant-ui.js
import Vue from 'vue'
import { Search, Swipe, SwipeItem, Grid, GridItem } from 'vant'

// 搜索框
Vue.use(Search)
// 轮播图
Vue.use(Swipe)
Vue.use(SwipeItem)
// 布局
Vue.use(Grid)
Vue.use(GridItem)

2. 接口封装

  • @/api/home.js
import request from '@/utils/request'

// 1.获取首页数据
export const getHomeData = () => {
  return request.get('/page/detail', {
    params: {
      pageId: 0
    }
  })
}

3. 封装组件

  • @/components/GoodsItem.vue





4. 页面渲染

  • @/views/layout/home.vue





2、搜索功能

1. 历史记录管理

  • 搜索历史基本渲染
  • 点击搜索 (添加历史):点击 搜索按钮 或 底下历史记录,都能进行搜索
    • 若之前 没有 相同搜索关键字,则直接追加到最前面
    • 若之前 已有 相同搜索关键字,将该原有关键字移除,再追加
  • 清空历史:添加清空图标,可以清空历史记录





2. 历史记录持久化

  • 持久化:搜索历史需要持久化,刷新历史不丢失
  • @/utils/storage.js
// 约定一个通用的键名
const HISTORY_KEY = 'shopping_history_search'

// 获取搜索历史
export const getSearchHistory = () => {
  const history = localStorage.getItem(HISTORY_KEY)
  return history ? JSON.parse(history) : []
}

// 设置搜索历史
export const setSearchHistory = (history) => {
  localStorage.setItem(HISTORY_KEY, JSON.stringify(history))
}

// 移除搜索历史
export const removeSearchHistory = () => {
  localStorage.removeItem(HISTORY_KEY)
}

3、搜索列表

1. 接口渲染

  • @/api/product.js
import request from '@/utils/request'

// 1.获取搜索数据
export const getSearchData = (searchObj) => {
  const { sortType, sortPrice, categoryId, goodsName, page } = searchObj
  return request.get('/goods/list', {
    params: {
      sortType, // all-按综合搜索(默认),sales-按销量搜索,price-按价格搜索
      sortPrice, // 0-价格从低到高, 1-价格从高到低
      categoryId,
      goodsName, // 商品名称
      page// 页码
    }
  })
}

2. 页面调用

  • @/views/search/list.vue





八、商品详情

1、商品详情展示

1. 封装接口

import request from '@/utils/request'

// 1.获取搜索数据
export const getSearchData = (searchObj) => {
  const { sortType, sortPrice, categoryId, goodsName, page } = searchObj
  return request.get('/goods/list', {
    params: {
      sortType, // all-按综合搜索(默认),sales-按销量搜索,price-按价格搜索
      sortPrice, // 0-价格从低到高, 1-价格从高到低
      categoryId,
      goodsName, // 商品名称
      page// 页码
    }
  })
}

// 2.根据商品id获取商品详情
export const getGoodsDetail = (goodsId) => {
  return request.get('/goods/detail', {
    params: {
      goodsId
    }
  })
}

// 3.根据商品id获取评价总数
export const getCommentTotal = (goodsId) => {
  return request.get('/comment/total', {
    params: {
      goodsId
    }
  })
}

// 4.根据商品id获取评价
export const getComments = (goodsId, limit) => {
  return request.get('/comment/listRows', {
    params: {
      goodsId,
      limit
    }
  })
}

// 5.根据商品id获取评价
export const getGoodsService = (goodsId) => {
  return request.get('/goods.service/list', {
    params: {
      goodsId
    }
  })
}

2. 页面渲染






2、加入购物车弹窗

1. 数字框组件

  • @/components/CountBox.vue





2. 加入购物车弹层






九、购物车

1、构建 vuex cart 模块

  • 构建模块 @/store/modules/cart.js

2、封装接口

import request from '@/utils/request'

// 添加购物车
export const addCart = (goodsId, goodsNum, goodsSkuId) => {
  return request.post('/cart/add', {
    goodsId,
    goodsNum,
    goodsSkuId
  })
}

// 获取购物车商品总数量
export const getCartTotalCount = () => {
  return request.get('/cart/total')
}

// 获取购物车商品列表
export const getCartList = () => {
  return request.get('/cart/list')
}

// 更新购物车商品数量
export const updateCartNum = (goodsNum, goodsId, goodsSkuId) => {
  return request.post('/cart/update', {
    goodsNum,
    goodsId,
    goodsSkuId
  })
}

// 删除购物车商品
export const deleteCartGoods = (cartIds) => {
  return request.post('/cart/clear', {
    cartIds
  })
}

3、动态渲染功能实现

  • 封装 getters 实现动态统计
  • 全选反选功能
  • 数字框修改数量功能
  • 编辑切换状态,删除功能
  • 空购物车处理





十、订单结算

1、订单结算台

1. 封装接口

import request from '@/utils/request'

// 1.获取收货地址
export const getAddress = () => {
  return request.get('/address/list')
}

// 2.获取默认收货地址
export const getDefaultAddress = () => {
  return request.get('/address/defaultId')
}

// 3.订单结算
// mode: cart => obj: { cartId }
// mode: buyNow => obj: { goodsId, goodsNum, goodsSkuId }
export const checkoutOrder = (mode, obj) => {
  return request.get('/checkout/order', {
    params: {
      delivery: 10, // 10 快递 20 自提
      couponId: 0, // 优惠券id, 0:不适用优惠
      isUsePoints: 0, // 是否使用积分, 0:不使用
      mode, // cart/buyNow
      ...obj // 将传递过来的参数对象动态展开
    }
  })
}

2. 订单页面渲染






3. mixins 混入

  • 如果此处和组件内,提供同名的 data 和 methods, 则会覆盖此处同名方法
// 登录确认
export default {
  methods: {
    // 登录确认
    loginConfirm () {
      if (!this.$store.getters.token) {
        // 未登录,引导前往登陆页面
        this.$dialog.confirm({
          title: '温馨提示',
          message: '此操作只有登陆才能进行!',
          confirmButtonText: '去登录',
          cancelButtonText: '再逛逛'
        }).then(() => {
          // 确定前往登录页(replace:跳转页面不会添加到历史记录)
          this.$router.replace({
            path: '/login',
            query: {
              // 登录成功后跳转回来
              backUrl: this.$route.fullPath
            }
          })
        }).catch(() => {})
        return true
      }
      return false
    }
  }
}

4. 立即购买

  • @/views/proDetail/index.vue





5. 购物车结算






2、提交订单并支付

1. 封装接口

import request from '@/utils/request'

// 4.订单提交
// mode: cart => obj: { cartId, remark }
// mode: buyNow => obj: { goodsId, goodsNum, goodsSkuId, remark }
export const submitOrder = (mode, obj) => {
  return request.get('/checkout/submit', {
    params: {
      delivery: 10, // 10 快递 20 自提
      couponId: 0, // 优惠券id, 0:不适用优惠
      isUsePoints: 0, // 是否使用积分, 0:不使用
      payType: 10, // 10:余额支付
      mode, // cart/buyNow
      ...obj // 将传递过来的参数对象动态展开
    }
  })
}

2. 页面渲染






3、订单管理

1. 封装接口

import request from '@/utils/request'

// 5. 我的订单
export const getOrderList = (dataType, page) => {
  return request.get('/order/list', {
    params: {
      dataType, // 订单类型,all-全部,payment-待支付,delivery-待发货,received-待收货,comment-待评价
      page
    }
  })
}

2. 订单组件






3. 页面渲染






4、个人中心

1. 封装接口

import request from '@/utils/request'

// 获取个人信息
export const getUserInfoDetail = () => {
  return request.get('/user/info')
}

2. 页面渲染






3. 退出登陆

  • @/views/layout/user.vue





  • @/store/modules/user.js
import { getUserInfo, setUserInfo } from '@/utils/storage'

export default {
  namespaced: true,
  state () {
    return {
      userInfo: getUserInfo()
    }
  },
  mutations: {
    setUserInfo (state, userInfo) {
      state.userInfo = userInfo
      // 缓存用户信息
      setUserInfo(userInfo)
    }
  },
  actions: {
    logout (context) {
      // 清除用户信息
      context.commit('setUserInfo', {})
      // 清除购物车信息
      context.commit('cart/setCartList', [], { root: true })
    }
  },
  getters: {}
}

十一、打包

1、打包命令

yarn build

2、配置publicPath

  • vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  publicPath: './',
  transpileDependencies: true
})

3、路由懒加载

import Vue from 'vue'
import VueRouter from 'vue-router'

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 Layout from '@/views/layout'

const Login = () => import('@/views/login')
const MyOrder = () => import('@/views/myOrder')
const Pay = () => import('@/views/pay')
const ProDetail = () => import('@/views/proDetail')
const Search = () => import('@/views/search')
const SearchList = () => import('@/views/search/list')

// import store from '@/store'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: Layout,
      redirect: '/home',
      children: [
        { path: '/home', component: Home },
        { path: '/category', component: Category },
        { path: '/cart', component: Cart },
        { path: '/user', component: User }
      ]
    },
    { path: '/login', component: Login },
    { path: '/myOrder', component: MyOrder },
    { path: '/pay', component: Pay },
    { path: '/proDetail/:id', component: ProDetail },
    { path: '/search', component: Search },
    { path: '/searchList', component: SearchList }
  ]
})

// 定义一个数组,存放所以需要权限的页面
const authUrls = ['/myOrder', '/pay']
// 全局前置导航守卫
// to:往哪里去, 到哪去的路由信息对象
// from:从哪里来, 从哪来的路由信息对象
// 3. next() 是否放行
// 如果next()调用,就是放行
// next(路径) 拦截到某个路径页面, 跳转到该页面
router.beforeEach((to, from, next) => {
  // 获取 to.path 判断是否存在于 需要权限的页面
  if (!authUrls.includes(to.path)) {
    // 放行
    next()
  } else {
    // const token = store.getters.token
    if (store.getters.token) {
      // 已经登录,放行
      next()
    } else {
      // 未登录,跳转登陆页
      next('/login')
    }
  }
})

export default router

你可能感兴趣的:(学习笔记,vue,前端)