0


【Vue3】vue3 + ts 封装城市选择组件

城市选择-基本功能

能够封装城市选择组件,并且完成基础的显示隐藏的交互功能

(1)封装通用组件

src/components/city/index.vue
<script lang="ts" setup name="City"></script><template><div class="city"><div class="select"><span class="placeholder">请选择配送地址</span><span class="value"></span><i class="iconfont icon-angle-down"></i></div><div class="option"><span class="ellipsis" v-for="i in 24":key="i">北京市</span></div></div></template><style scoped lang="less">.city {
  display: inline-block;
  position: relative;
  z-index:400;.select {
    border: 1px solid #e4e4e4;
    height: 30px;
    padding:0 5px;
    line-height: 28px;
    cursor: pointer;&.active {
      background: #fff;}.placeholder {
      color: #999;}.value {
      color: #666;
      font-size: 12px;}
    i {
      font-size: 12px;
      margin-left: 5px;}}.option {
    width: 542px;
    border: 1px solid #e4e4e4;
    position: absolute;
    left:0;
    top: 29px;
    background: #fff;
    min-height: 30px;
    line-height: 30px;
    display: flex;
    flex-wrap: wrap;
    padding: 10px;> span {
      width: 130px;
      text-align: center;
      cursor: pointer;
      border-radius: 4px;
      padding:0 3px;&:hover {
        background: #f5f5f5;}}}}</style>

全局注册

import XtxCity from'@/components/city/index.vue'exportdefault{install(app: App){
    app.component('City', City)},}

提供类型

src/global.d.ts
import XtxCity from'@/components/city/index.vue'declaremodule'vue'{
    City:typeof City
  }}

(2)在商品详情组件中渲染city组件

src/views/goods/components/goods-name.vue
<dl><dt>配送</dt><dd>至 <City></City></dd></dl>

(3)控制城市的显示和隐藏

  1. 点击的时候切换省市区的样式
  2. 切换城市弹层的显示或隐藏
<script lang="ts" setup name="City">+import{ ref }from'vue'+const active =ref(false)+consttoggle=()=>{+  active.value =!active.value
+}</script><template><div class="city">+<div class="select"@click="toggle":class="{ active: active }"><span class="placeholder">请选择配送地址</span><span class="value"></span><i class="iconfont icon-angle-down"></i></div>+<div class="option" v-show="active"><span class="ellipsis" v-for="i in 24":key="i">北京市</span></div></div></template>

(4)点击弹层外部,关闭弹层

<script lang="ts" setup>import{ ref }from'vue'+import{ onClickOutside }from'@vueuse/core'const active =ref(false)consttoggle=()=>{
  active.value =!active.value
}// 点击外面,关闭弹窗const target =ref(null)onClickOutside(target,()=>{// 参数1:监听哪个元素,target是通过ref绑定给我们监听盒子的// 参数2:点击了该元素外其他地方触发的函数
  active.value =false})</script><template><div class="city" ref="target"><div class="select"@click="toggle":class="{ active: active }"><span class="placeholder">请选择配送地址</span><span class="value"></span><i class="iconfont icon-angle-down"></i></div><div class="option" v-if="active"><span class="ellipsis" v-for="i in 24":key="i">北京市</span></div></div></template>

城市选择-动态渲染

需求:完成城市数据的获取以及渲染

注意

城市数据并不是直接从接口服务器中获取的,而是从阿里云服务器上获取的数据,所以不能使用封装好的request发送请求,直接使用 原生的axios 发送请求即可。https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.jsonopen in new window

(1)定义数据类型

// 城市列表类型exporttypeAreaList={
  code:string
  level:number
  name:string
  areaList: AreaList[]}

(2)获取数据

src/components/city/index.vue

需求: 从接口获取城市数据,赋值给本地数据

  1. 定义列表数据变量 const cityList = ref<AreaList[]>([])
  2. 封装调接口的方法
  3. 方法内部调用接口,把获取到的结果赋值给cityList
  4. 调用方法
<script lang="ts" setup name="City">import{ ref }from'vue'import{ onClickOutside }from'@vueuse/core'importtype{ AreaList }from'@/types/goods'import axios from'axios'// 控制弹层的显示隐藏const active =ref(false)consttoggle=()=>{
  active.value =!active.value
}const target =ref(null)onClickOutside(target,(e)=>{// console.log(e)// 当点击target元素的外面的时候,就会触发
  active.value =false})const cityList =ref<AreaList[]>([])constgetCityList=async()=>{const res =await axios.get<AreaList[]>('https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json')
  cityList.value = res.data
}getCityList()</script>

(3)渲染数据

src/components/City/index.vue
<template><divclass="city"ref="target"><divclass="select":class="{ active: active }"@click="toggle"><spanclass="placeholder">请选择配送地址</span><spanclass="value"></span><iclass="iconfont icon-angle-down"></i></div><divclass="option"v-show="active"><spanclass="ellipsis"v-for="item in cityList":key="item.code">
        {{ item.name }}
      </span></div></div></template>

(4)点击弹层外部,关闭弹层

<script lang="ts" setup>import{ ref }from'vue'+import{ onClickOutside }from'@vueuse/core'const active =ref(false)consttoggle=()=>{
  active.value =!active.value
}// 点击外面,关闭弹窗const target =ref(null)onClickOutside(target,()=>{// 参数1:监听哪个元素,target是通过ref绑定给我们监听盒子的// 参数2:点击了该元素外其他地方触发的函数
  active.value =false})</script><template><div class="city" ref="target"><div class="select"@click="toggle":class="{ active: active }"><span class="placeholder">请选择配送地址</span><span class="value"></span><i class="iconfont icon-angle-down"></i></div><div class="option" v-if="active"><span class="ellipsis" v-for="i in 24":key="i">北京市</span></div></div></template>

城市选择-交互逻辑

在这里插入图片描述

需求:

  1. 点击选择省市区的时候能够把省市区数据存储起来 1. 根据点击的每一个节点的level决定他应该存储到省/城/区的哪个下面2. 点击的时候,需要把当前弹层城市信息替换为当前点击节点的子节点的城市信息(点击省显示市,点击市显示县)3. 如果点击的是区的节点,则需要关弹层4. 如果中途点击了关闭弹层,则需要重置城市数据

点击某个省,显示省下面的市。点击市,显示市下面的县。

根据level判断级别。

(1)给城市注册点击事件

<div class="option" v-if="active"><span
    class="ellipsis"
    v-for="item in cityList":key="item.code"+@click="selectCity(item)">{{ item.name }}</span></div>

(2)城市切换逻辑

// 选择城市const changeResult =ref({provinceCode:'',provinceName:'',cityCode:'',cityName:'',countyCode:'',countyName:''})constselectCity=(city: AreaList)=>{if(city.level ===0){// 省
    changeResult.value.provinceName = city.name
    changeResult.value.provinceCode = city.code
    cityList.value = city.areaList
  }if(city.level ===1){// 市
    changeResult.value.cityName = city.name
    changeResult.value.cityCode = city.code
    cityList.value = city.areaList
  }if(city.level ===2){// 县(区)
    changeResult.value.countyName = city.name
    changeResult.value.countyCode = city.code
    // 关闭弹窗
    active.value =false}}

(3)关闭时恢复城市数据

const cityList = ref<AreaList[]>([])const cacheList = ref<AreaList[]>([])constgetCityData=async()=>{const{data:res}=await axios.get<AreaListObj[]>('https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json')
  cityList.value = res
  cacheList.value = res
}getCityData()// 监听关闭弹窗的处理,恢复数据watch(active,(value)=>{// 当关闭active的时候,需要回复数据if(!value){
    cityList.value = cacheList.value
  }})

城市选择-完整地址处理

需求描述

  1. 默认展示的配送地址需要从父组件传递过来(登录的用户可以拿到当前用户的默认配送地址)
  2. 子组件选择完省市区需要传递给父组件,由父组件组织数据传给子组件进行展示(默认展示配送地址的数据源在父组件)

注意:完整地址需要父组件传递给子组件,将来如果登录的用户,父组件可以获取到完整的地址。

(1)父组件将城市数据传递给子组件

<script lang="ts" setup>const userAddress =ref('江西省 九江市 不知道县')</script><City :userAddress="userAddress"></City></dd>

(2)子组件接收,并且进行展示

注意点:具体的地址和请选择配送同时只展示一个

defineProps<{
  userAddress?: string
}>()<div class="select" @click="toggle":class="{ active }"><span class="value" v-if="userAddress">{{ userAddress }}</span><span class="placeholder" v-else>请选择配送地址</span><i class="iconfont icon-angle-down"></i></div>

(3)子组件选择完城市,需要将数据传递给父组件

// 选择的城市结果类型exporttypeCityResult={
  provinceCode:string
  provinceName:string
  cityCode:string
  cityName:string
  countyCode:string
  countyName:string}const changeResult =ref<Partial<CityResult>>({})const emit =defineEmits<{(e:'changeCity', value: CityResult):void}>()constselectCity=(city: AreaList)=>{if(city.level ===0){// 省
    changeResult.value.provinceName = city.name
    changeResult.value.provinceCode = city.code
    cityList.value = city.areaList
  }if(city.level ===1){// 市
    changeResult.value.cityName = city.name
    changeResult.value.cityCode = city.code
    cityList.value = city.areaList
  }if(city.level ===2){// 县(区)
    changeResult.value.countyName = city.name
    changeResult.value.countyCode = city.code
    // 关闭弹窗
    active.value =false// 子传父emit('changeCity', changeResult.value)}}

优化代码(可选)

const changeResult = ref<Partial<CityResult>>({})// record接受两个泛型参数,第一个为对象key的类型,第二个为对象值的类型constcityMap: Record<number,'province'|'city'|'county'>={0:'province',1:'city',2:'county'}constselectedCity=(city: AreaList)=>{
  changeResult.value[`${cityMap[city["level"]]}Name`]= city.name
  changeResult.value[`${cityMap[city["level"]]}Code`]= city.code
  if(city.level ===2){setIsShowCity(false)emits('changeCity', changeResult.value)}else{
    cityList.value = city.areaList
  }}

(4)父组件接受数据并且处理

<dl><dt>配送</dt><dd>
    至
    <XtxCity
      @changeCity="changeCity":fullPath="fullPath"></XtxCity></dd></dl>const userAddress =ref('江西省 九江市 不知道县')constchangeCity=(changeResult: CityResult)=>{
  userAddress.value =
    changeResult.provinceName +' '+
    changeResult.cityName +' '+
    changeResult.countyName
}
', changeResult.value)}else{
    cityList.value = city.areaList
  }}

(4)父组件接受数据并且处理

<dl><dt>配送</dt><dd>
    至
    <XtxCity
      @changeCity="changeCity":fullPath="fullPath"></XtxCity></dd></dl>const userAddress =ref('江西省 九江市 不知道县')constchangeCity=(changeResult: CityResult)=>{
  userAddress.value =
    changeResult.provinceName +' '+
    changeResult.cityName +' '+
    changeResult.countyName
}

本文转载自: https://blog.csdn.net/weixin_46862327/article/details/129252653
版权归原作者 itpeilibo 所有, 如有侵权,请联系我们删除。

“【Vue3】vue3 + ts 封装城市选择组件”的评论:

还没有评论