20230722----重返学习-vue3项目实战-收尾

day-118-one-hundred-and-eighteen-20230722-vue3项目实战-收尾

vue3项目实战-收尾

对数据统一做处理

  • src/utils/http.js
import axios from 'axios'
// 请求的时候可以做什么事? 1)拦截 携带token, 对响应状态码处理, 2)增加loading (增添一个队列)  3)接口可以保存取消的操作
class Http {
  setInterceptor(instance) {
    instance.interceptors.response.use(
      (res) => {
        if (res.data.resultCode == 200) {
          return res.data.data
        }
        return res.data // {resultCode:200,data:{}}
      },
      (err) => {
        return Promise.reject(err)
      }
    )
  }
}
export default new Http()
  • src/api/index.js
import http from '@/utils/http.js'
const API_LIST = {
  queryIndexInfos: '/index-infos' // 首页的获取数据接口,都在这里
}
// 首页数据的获取,直接通过 api 这个文件来操作
export function queryIndexInfos() {
  return http.get(API_LIST.queryIndexInfos)
}
  • src/views/Home.vue

后退按钮

  • src/components/TopBar.vue


表单事件初步

  • src/views/Login.vue




验证码组件封装

  • src/components/Captcha.vue


验证码使用及校验

  • src/components/Captcha.vue


  • src/views/Login.vue




md5摘要算法

pnpm i blueimp-md5
  1. md5特点?
    • 不是加密算法,摘要算法。
    1. 内容不同摘要的结果不同
    2. 如果内容发生一点变化就会发生翻天覆地的变化 , 雪崩效应
    3. 生成长度一致
    4. 相同的内容生成的结果一直
    5. 不可逆
  • src/api/index.js
import http from '@/utils/http.js'
import md5 from 'blueimp-md5'
const API_LIST = {
  queryIndexInfos: '/index-infos', // 首页的获取数据接口,都在这里
  userLogin: '/user/login',
  userRegister: '/user/register'
}
// 首页数据的获取,直接通过 api 这个文件来操作
export function queryIndexInfos() {
  return http.get(API_LIST.queryIndexInfos)
}
// md5特点? 不是加密算法,摘要算法 1)内容不同摘要的结果不同 2) 如果内容发生一点变化就会发生翻天覆地的变化 , 雪崩效应
// 3) 生成长度一致  4)相同的内容生成的结果一直 5) 不可逆
export const userLogin = (loginName, password) => {
  password = md5(password)
  return http.post(API_LIST.userLogin, {
    loginName,
    passwordMd5: password
  })
}
// 用户注册
export const userRegister = (loginName, password) => {
  return http.post(API_LIST.userRegister, {
    loginName,
    password
  })
}

注册登录操作

  • 核心操作:

    • src/utils/http.js

      import axios from 'axios'
      // 请求的时候可以做什么事? 1)拦截 携带token, 对响应状态码处理, 2)增加loading (增添一个队列)  3)接口可以保存取消的操作
      class Http {
        setInterceptor(instance) {
          instance.interceptors.response.use(
            (res) => {
              if (res.data.resultCode == 200) {
                // 对返回值的状态码是200的情况统一处理
                return res.data.data
              }
              if (res.data.resultCode === 500) {
                return Promise.reject(res.data)
              }
              return res.data // {resultCode:200,data:{}}
            },
            (err) => {
              return Promise.reject(err)
            }
          )
        }
      }
      export default new Http()
      
    • src/api/index.js

      import http from '@/utils/http.js'
      import md5 from 'blueimp-md5'
      const API_LIST = {
        userLogin: '/user/login',
        userRegister: '/user/register'
      }
      // md5特点? 不是加密算法,摘要算法 1)内容不同摘要的结果不同 2) 如果内容发生一点变化就会发生翻天覆地的变化 , 雪崩效应
      // 3) 生成长度一致  4)相同的内容生成的结果一直 5) 不可逆
      export const userLogin = (loginName, password) => {
        password = md5(password)
        return http.post(API_LIST.userLogin, {
          loginName,
          passwordMd5: password
        })
      }
      // 用户注册
      export const userRegister = (loginName, password) => {
        return http.post(API_LIST.userRegister, {
          loginName,
          password
        })
      }
      
    • src/views/Login.vue

      
      
      
  • 代码示例

    • src/utils/http.js

      import axios from 'axios'
      // 请求的时候可以做什么事? 1)拦截 携带token, 对响应状态码处理, 2)增加loading (增添一个队列)  3)接口可以保存取消的操作
      class Http {
        constructor() {
          // 根据环境变量设置请求的路径
          this.baseURL = import.meta.env.DEV ? 'http://backend-api-01.newbee.ltd/api/v1' : '/'
          this.timeout = 5000
        }
      
        setInterceptor(instance) {
          instance.interceptors.request.use(
            (config) => {
              // 携带token来做处理
              return config
            },
            (err) => {
              return Promise.reject(err)
            }
          )
          instance.interceptors.response.use(
            (res) => {
              if (res.data.resultCode == 200) {
                // 对返回值的状态码是200的情况统一处理
                return res.data.data
              }
              if (res.data.resultCode === 500) {
                return Promise.reject(res.data)
              }
              return res.data // {resultCode:200,data:{}}
            },
            (err) => {
              return Promise.reject(err)
            }
          )
        }
        request(options) {
          // 请求会实现拦截器
          const instance = axios.create() // 1.每次请求要创建一个新的实例
          this.setInterceptor(instance) // 2.设置拦截器
          // 发送请求参数
          return instance({
            ...options,
            baseURL: this.baseURL,
            timeout: this.timeout
          })
        }
        get(url, data) {
          return this.request({
            method: 'get',
            url,
            params: data
          })
        }
        post(url, data) {
          return this.request({
            method: 'post',
            url,
            data
          })
        }
      }
      export default new Http()
      
    • src/api/index.js

      import http from '@/utils/http.js'
      import md5 from 'blueimp-md5'
      const API_LIST = {
        queryIndexInfos: '/index-infos', // 首页的获取数据接口,都在这里
        userLogin: '/user/login',
        userRegister: '/user/register'
      }
      // 首页数据的获取,直接通过 api 这个文件来操作
      export function queryIndexInfos() {
        return http.get(API_LIST.queryIndexInfos)
      }
      // md5特点? 不是加密算法,摘要算法 1)内容不同摘要的结果不同 2) 如果内容发生一点变化就会发生翻天覆地的变化 , 雪崩效应
      // 3) 生成长度一致  4)相同的内容生成的结果一直 5) 不可逆
      export const userLogin = (loginName, password) => {
        password = md5(password)
        return http.post(API_LIST.userLogin, {
          loginName,
          passwordMd5: password
        })
      }
      // 用户注册
      export const userRegister = (loginName, password) => {
        return http.post(API_LIST.userRegister, {
          loginName,
          password
        })
      }
      
    • src/components/Captcha.vue

      
      
      
    • src/views/Login.vue

      
      
      
      
      
      

登录态校验

  • 代码示例

    • src/router/index.js

      import { createRouter, createWebHistory } from 'vue-router'
      import Home from '../views/Home.vue'
      const router = createRouter({
        history: createWebHistory(import.meta.env.BASE_URL),
        routes: [
          {
            path: '/',
            redirect: '/home'
          },
          {
            path: '/home',
            name: 'home',
            component: Home,
            meta: { title: '首页' }
          },
          {
            path: '/category',
            meta: { title: '分类' },
            name: 'category',
            component: () => import('../views/Category.vue')
          },
          {
            path: '/cart',
            name: 'cart',
            component: () => import('../views/Cart.vue'),
            meta: {
              needLogin: true // 需要登录才能访问的 我们需要配置这个属性
            }
          },
          {
            path: '/user',
            name: 'user',
            component: () => import('../views/User.vue')
          },
          {
            path: '/login',
            name: 'login',
            component: () => import('../views/Login.vue')
          }
        ]
      })
      
      router.beforeEach(async (to, from) => {
        let token = localStorage.getItem('token')
        if (token) {
          // 如果有token 说明登录成功,但是如果你访问的还是登录
          if (to.name === 'login') {
            return { path: '/' }
          }
        } else {
          // 没有登录,跳转到登录页面
          // /a/b  -> ['/a','/a/b']
          if (to.matched.some((item) => item.meta.needLogin)) {
            // 此路由需要登录但是没有登录, 应该跳转到登录页面
            return {
              path: '/login',
              query: {
                redirect: to.path, // 跳转到登录页面,并且告诉登录页面稍后回调回来
                ...to.query // 当前页面的其他参数也添加进去
              }
            }
          }
        }
      })
      export default router
      
    • src/views/Login.vue

      
      
      
      
      
  • 核心代码

    • src/router/index.js

      const router = createRouter({
        routes: [
          {
            path: '/cart',
            name: 'cart',
            component: () => import('../views/Cart.vue'),
            meta: {
              needLogin: true // 需要登录才能访问的 我们需要配置这个属性
            }
          },
          {
            path: '/login',
            name: 'login',
            component: () => import('../views/Login.vue')
          }
        ]
      })
      
      router.beforeEach(async (to, from) => {
        let token = localStorage.getItem('token')
        if (token) {
          // 如果有token 说明登录成功,但是如果你访问的还是登录
          if (to.name === 'login') {
            return { path: '/' }
          }
        } else {
          // 没有登录,跳转到登录页面
          // /a/b  -> ['/a','/a/b']
          if (to.matched.some((item) => item.meta.needLogin)) {
            // 此路由需要登录但是没有登录, 应该跳转到登录页面
            return {
              path: '/login',
              query: {
                redirect: to.path, // 跳转到登录页面,并且告诉登录页面稍后回调回来
                ...to.query // 当前页面的其他参数也添加进去
              }
            }
          }
        }
      })
      export default router
      
    • src/views/Login.vue

      
      
      

使用pinia的集成

  • 核心代码:

    • src/api/index.js

      import http from '@/utils/http.js'
      import md5 from 'blueimp-md5'
      const API_LIST = {
        queryCategories: '/categories' // 查询分类的路径
      }
      
      // 查询所有的分类
      export function queryCategories() {
        return http.get(API_LIST.queryCategories)
      }
      
      
    • src/stores/category.js

      import { queryCategories } from '@/api'
      export const useCategoryStore = defineStore('category', () => {
        const list = ref([]) // 默认所有分类的信息
        async function getCategories() {
          list.value = await queryCategories()
        }
        return { list, getCategories }
      })
      
    • src/views/Category.vue

      
      
  • 代码示例:

    • src/api/index.js

      import http from '@/utils/http.js'
      import md5 from 'blueimp-md5'
      const API_LIST = {
        queryIndexInfos: '/index-infos', // 首页的获取数据接口,都在这里
        userLogin: '/user/login',
        userRegister: '/user/register',
        queryCategories: '/categories' // 查询分类的路径
      }
      // 首页数据的获取,直接通过 api 这个文件来操作
      export function queryIndexInfos() {
        return http.get(API_LIST.queryIndexInfos)
      }
      // md5特点? 不是加密算法,摘要算法 1)内容不同摘要的结果不同 2) 如果内容发生一点变化就会发生翻天覆地的变化 , 雪崩效应
      // 3) 生成长度一致  4)相同的内容生成的结果一直 5) 不可逆
      export const userLogin = (loginName, password) => {
        password = md5(password)
        return http.post(API_LIST.userLogin, {
          loginName,
          passwordMd5: password
        })
      }
      // 用户注册
      export const userRegister = (loginName, password) => {
        return http.post(API_LIST.userRegister, {
          loginName,
          password
        })
      }
      
      // 查询所有的分类
      export function queryCategories() {
        return http.get(API_LIST.queryCategories)
      }
      
      
    • src/stores/category.js

      import { queryCategories } from '@/api'
      export const useCategoryStore = defineStore('category', () => {
        const list = ref([]) // 默认所有分类的信息
        async function getCategories() {
          list.value = await queryCategories()
        }
        return { list, getCategories }
      })
      
    • src/views/Category.vue

      
      

多层级渲染

  • src/views/Category.vue



新增一个跳转详情页

  • src/utils/http.js
import axios from 'axios'
// 请求的时候可以做什么事? 1)拦截 携带token, 对响应状态码处理, 2)增加loading (增添一个队列)  3)接口可以保存取消的操作
class Http {
  constructor() {
    // 根据环境变量设置请求的路径
    this.baseURL = import.meta.env.DEV ? 'http://backend-api-01.newbee.ltd/api/v1' : '/'
    this.timeout = 5000
  }

  setInterceptor(instance) {
    instance.interceptors.request.use(
      (config) => {
        // 携带token来做处理
        let token = localStorage.getItem('token')
        if (token) {
          config.headers['token'] = token
        }
        return config
      },
      (err) => {
        return Promise.reject(err)
      }
    )

    instance.interceptors.response.use(
      (res) => {
        if (res.data.resultCode == 200) {
          // 对返回值的状态码是200的情况统一处理
          return res.data.data
        }
        if (res.data.resultCode === 500) {
          return Promise.reject(res.data)
        }
        if (res.data.resultCode === 416) {
          localStorage.removeItem('token') // 416 可能是token错误,这个时候清除token,重新刷新
          // 刷新后就在此路由的全局钩子,就会走没有token的逻辑
          return window.location.reload()
          // return Promise.reject(res.data)
        }
        return res.data // {resultCode:200,data:{}}
      },
      (err) => {
        return Promise.reject(err)
      }
    )
  }
  request(options) {
    // 请求会实现拦截器
    const instance = axios.create() // 1.每次请求要创建一个新的实例
    this.setInterceptor(instance) // 2.设置拦截器
    // 发送请求参数
    return instance({
      ...options,
      baseURL: this.baseURL,
      timeout: this.timeout
    })
  }
  get(url, data) {
    return this.request({
      method: 'get',
      url,
      params: data
    })
  }
  post(url, data) {
    return this.request({
      method: 'post',
      url,
      data
    })
  }
}
export default new Http()
  • src/api/index.js
import http from '@/utils/http.js'
import md5 from 'blueimp-md5'
const API_LIST = {
  queryIndexInfos: '/index-infos', // 首页的获取数据接口,都在这里
  userLogin: '/user/login',
  userRegister: '/user/register',
  queryCategories: '/categories', // 查询分类的路径

  queryGoodsInfo: '/goods/detail'
}
export const queryGoodsInfo = (id) => {
  return http.get(API_LIST.queryGoodsInfo + `/${id}`)
}
// 首页数据的获取,直接通过 api 这个文件来操作
export function queryIndexInfos() {
  return http.get(API_LIST.queryIndexInfos)
}
// md5特点? 不是加密算法,摘要算法 1)内容不同摘要的结果不同 2) 如果内容发生一点变化就会发生翻天覆地的变化 , 雪崩效应
// 3) 生成长度一致  4)相同的内容生成的结果一直 5) 不可逆
export const userLogin = (loginName, password) => {
  password = md5(password)
  return http.post(API_LIST.userLogin, {
    loginName,
    passwordMd5: password
  })
}
// 用户注册
export const userRegister = (loginName, password) => {
  return http.post(API_LIST.userRegister, {
    loginName,
    password
  })
}

// 查询所有的分类
export function queryCategories() {
  return http.get(API_LIST.queryCategories)
}
  • src/components/HomeGoodsItem.vue




  • src/views/Detail.vue







请求携带token

  1. 在请求拦截器中做,如果本地有token这个字段,就在请求头上带着token。
  • src/utils/http.js
class Http {

  setInterceptor(instance) {
    instance.interceptors.request.use(
      (config) => {
        // 携带token来做处理
        let token = localStorage.getItem('token')
        if (token) {
          config.headers['token'] = token
        }
        return config
      },
      (err) => {
        return Promise.reject(err)
      }
    )
  }
}
  1. 每次请求如果有token,就带上token。
  • src/utils/http.js
import axios from 'axios'
// 请求的时候可以做什么事? 1)拦截 携带token, 对响应状态码处理, 2)增加loading (增添一个队列)  3)接口可以保存取消的操作
class Http {
  constructor() {
    // 根据环境变量设置请求的路径
    this.baseURL = import.meta.env.DEV ? 'http://backend-api-01.newbee.ltd/api/v1' : '/'
    this.timeout = 5000
  }

  setInterceptor(instance) {
    instance.interceptors.request.use(
      (config) => {
        // 携带token来做处理
        let token = localStorage.getItem('token')
        if (token) {
          config.headers['token'] = token
        }
        return config
      },
      (err) => {
        return Promise.reject(err)
      }
    )

    instance.interceptors.response.use(
      (res) => {
        if (res.data.resultCode == 200) {
          // 对返回值的状态码是200的情况统一处理
          return res.data.data
        }
        if (res.data.resultCode === 500) {
          return Promise.reject(res.data)
        }
        return res.data // {resultCode:200,data:{}}
      },
      (err) => {
        return Promise.reject(err)
      }
    )
  }
  request(options) {
    // 请求会实现拦截器
    const instance = axios.create() // 1.每次请求要创建一个新的实例
    this.setInterceptor(instance) // 2.设置拦截器
    // 发送请求参数
    return instance({
      ...options,
      baseURL: this.baseURL,
      timeout: this.timeout
    })
  }
  get(url, data) {
    return this.request({
      method: 'get',
      url,
      params: data
    })
  }
  post(url, data) {
    return this.request({
      method: 'post',
      url,
      data
    })
  }
}
export default new Http()

处理无效token

  • src/utils/http.js
import axios from 'axios'
// 请求的时候可以做什么事? 1)拦截 携带token, 对响应状态码处理, 2)增加loading (增添一个队列)  3)接口可以保存取消的操作
class Http {
  constructor() {
    // 根据环境变量设置请求的路径
    this.baseURL = import.meta.env.DEV ? 'http://backend-api-01.newbee.ltd/api/v1' : '/'
    this.timeout = 5000
  }

  setInterceptor(instance) {
    instance.interceptors.request.use(
      (config) => {
        // 携带token来做处理
        let token = localStorage.getItem('token')
        if (token) {
          config.headers['token'] = token
        }
        return config
      },
      (err) => {
        return Promise.reject(err)
      }
    )

    instance.interceptors.response.use(
      (res) => {
        if (res.data.resultCode == 200) {
          // 对返回值的状态码是200的情况统一处理
          return res.data.data
        }
        if (res.data.resultCode === 500) {
          return Promise.reject(res.data)
        }
        if (res.data.resultCode === 416) {
          localStorage.removeItem('token') // 416 可能是token错误,这个时候清除token,重新刷新
          // 刷新后就在此路由的全局钩子,就会走没有token的逻辑
          return window.location.reload()
          // return Promise.reject(res.data)
        }
        return res.data // {resultCode:200,data:{}}
      },
      (err) => {
        return Promise.reject(err)
      }
    )
  }
  request(options) {
    // 请求会实现拦截器
    const instance = axios.create() // 1.每次请求要创建一个新的实例
    this.setInterceptor(instance) // 2.设置拦截器
    // 发送请求参数
    return instance({
      ...options,
      baseURL: this.baseURL,
      timeout: this.timeout
    })
  }
  get(url, data) {
    return this.request({
      method: 'get',
      url,
      params: data
    })
  }
  post(url, data) {
    return this.request({
      method: 'post',
      url,
      data
    })
  }
}
export default new Http()
class Http {

  setInterceptor(instance) {

    instance.interceptors.response.use(
      (res) => {
        if (res.data.resultCode === 416) {
          localStorage.removeItem('token') // 416 可能是token错误,这个时候清除token,重新刷新
          // 刷新后就在此路由的全局钩子,就会走没有token的逻辑
          return window.location.reload()
          // return Promise.reject(res.data)
        }
        return res.data // {resultCode:200,data:{}}
      },
      (err) => {
        return Promise.reject(err)
      }
    )
  }
}
  • src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      redirect: '/home'
    },
    {
      path: '/home',
      name: 'home',
      component: Home,
      meta: { title: '首页' }
    },
    {
      path: '/category',
      meta: { title: '分类' },
      name: 'category',
      component: () => import('../views/Category.vue')
    },
    {
      path: '/cart',
      name: 'cart',
      component: () => import('../views/Cart.vue'),
      meta: {
        needLogin: true // 需要登录才能访问的 我们需要配置这个属性
      }
    },
    {
      path: '/user',
      name: 'user',
      component: () => import('../views/User.vue')
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/Login.vue')
    },
    {
      path: '/detail/:id',
      name: 'detail',
      meta: {
        needLogin: true
      },
      component: () => import('../views/Detail.vue')
    }
  ]
})

router.beforeEach(async (to, from) => {
  let token = localStorage.getItem('token')
  if (token) {
    // 如果有token 说明登录成功,但是如果你访问的还是登录
    if (to.name === 'login') {
      return { path: '/' }
    }
  } else {
    // 没有登录,跳转到登录页面
    // /a/b  -> ['/a','/a/b']
    if (to.matched.some((item) => item.meta.needLogin)) {
      // 此路由需要登录但是没有登录, 应该跳转到登录页面
      return {
        path: '/login',
        query: {
          redirect: to.path, // 跳转到登录页面,并且告诉登录页面稍后回调回来
          ...to.query // 当前页面的其他参数也添加进去
        }
      }
    }
  }
})
export default router

购物车相关操作

  • src/api/index.js
// 获取购物车列表
export const queryShopCart = () => http.get('/shop-cart')

// 修改购买数量
export const setCartCount = (cartItemId, goodsCount) => {
  return http.request(
    {
      url: '/shop-cart',
      method: 'PUT'
    },
    {
      cartItemId,
      goodsCount
    }
  )
}
// 删除某条购物车数据
export const removeCart = (cartItemId) => {
  return http.request({
    url: `/shop-cart/${cartItemId}`,
    method: 'DELETE'
  })
}

// 新增购物车数据
export const addNewCart = (goodsId, goodsCount = 1) => {
  return http.post('/shop-cart', {
    goodsId,
    goodsCount
  })
}
  • src/stores/cart.js
// 1.获取购物车列表queryShopCart
// 2.修改数量 哪个商品的数量是多少 setCartCount
// 3.删除购物车的某个商品 removeCart
// 4.新增只传递新增的id即可addNewCart
import { queryShopCart, setCartCount, removeCart, addNewCart } from '@/api'
export const useCartStore = defineStore('cart', () => {
  const list = ref([])
  // 在pinia中缓存数据
  async function getShopCartList() {
    list.value = await queryShopCart()
  }
  async function setShopCartItem(id, count) {
    await setCartCount(id, count)
    // 将列表中的数据也要更新
  }
  async function removeShopCartItem(id) {
    await removeCart(id)
    // 将列表中的数据也要更新
  }
  async function addShopCartItem(id) {
    await addNewCart(id)
    // 将列表中的数据也要更新
  }

  return { list, getShopCartList, setShopCartItem, removeShopCartItem, addShopCartItem }
})
  • src/views/Detail.vue







购物车列表获取

  1. 在pinia中定义两个状态,一个hasCartStore表示是否已经请求过。一个list表示购物车列表。
  2. 购物车列表需要在用户跳转时,于路由前置守卫中在pinia中获取。
    • 如果有token,就从pinia中获取hasCartStore。
      • hasCartStore为true,就代表已经请求过后端了,页面中就可以直接获取到pinia中的购物车列表了。
      • hasCartStore为false,就表示没请求过后端了。调用pinia中的方法,更新数据,在请求完成后,缓存数据,并设置hasCartStore为true。
  • src/api/index.js
import http from '@/utils/http.js'
import md5 from 'blueimp-md5'
const API_LIST = {
  queryIndexInfos: '/index-infos', // 首页的获取数据接口,都在这里
  userLogin: '/user/login',
  userRegister: '/user/register',
  queryCategories: '/categories', // 查询分类的路径

  queryGoodsInfo: '/goods/detail'
}

// 获取购物车列表
export const queryShopCart = () => http.get('/shop-cart')

// 修改购买数量
export const setCartCount = (cartItemId, goodsCount) => {
  return http.request(
    {
      url: '/shop-cart',
      method: 'PUT'
    },
    {
      cartItemId,
      goodsCount
    }
  )
}
// 删除某条购物车数据
export const removeCart = (cartItemId) => {
  return http.request({
    url: `/shop-cart/${cartItemId}`,
    method: 'DELETE'
  })
}

// 新增购物车数据
export const addNewCart = (goodsId, goodsCount = 1) => {
  return http.post('/shop-cart', {
    goodsId,
    goodsCount
  })
}

export const queryGoodsInfo = (id) => {
  return http.get(API_LIST.queryGoodsInfo + `/${id}`)
}
// 首页数据的获取,直接通过 api 这个文件来操作
export function queryIndexInfos() {
  return http.get(API_LIST.queryIndexInfos)
}
// md5特点? 不是加密算法,摘要算法 1)内容不同摘要的结果不同 2) 如果内容发生一点变化就会发生翻天覆地的变化 , 雪崩效应
// 3) 生成长度一致  4)相同的内容生成的结果一直 5) 不可逆
export const userLogin = (loginName, password) => {
  password = md5(password)
  return http.post(API_LIST.userLogin, {
    loginName,
    passwordMd5: password
  })
}
// 用户注册
export const userRegister = (loginName, password) => {
  return http.post(API_LIST.userRegister, {
    loginName,
    password
  })
}

// 查询所有的分类
export function queryCategories() {
  return http.get(API_LIST.queryCategories)
}
  • src/stores/cart.js
// 1.获取购物车列表queryShopCart
// 2.修改数量 哪个商品的数量是多少 setCartCount
// 3.删除购物车的某个商品 removeCart
// 4.新增只传递新增的id即可addNewCart
import { queryShopCart, setCartCount, removeCart, addNewCart } from '@/api'
export const useCartStore = defineStore('cart', () => {
  const list = ref([])
  const hasCartStore = ref(false)
  // 在pinia中缓存数据
  async function getShopCartList() {
    list.value = await queryShopCart()
    hasCartStore.value = true
  }

  async function setShopCartItem(id, count) {
    await setCartCount(id, count)
    // 将列表中的数据也要更新
  }
  async function removeShopCartItem(id) {
    await removeCart(id)
    // 将列表中的数据也要更新
  }
  async function addShopCartItem(id) {
    await addNewCart(id)
    // 将列表中的数据也要更新
  }

  return {
    list,
    getShopCartList,
    setShopCartItem,
    removeShopCartItem,
    addShopCartItem,
    hasCartStore
  }
})
  • src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import { useCartStore } from '@/stores/cart.js'
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      redirect: '/home'
    },
    {
      path: '/home',
      name: 'home',
      component: Home,
      meta: { title: '首页' }
    },
    {
      path: '/category',
      meta: { title: '分类' },
      name: 'category',
      component: () => import('../views/Category.vue')
    },
    {
      path: '/cart',
      name: 'cart',
      component: () => import('../views/Cart.vue'),
      meta: {
        needLogin: true // 需要登录才能访问的 我们需要配置这个属性
      }
    },
    {
      path: '/user',
      name: 'user',
      component: () => import('../views/User.vue')
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/Login.vue')
    },
    {
      path: '/detail/:id',
      name: 'detail',
      meta: {
        needLogin: true
      },
      component: () => import('../views/Detail.vue')
    }
  ]
})

// 登陆态校验。
router.beforeEach(async (to, from) => {
  let token = localStorage.getItem('token')
  if (token) {
    // 如果有token 说明登录成功,但是如果你访问的还是登录
    if (to.name === 'login') {
      return { path: '/' }
    }
  } else {
    // 没有登录,跳转到登录页面
    // /a/b  -> ['/a','/a/b']
    if (to.matched.some((item) => item.meta.needLogin)) {
      // 此路由需要登录但是没有登录, 应该跳转到登录页面
      return {
        path: '/login',
        query: {
          redirect: to.path, // 跳转到登录页面,并且告诉登录页面稍后回调回来
          ...to.query // 当前页面的其他参数也添加进去
        }
      }
    }
  }
})

//
router.beforeEach(() => {
  let token = localStorage.getItem('token')
  if (token) {
    const store = useCartStore()
    if (!store.hasCartStore) {
      // 如果当前没有获取过购物车信息
      store.getShopCartList()
    }
  }
})

export default router
  • src/views/Detail.vue







Vue.js devtools的使用

购物车数量及添加

  • src/api/index.js
import http from '@/utils/http.js'
import md5 from 'blueimp-md5'
const API_LIST = {
  queryIndexInfos: '/index-infos', // 首页的获取数据接口,都在这里
  userLogin: '/user/login',
  userRegister: '/user/register',
  queryCategories: '/categories', // 查询分类的路径

  queryGoodsInfo: '/goods/detail'
}

// 获取购物车列表
export const queryShopCart = () => http.get('/shop-cart')

// 修改购买数量
export const setCartCount = (cartItemId, goodsCount) => {
  return http.request({
    url: '/shop-cart',
    method: 'PUT',
    data: {
      cartItemId,
      goodsCount
    }
  })
}

// 删除某条购物车数据
export const removeCart = (cartItemId) => {
  return http.request({
    url: `/shop-cart/${cartItemId}`,
    method: 'DELETE'
  })
}

// 新增购物车数据
export const addNewCart = (goodsId, goodsCount = 1) => {
  return http.post('/shop-cart', {
    goodsId,
    goodsCount
  })
}

export const queryGoodsInfo = (id) => {
  return http.get(API_LIST.queryGoodsInfo + `/${id}`)
}
// 首页数据的获取,直接通过 api 这个文件来操作
export function queryIndexInfos() {
  return http.get(API_LIST.queryIndexInfos)
}
// md5特点? 不是加密算法,摘要算法 1)内容不同摘要的结果不同 2) 如果内容发生一点变化就会发生翻天覆地的变化 , 雪崩效应
// 3) 生成长度一致  4)相同的内容生成的结果一直 5) 不可逆
export const userLogin = (loginName, password) => {
  password = md5(password)
  return http.post(API_LIST.userLogin, {
    loginName,
    passwordMd5: password
  })
}
// 用户注册
export const userRegister = (loginName, password) => {
  return http.post(API_LIST.userRegister, {
    loginName,
    password
  })
}

// 查询所有的分类
export function queryCategories() {
  return http.get(API_LIST.queryCategories)
}
  • src/stores/cart.js
// 1.获取购物车列表queryShopCart
// 2.修改数量 哪个商品的数量是多少 setCartCount
// 3.删除购物车的某个商品 removeCart
// 4.新增只传递新增的id即可addNewCart
import { queryShopCart, setCartCount, removeCart, addNewCart } from '@/api'
export const useCartStore = defineStore('cart', () => {
  const list = ref([])
  const hasCartStore = ref(false)
  // 在pinia中缓存数据
  async function getShopCartList() {
    list.value = await queryShopCart()
    hasCartStore.value = true
  }
  async function setShopCartItem(id, count) {
    await setCartCount(id, count)
    // 将列表中的数据也要更新,如果是修改也可以不发请求 通过id 获取到这一项,更改数量即可
    list.value.find((item) => item.cartItemId == id).goodsCount = count
  }
  async function removeShopCartItem(id) {
    await removeCart(id)
    // 将列表中的数据也要更新
  }
  async function addShopCartItem(id) {
    await addNewCart(id)
    // 将列表中的数据也要更新
    // 正常操作的情况下 应该是添加成功后,返回添加后的值
    getShopCartList()
  }
  return {
    hasCartStore,
    list,
    getShopCartList,
    setShopCartItem,
    removeShopCartItem,
    addShopCartItem
  }
})
  • src/views/Detail.vue







购物车页渲染

  • src/stores/cart.js
// 1.获取购物车列表queryShopCart
// 2.修改数量 哪个商品的数量是多少 setCartCount
// 3.删除购物车的某个商品 removeCart
// 4.新增只传递新增的id即可addNewCart
import { queryShopCart, setCartCount, removeCart, addNewCart } from '@/api'
export const useCartStore = defineStore('cart', () => {
  const list = ref([])
  const hasCartStore = ref(false)
  // 在pinia中缓存数据
  async function getShopCartList() {
    list.value = await queryShopCart()
    hasCartStore.value = true
  }
  async function setShopCartItem(id, count) {
    await setCartCount(id, count)
    // 将列表中的数据也要更新,如果是修改也可以不发请求 通过id 获取到这一项,更改数量即可
    list.value.find((item) => item.cartItemId == id).goodsCount = count
  }
  async function removeShopCartItem(id) {
    await removeCart(id)
    // 将列表中的数据也要更新
  }
  async function addShopCartItem(id) {
    await addNewCart(id)
    // 将列表中的数据也要更新
    // 正常操作的情况下 应该是添加成功后,返回添加后的值
    getShopCartList()
  }
  return {
    hasCartStore,
    list,
    getShopCartList,
    setShopCartItem,
    removeShopCartItem,
    addShopCartItem
  }
})
  • src/views/Cart.vue




  • src/components/GoodsItem.vue




购物车页操作

  • src/stores/cart.js
// 1.获取购物车列表queryShopCart
// 2.修改数量 哪个商品的数量是多少 setCartCount
// 3.删除购物车的某个商品 removeCart
// 4.新增只传递新增的id即可addNewCart
import { queryShopCart, setCartCount, removeCart, addNewCart } from '@/api'
export const useCartStore = defineStore('cart', () => {
  const list = ref([])
  const hasCartStore = ref(false)
  // 在pinia中缓存数据
  async function getShopCartList() {
    list.value = await queryShopCart()
    hasCartStore.value = true
  }
  async function setShopCartItem(id, count) {
    await setCartCount(id, count)
    // 将列表中的数据也要更新,如果是修改也可以不发请求 通过id 获取到这一项,更改数量即可
    list.value.find((item) => item.cartItemId == id).goodsCount = count
  }
  async function removeShopCartItem(id) {
    await removeCart(id)
    // 将列表中的数据也要更新
  }
  async function addShopCartItem(id) {
    await addNewCart(id)
    // 将列表中的数据也要更新
    // 正常操作的情况下 应该是添加成功后,返回添加后的值
    getShopCartList()
  }

  const badge = computed(() => {
    // 暂时先计算出 我们当前选中的详情页的数量
    const current = list.value.reduce((memo, current) => ((memo += current.goodsCount), memo), 0)
    return current
  })

  return {
    badge,
    hasCartStore,
    list,
    getShopCartList,
    setShopCartItem,
    removeShopCartItem,
    addShopCartItem
  }
})
  • src/views/Detail.vue







  • src/views/Cart.vue




  • src/components/NavBar.vue


  • src/components/GoodsItem.vue




购物车全选与删除操作

  • src/views/Cart.vue




  • src/stores/cart.js
// 1.获取购物车列表queryShopCart
// 2.修改数量 哪个商品的数量是多少 setCartCount
// 3.删除购物车的某个商品 removeCart
// 4.新增只传递新增的id即可addNewCart
// @ts-ignore
import { queryShopCart, setCartCount, removeCart, addNewCart } from '@/api'
export const useCartStore = defineStore('cart', () => {
  const list = ref([])
  const hasCartStore = ref(false)
  // 在pinia中缓存数据
  async function getShopCartList() {
    list.value = await queryShopCart()
    hasCartStore.value = true
  }
  async function setShopCartItem(id, count) {
    await setCartCount(id, count)
    // 将列表中的数据也要更新,如果是修改也可以不发请求 通过id 获取到这一项,更改数量即可
    list.value.find((item) => item.cartItemId == id).goodsCount = count
  }
  async function removeShopCartItem(id) {
    await removeCart(id)
    // 将列表中的数据也要更新
    list.value = list.value.filter((item) => item.cartItemId != id)
  }
  async function addShopCartItem(id) {
    await addNewCart(id)
    // 将列表中的数据也要更新
    // 正常操作的情况下 应该是添加成功后,返回添加后的值
    getShopCartList()
  }

  const badge = computed(() => {
    // 暂时先计算出 我们当前选中的详情页的数量
    const current = list.value.reduce((memo, current) => ((memo += current.goodsCount), memo), 0)
    return current
  })

  const total = computed(() => {
    return list.value.reduce((memo, current) => {
      if (current.checked) {
        // 选中就累加
        memo += current.goodsCount * current.sellingPrice
      }
      return memo
    }, 0)
  })
  const isCheckedAll = computed({
    get() {
      return list.value.every((item) => item.checked)
    },
    set(newVal) {
      list.value.forEach((item) => (item.checked = newVal))
    }
  })
  return {
    total,
    isCheckedAll,
    badge,
    hasCartStore,
    list,
    getShopCartList,
    setShopCartItem,
    removeShopCartItem,
    addShopCartItem
  }
})

搜索操作

  • src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import { useCartStore } from '@/stores/cart'
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      redirect: '/home'
    },
    {
      path: '/home',
      name: 'home',
      component: Home,
      meta: { title: '首页' }
    },
    {
      path: '/category',
      meta: { title: '分类' },
      name: 'category',
      component: () => import('../views/Category.vue')
    },
    {
      path: '/cart',
      name: 'cart',
      component: () => import('../views/Cart.vue'),
      meta: {
        needLogin: true // 需要登录才能访问的 我们需要配置这个属性
      }
    },
    {
      path: '/user',
      name: 'user',
      component: () => import('../views/User.vue')
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/Login.vue')
    },
    {
      path: '/detail/:id',
      name: 'detail',
      meta: {
        needLogin: true
      },
      component: () => import('../views/Detail.vue')
    },
    {
      path: '/search',
      name: 'search',
      meta: {
        needLogin: true
      },
      component: () => import('../views/Search.vue')
    }
  ]
})

// 鉴权看是否登录过
router.beforeEach(async (to) => {
  let token = localStorage.getItem('token')
  if (token) {
    // 如果有token 说明登录成功,但是如果你访问的还是登录
    if (to.name === 'login') {
      return { path: '/' }
    }
  } else {
    // 没有登录,跳转到登录页面
    // /a/b  -> ['/a','/a/b']
    if (to.matched.some((item) => item.meta.needLogin)) {
      // 此路由需要登录但是没有登录, 应该跳转到登录页面
      return {
        path: '/login',
        query: {
          redirect: to.path, // 跳转到登录页面,并且告诉登录页面稍后回调回来
          ...to.query // 当前页面的其他参数也添加进去
        }
      }
    }
  }
})
// 如果登陆过查询是否已经获取购物车信息
// 获取购物车的信息存储到pinia中
router.beforeEach(() => {
  let token = localStorage.getItem('token')
  if (token) {
    const store = useCartStore()
    if (!store.hasCartStore) {
      // 如果当前没有获取过购物车信息
      store.getShopCartList()
    }
  }
})
export default router
  • src/stores/category.js
import { queryCategories } from '@/api'
export const useCategoryStore = defineStore('category', () => {
  const list = ref([]) // 默认所有分类的信息
  async function getCategories() {
    // 获取分类将获取的数据保存到list变量中
    list.value = await queryCategories()
  }
  return { list, getCategories }
})
  • src/views/Search.vue





数组数据转化-扁平化

层序遍历

const stack = [
  {
    categoryId: 15,
    categoryLevel: 1,
    categoryName: '家电 数码 手机',
    secondLevelCategoryVOS: [
      {
        categoryId: 17,
        parentId: 15,
        categoryLevel: 2,
        categoryName: '家电',
        thirdLevelCategoryVOS: [
          {
            categoryId: 20,
            categoryLevel: 3,
            categoryName: '生活电器'
          },
          {
            categoryId: 110,
            categoryLevel: 3,
            categoryName: 'wer'
          },
          {
            categoryId: 21,
            categoryLevel: 3,
            categoryName: '厨房电器'
          },
          {
            categoryId: 22,
            categoryLevel: 3,
            categoryName: '扫地机器人'
          },
          {
            categoryId: 31,
            categoryLevel: 3,
            categoryName: '空气净化器'
          }
        ]
      },
      {
        categoryId: 19,
        parentId: 15,
        categoryLevel: 2,
        categoryName: '手机',
        thirdLevelCategoryVOS: [
          {
            categoryId: 45,
            categoryLevel: 3,
            categoryName: '荣耀手机'
          },
          {
            categoryId: 57,
            categoryLevel: 3,
            categoryName: 'vivo'
          },
          {
            categoryId: 58,
            categoryLevel: 3,
            categoryName: '手机以旧换新'
          }
        ]
      }
    ]
  },
  {
    categoryId: 16,
    categoryLevel: 1,
    categoryName: '女装 男装 穿搭',
    secondLevelCategoryVOS: [
      {
        categoryId: 67,
        parentId: 16,
        categoryLevel: 2,
        categoryName: '女装',
        thirdLevelCategoryVOS: [
          {
            categoryId: 76,
            categoryLevel: 3,
            categoryName: '外套'
          }
        ]
      }
    ]
  },
  {
    categoryId: 61,
    categoryLevel: 1,
    categoryName: '家具 家饰 家纺',
    secondLevelCategoryVOS: [
      {
        categoryId: 70,
        parentId: 61,
        categoryLevel: 2,
        categoryName: '家具',
        thirdLevelCategoryVOS: [
          {
            categoryId: 77,
            categoryLevel: 3,
            categoryName: '沙发'
          }
        ]
      }
    ]
  }
]
const result = []
console.log(`0. stack-->`, JSON.parse(JSON.stringify(stack)))

// 树的遍历, 层序遍历
let index = 0 // 指针
let current
// 递归会出现爆栈的问题,避免用递归
while ((current = stack[index++]) != null) {
  if (current.categoryLevel == 1) {
    stack.push(...current.secondLevelCategoryVOS)
    console.log(`1. stack-->`, JSON.parse(JSON.stringify(stack)))
  } else if (current.categoryLevel == 2) {
    stack.push(...current.thirdLevelCategoryVOS)
    console.log(`2. stack-->`, JSON.parse(JSON.stringify(stack)))
  } else {
    result.push({ text: current.categoryName, value: current.categoryId })
    console.log(`3. stack-->`, JSON.parse(JSON.stringify(stack)))
  }
}
console.log(`result-->`, result)

进阶参考

你可能感兴趣的:(vue,重返学习,学习,vue.js,前端)