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

news/2024/11/15 6:17:38/

城市选择-基本功能

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

(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'
export default {install(app: App) {app.component('City', City)},
}

提供类型 src/global.d.ts

import XtxCity from '@/components/city/index.vue'
declare module '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)
+const toggle = () => {
+  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)
const toggle = () => {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)定义数据类型

// 城市列表类型
export type AreaList = {code: stringlevel: numbername: stringareaList: 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'
import type { AreaList } from '@/types/goods'
import axios from 'axios'
// 控制弹层的显示隐藏
const active = ref(false)
const toggle = () => {active.value = !active.value
}
const target = ref(null)
onClickOutside(target, (e) => {// console.log(e)// 当点击target元素的外面的时候,就会触发active.value = false
})const cityList = ref<AreaList[]>([])
const getCityList = 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><div class="city" ref="target"><div class="select" :class="{ active: active }" @click="toggle"><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="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)
const toggle = () => {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"><spanclass="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: ''
})const selectCity = (city: AreaList) => {if (city.level === 0) {// 省changeResult.value.provinceName = city.namechangeResult.value.provinceCode = city.codecityList.value = city.areaList}if (city.level === 1) {// 市changeResult.value.cityName = city.namechangeResult.value.cityCode = city.codecityList.value = city.areaList}if (city.level === 2) {// 县(区)changeResult.value.countyName = city.namechangeResult.value.countyCode = city.code// 关闭弹窗active.value = false}
}

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

const cityList = ref<AreaList[]>([])
const cacheList = ref<AreaList[]>([])const getCityData = async () => {const {data:res} = await axios.get<AreaListObj[]>('https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json')cityList.value = rescacheList.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)子组件选择完城市,需要将数据传递给父组件

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

优化代码(可选)

const changeResult = ref<Partial<CityResult>>({})
// record接受两个泛型参数,第一个为对象key的类型,第二个为对象值的类型
const cityMap: Record<number, 'province' | 'city' | 'county'> = {0: 'province',1: 'city',2: 'county'
}
const selectedCity = (city: AreaList) => {changeResult.value[`${cityMap[city["level"]]}Name`] = city.namechangeResult.value[`${cityMap[city["level"]]}Code`] = city.codeif (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('江西省 九江市 不知道县')
const changeCity = (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('江西省 九江市 不知道县')
const changeCity = (changeResult: CityResult) => {userAddress.value =changeResult.provinceName +' ' +changeResult.cityName +' ' +changeResult.countyName
}

http://www.ppmy.cn/news/28877.html

相关文章

Java-扑克牌的创建以及发放

Java-扑克牌的创建以及发放题目&#xff1a;创建一个扑克牌(不需要包含大小王)&#xff0c;分别分发给3个人&#xff0c;一个人发5张牌&#xff0c;输出结果要求包含全套牌(52张牌)&#xff0c;以及3个人各自的牌的花色以及数字。1.扑克牌的源代码2.扑克牌运行结果3.扑克牌代码…

css-居中

line-height 单行文本: 行高等于高度 旁边有img 或者其他行内块元素 vertical-align: middle;水平居中 text-lign: center; /* 行内元素/行内块 */ width: fit-content; /* 宽度适应子元素 */ margin: auto; /* 用得做多的居中方法, 要求有宽度 */ margin: 0 auto; margin: …

亿发软件:钉钉移动ERP业务在线,审批、管理更方便!

钉钉系统是企业级智能移动办公平台&#xff0c;平台覆盖大中小微各量级企业&#xff0c;帮助中国企业移动办公管理。企业无需复杂的部署操作&#xff0c;在对应的功能制定流程和相关负责人即可。 亿发企业ERP管理系统于2022年与钉钉系统做了对接&#xff0c;提供一站式的企业管…

vue 依赖注入使用教程

vue 中的依赖注入&#xff0c;官网文档已经非常详细&#xff0c;笔者在这里总结一份 目录 1、背景介绍 2、代码实现 2.1、依赖注入固定值 2.2、 依赖注入响应式数据 3、注入别名 4、注入默认值 5、应用层 Provide 6、使用 Symbol 作注入名 7、组合式API使用依赖注入 1…

【C++基础入门】初识C++、数据类型

一&#xff1a;C简介 1.1 介绍 C&#xff08;c plus plus&#xff09;是一种计算机高级程序设计语言&#xff0c;由C语言扩展升级而产生 [17] &#xff0c;最早于1979年由本贾尼斯特劳斯特卢普在AT&T贝尔工作室研发。C既可以进行C语言的过程化程序设计&#xff0c;又可以…

OpenHarmony的未来和如何做好一个开源社区

今天要分享的文章&#xff0c;可能更多只是作为一种观点。主要包括2个内容。OpenHarmony的未来和如何做好一个开源社区&#xff0c;好的&#xff0c;接下来开始今天的内容。 你对OpenHarmony的未来如何看待&#xff1f; OpenHarmony的未来看起来非常光明&#xff0c;因为它具…

程序员看过都说好的资源网站,看看你都用过哪些?

程序员必备的相关资源网站一.图片专区1.表情包&#xff08;1&#xff09;发表情&#xff08;2&#xff09;逗比拯救世界&#xff08;3&#xff09;搞怪图片生成&#xff08;4&#xff09;哇咔工具2.图标库&#xff08;1&#xff09;Font Awesome&#xff08;2&#xff09;iconf…

聊一聊过度设计!

文章目录什么是过度设计&#xff1f;过度设计的坏处如何避免过度设计充分理解问题本身保持简单小步快跑征求其他人的意见总结新手程序员在做设计时&#xff0c;因为缺乏经验&#xff0c;很容易写出欠设计的代码&#xff0c;但有一些经验的程序员&#xff0c;尤其是在刚学习过设…