其他分享
首页 > 其他分享> > vue3项目-小兔鲜儿笔记-商品详情页01

vue3项目-小兔鲜儿笔记-商品详情页01

作者:互联网

1. 基础布局

完成商品详情基础布局,路由配置,搭好页面架子

 

 

2. 渲染面包屑

编写一个钩子函数useGoods.js,将面包屑获取数据的逻辑抽取出来。

// 拿到商品信息
import { findGoods } from '@/api/product'
import { nextTick, ref, watch } from 'vue'
import { useRoute } from 'vue-router'

const useGoods = () => {
  const route = useRoute()

  const goods = ref(null)

  watch(
    () => route.params.id,
    (newVal) => {
      // 先判断路由是否为/product/id,是这个路由才发送查找商品详情的请求
      if (newVal && `/product/${newVal}` === route.path) {
        findGoods(newVal).then(({ result }) => {
          // 先置空,让依赖于goods的组件(v-if=goods)这些组件可以先销毁再创建
          goods.value = null
          nextTick(() => {
            goods.value = result
          })
        })
      }
    },
    { immediate: true }
  )

  return {
    goods
  }
}

export default useGoods

// 面包屑组件中使用:
// goods-bread:
const { goods } = useGoods()

 

3.图片放大镜

// 大图容器  
.large {
    position: absolute;
    top: 0;
    left: 412px;
    width: 400px;
    height: 400px;
    box-shadow: 0 0 10px rgba(0, 8, 8, 0.1);
    background-repeat: no-repeat;
    background-size: 800px 800px;
    background-color: #f8f8f8;
  }

// 遮罩容器
 .layer {
      position: absolute;
      left: 0;
      top: 0;
      width: 200px;
      height: 200px;
      background: rgba(0, 0, 0, 0.2);
    }

 

抽取图片放大镜逻辑到usePreviewImage.js中,使得图片预览组件没有那么臃肿

初始数据:
  // 要观察的容器
  const target = ref(null)
  // 是否显示遮罩层和大图背景
  const show = ref(false)
  // 遮罩层坐标(左上角),因为遮罩层是绝对定位,设置成left、top
  const layerPosition = reactive({
    left: 0,
    top: 0
  })
  // 大图背景坐标(左上角),因为大图是背景定位,设置成backgroundPositionX
  const largeImagePosition = reactive({
    backgroundPositionX: 0,
    backgroundPositionY: 0
  })

大致步骤:

  // elementX:鼠标基于容器X轴的偏移量
  // elementY: 鼠标基于容器Y轴的偏移量
  // isOutside: 鼠标是否在容器外部,这三个数据都是响应式变量
  const { elementX, elementY, isOutside } = useMouseInElement(target)
  // 监听这三个数据,让遮罩层坐标和大图背景坐标随着偏移量的变化而变化
  watch([elementX, elementY, isOutside], () => {
    show.value = !isOutside.value
    // 让鼠标位于遮罩层中心,就是让遮罩层左移和上移
    // 遮罩层的坐标范围:0~200,大图背景坐标范围:-300~-100,因为大图背景随着鼠标移动而移动,鼠标往右,背景往左移
    layerPosition.left = elementX.value - 100 + 'px'
    layerPosition.top = elementY.value - 100 + 'px'
    largeImagePosition.backgroundPositionX = -elementX.value + 'px'
    largeImagePosition.backgroundPositionY = -elementY.value + 'px'
    if (elementX.value <= 100) {
      layerPosition.left = 0
      largeImagePosition.backgroundPositionX = '-100px'
    }
    if (elementY.value <= 100) {
      layerPosition.top = 0
      largeImagePosition.backgroundPositionY = '-100px'
    }
    if (elementX.value >= 300) {
      layerPosition.left = '200px'
      largeImagePosition.backgroundPositionX = '-300px'
    }
    if (elementY.value >= 300) {
      layerPosition.top = '200px'
      largeImagePosition.backgroundPositionY = '-300px'
    }
  })

 

4. 城市组件的封装

初始数据:
const target = ref(null)
const visible = ref(false) // 控制窗口是否显示
const loading = ref(false) // 数据加载中状态
const cityData = ref([]) // 地区数组
// 根据点击的地区列表的按钮动态更改选择的地区结果
const changeResult = reactive({
  provinceCode: '',
  provinceName: '',
  cityCode: '',
  cityName: '',
  countyCode: '',
  countyName: '',
  fullLocation: ''
})
// 获取地址数据
// 1.地址在窗口打开的时候才获取
// 2.获取了地址之后要做全局缓存,因为数据量太大
// 3.获取了缓存之后下次打开窗口就直接从缓存中取,因此数据有可能是异步也有可能是同步获取,需要使用promise判断
const getCityData = () => {
  return new Promise((resolve, reject) => {
    if (window.cityData) {
      resolve(window.cityData)
    } else {
      const url =
        'https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json'
      axios.get(url).then(({ data }) => {
        window.cityData = data
        resolve(data)
      })
    }
  })
}
开关窗口的逻辑
const open = () => {
  visible.value = true
  // 获取地址数据
  loading.value = true
  getCityData().then((res) => {
    cityData.value = res
    loading.value = false
  })
}
const close = () => {
  visible.value = false
}
// 控制窗口是否显示
const toggleDialog = () => {
  visible.value ? close() : open()
}
// 当鼠标点击外部,关闭窗口
onClickOutside(target, () => {
  close()
})
点击地区组件的逻辑
// 点击地区列表的按钮时,根据点的是省还是市还是区做判断
const changeItem = (item) => {
  if (item.level === 0) {
      // 如果点击的是省,就拿到这个省的代码
    changeResult.provinceCode = item.code
    changeResult.provinceName = item.name
  }
  if (item.level === 1) {
      // 如果是点击的是市,就拿到这个市的代码
    changeResult.cityCode = item.code
    changeResult.cityName = item.name
  }
  // 如果已经点到区了,就说明是地址已经全部点完,可生成完整地址,并通知父组件地址已改变
  if (item.level === 2) {
    changeResult.countyCode = item.code
    changeResult.countyName = item.name
    changeResult.fullLocation = `${changeResult.provinceName} ${changeResult.cityName} ${changeResult.countyName}`
    // 把点击完生成的地址结果传给父组件
    emit('change', changeResult)
    close()
  }
}
地区列表的显示:
// 地区列表中显示的不一定是省的数据,可能会是市和区
// 但是会依赖于cityData来动态显示,根据已动态更改的地区结果改变渲染的地区列表
const currList = computed(() => {
  let list = cityData.value // 一开始默认展示的省列表
  // 可能是市,如果点击了某个省,就通过省代码找到这个省,获得这个省下的市列表进行展示
  if (changeResult.provinceCode) {
    list = list.find(
      (province) => province.code === changeResult.provinceCode
    ).areaList
  }
  // 可能是区,如果点击了某个市,就通过市代码找到这个市,获得这个市下的区列表进行展示
  if (changeResult.cityCode) {
    list = list.find((city) => city.code === changeResult.cityCode).areaList
  }
  return list
})

 

标签:遮罩,01,const,鼠标,容器,鲜儿,value,详情页,changeResult
来源: https://www.cnblogs.com/jzhFlash/p/16656487.html