首先, 讲述一下这个组件需要实现的需求:

1. 在页面显示完整用户选择的省市区信息

2. 此组件是作用在别的组件里面, 接收父组件传入完整的省市区信息; 提供模板中使用

3. 根据后端规定的字段名定义一个对象; 将用户修改后的省市区数据放入这个对象中, 供需要的父组件使用

有三种情况, 父组件会向此组件传入完整的省市区信息

我所讲述的这一个组件是应用在一个电商平台所需要的省市区联动组件

1. 用户没有登录时, 父组件传入一个定死的收货地址信息

2. 用户登录后, 父组件传入从数据库中获取当前登录账号的默认收货地址信息

3. 用户修改后, 因为所有的省市区数据都是在此组件里面; 所以记录用户修改后的省市区数据会 emit 给父组件; 由父组件传给此组件, 然后此组件通过 props 接收提供给模板使用

为什么第三种情况这样做, 是因为第一种和第二种情况都是父组件传给此组件一个完整的省市区数据; 这就说明, 此组件如何显示省市区都是有父组件来决定的

所以, 此组件里面不能直接进行修改, 而是由父组件传入

好了, 说了些基本逻辑; 大家可能还是一头雾水, 不知道我在说啥, 现在就上代码显示

首先, 完成基本布局

布局分析:

1. 大盒子里面包含两个子盒子, 一是显示配送地址信息盒子; 二是所有省市区信息盒子

2. 配送地址信息盒子里面就是两个 span , 一个是显示信息盒子; 另外一个是 icon

<template><div class="xtx-city"><!-- 默认显示或选择完毕的元素 --><div class="select"><!-- 默认显示的span信息 --><span class="placeholder">请选择配送地址</span><!-- 用户选择完毕之后替换掉默认显示的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><script>
export default {name: 'XtxCity'
}
</script><style scoped lang="less">
.xtx-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>

现在来完成基本的交互

思路分析:

1. 定义控制省市区盒子(option), 显示隐藏的变量(active); 默认值为 false

2. 定义 option 元素显示的方法(open), 将 active 变量值变成 true

3. 定义 option 元素隐藏的方法(close), 将 active 变量值变成 false

4. 定义一个调用 open 和 close 的方法(toggleOption), 通过判断 active 的值; 动态的调用 open 和close方法

5. 当用户点击页面其他地方的时候, 应当将 option 元素隐藏

<template><div class="xtx-city" ref="target"><div class="select" @click="toggleOption" :class="{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><script>
import { ref } from 'vue'
import { onClickOutside } from '@vueuse/core'
export default {name: 'XtxCity',setup () {// 控制option选项显示隐藏变量, 默认隐藏const active = ref(false)// 展开option元素方法const open = () => {active.value = true}// 关闭option元素方法const close = () => {active.value = false}// 根据active状态来改变options元素的显示隐藏const toggleOption = () => {if (active.value) close()else open()}// 点击其他地方, 关闭option元素方法const target = ref(null)onClickOutside(target, () => {close()})return { active, toggleOption, target }}
}
</script><style scoped lang="less">
.xtx-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;}}......
}
</style>

最后完成省市区联动的逻辑交互

思路分析:

1. 首先封装调用接口函数, 获取所有的省市区数据(使用的是阿里的省市区json数据)

2. 用户可以频繁的进行点击, 所以; 需要做缓存

3. 定义变量(cityList), 接收返回回来的数据(在数据还没有返回时, 显示loading效果)

4. 在 open 方法被调用的时候, 调用接口函数; 向 cityList 赋值(cityList的值是全部的初始的数据, 页面需要的数据是计算属性计算得到的数据)

5. 定义后端需要的字段对象(changeResult), 其中有省市区的地域编号和名称

6. 定义一个方法(changeOption), 用户点击进行选择时; 将用户选择的当前数据传给此方法

7. changeOption 方法内判断, 用户点击的是省或市或区; 对 changeResult 对象中的字段进行赋值

8. 使用计算属性(currList), 内部再返回一个变量(currList), 通过 changeResult 对象中的省市区的地域编号动态的改变 currList 的值; 最终 currList 提给给模板渲染数据

9. 用户选择到区一级时, 将完整的省市区数据 emit 给父组件, 调用 close 方法

10. 用户再次进行修改省市区数据时, 情况 changeResult 对象中先前的数据

11. 当用户选择错误时, 点击页面其他地方; 需要对 changeResult 对象中的数据进行重置

<template><div class="xtx-city" ref="target"><div class="select" @click="toggleOption" :class="{active}"><span class="placeholder" v-if="!fullLocation">请选择配送地址</span><span class="value" v-else>{{ fullLocation }}</span><i class="iconfont icon-angle-down"></i></div><div class="option" v-if="active"><!-- loading效果 --><div v-if="loading" class="loading"></div><template v-else><span class="ellipsis" @click="changeOption(item)" v-for="item in currList" :key="item.code">{{ item.name }}</span></template></div></div>
</template><script>
import { ref, computed, reactive } from 'vue'
import { onClickOutside } from '@vueuse/core'
import axios from 'axios'
export default {name: 'XtxCity',props: {fullLocation: {type: String,default: ''}},setup (props, { emit }) {// 1. 获取省市区数据方法(初始数据)const getCityData = () => {const url = 'https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json'return new Promise((resolve, reject) => {// 2. 做缓存// 有缓存if (window.cityData) {resolve(window.cityData)} else {// 没有缓存axios.get(url).then(({ data }) => {window.cityData = dataresolve(window.cityData)})}})}// 3. 存储获取的省市区数据const cityList = ref([])// 当数据还在加载时, 显示loading效果const loading = ref(false)// 4. 展开option元素方法const open = () => {active.value = trueloading.value = truegetCityData().then(res => {cityList.value = resloading.value = false}).catch(err => err)// 10. 再次打开的时候, 清空先前的数据for (const key in changeResult) {changeResult[key] = ''}}// 5. 定义依据后端字段需要的数据和父组件需要的数据const changeResult = reactive({// 省地域编号和名称provinceCode: '',provinceName: '',// 市地域编号和名称cityCode: '',cityName: '',// 区地域编号和名称countyCode: '',countyName: '',// 完整的省市区数据fullLocation: ''})// 6. 修改省市区数据的方法const changeOption = (item) => {// 7. 用户点击当前的省数据if (item.level === 0) {changeResult.provinceCode = item.codechangeResult.provinceName = item.name} else if (item.level === 1) {changeResult.cityCode = item.codechangeResult.cityName = item.name} else {changeResult.countyCode = item.codechangeResult.countyName = item.name// 拼接好完整的数据changeResult.fullLocation = `${changeResult.provinceName} ${changeResult.cityName} ${changeResult.countyName}`// 9. 提供给父组件使用emit('change', changeResult)close()}}// 8. 根据当前的省市区数据, 获取对应的下一级数据列表const currList = computed(() => {// 全部数据(不能直接的去影响cityList的数据, 重新定义变量; 对此变量进行修改即可)let currList = cityList.value// 用户选择省数据// 主要是判断changeResult中省市区的地域编码是否存在; 从而动态的改变的currList值// 且 currList 是模板渲染的主要数据, 所以用户选择的数据会随着选择变化而变化if (changeResult.provinceCode) {currList = currList.find(item => item.code === changeResult.provinceCode).areaList}// 用户选择当前省的市数据if (changeResult.cityCode) {currList = currList.find(item => item.code === changeResult.cityCode).areaList}// 用户选择当前市的区数据if (changeResult.countyCode) {currList = currList.find(item => item.code === changeResult.countyCode).areaList}return currList})// 11. 点击其他地方, 关闭option元素方法const target = ref(null)onClickOutside(target, () => {close()// 重置数据for (const key in changeResult) {changeResult[key] = ''}})// 控制option选项显示隐藏变量, 默认隐藏const active = ref(false)// 根据active状态来改变options元素的显示隐藏const toggleOption = () => {if (active.value) close()else open()}// 关闭option元素方法const close = () => {active.value = false}return { active, toggleOption, target, loading, cityList, currList, changeOption, changeResult }}
}
</script><style scoped lang="less">
.xtx-city {.......option {.......loading {height: 290px;width: 100%;background: url(../../assets/images/loading.gif) no-repeat center;}}
}
</style>

父组件代码

<template><p class="g-name">{{ goods.name }}</p><p class="g-desc">{{ goods.desc }}</p><p class="g-price"><span>{{ goods.price }}</span><span>{{ goods.oldPrice }}</span></p><div class="g-service"><dl><dt>促销</dt><dd>12月好物放送,App领券购买直降120元</dd></dl><dl><dt>配送</dt><dd>至 <XtxCity @change="changeCity " :fullLocation="fullLocation" /></dd></dl><dl><dt>服务</dt><dd><span>无忧退货</span><span>快速退款</span><span>免费包邮</span><a href="javascript:;">了解详情</a></dd></dl></div>
</template><script>
import { ref } from 'vue'
export default {name: 'GoodName',props: {goods: {type: Object,default: () => {}}},setup (props) {// 当用户没有登录的时候, 需要显示默认的数据const provinceCode = ref('110000')const cityCode = ref('119900')const countyCode = ref('110101')const fullLocation = ref('北京市 市辖区 东城区')// 判断用户时候有登录if (props.goods.userAddresses) {// 如果存在, city组件就需要渲染用户的默认收货地址信息const defaultAddr = props.goods.userAddresses.find(addr => addr.isDefault === 1)if (defaultAddr) {provinceCode.value = defaultAddr.provinceCodecityCode.value = defaultAddr.cityCodecountyCode.value = defaultAddr.countyCodefullLocation.value = defaultAddr.fullLocation}}// 接收子组件传入的数据const changeCity = (data) => {provinceCode.value = data.provinceCodecityCode.value = data.cityCodecountyCode.value = data.countyCodefullLocation.value = data.fullLocation}return { fullLocation, changeCity }}
}
</script><style lang="less" scoped>
.g-name {font-size: 22px
}
.g-desc {color: #999;margin-top: 10px;
}
.g-price {margin-top: 10px;span {&::before {content: "¥";font-size: 14px;}&:first-child {color: @priceColor;margin-right: 10px;font-size: 22px;}&:last-child {color: #999;text-decoration: line-through;font-size: 16px;}}
}
.g-service {background: #f5f5f5;width: 500px;padding: 20px 10px 0 10px;margin-top: 10px;dl {padding-bottom: 20px;display: flex;align-items: center;dt {width: 50px;color: #999;}dd {color: #666;&:last-child {span {margin-right: 10px;&::before {content: "•";color: @xtxColor;margin-right: 2px;}}a {color: @xtxColor;}}}}
}
</style>

组件封装 - 省市区联动组件相关推荐

  1. Ant Design Cascader组件实现联级组件实现省市区联动

    Ant Design Cascader 联级组件实现省市区联动 <a-form ref="formRegister" :form="form" id=&q ...

  2. php 小程序自定义图,微信小程序之如何使用自定义组件封装原生 image 组件

    零.问题的由来 一般在前端展示图片时都会碰到这两个常见的需求: 图片未加载完成时先展示占位图,等到图片加载完毕后再展示实际的图片. 假如图片链接有问题(比如 404),依然展示占位图.甚至你还可以增加 ...

  3. 基于腾讯地图+Ant-Design-Vue封装省市区联动查询组件

    一.腾讯地图key申请 附上教程:腾讯地图key申请 二.在项目加载JS API 在VUE项目的pubilc文件夹下的index.html中加入 <script src="https: ...

  4. 组件封装 - 面包屑组件

    我们主要分为三个部分: 1. 初级的面包屑 2. 高阶的面包屑(过渡) 3. 高阶的面包屑(终极) 现在就来封装一个最简易的面包屑组件 简易面包屑只能兼容两级, 如果说有两级以上的; 这样的面包屑组件 ...

  5. Vue组件封装 ——input 输入框组件

      一.基础准备工作 1.创建一个基础的vue项目包 2.创建components文件夹,用于存放组件,新建input.vue组件,可以自己取个名字 <script> export def ...

  6. Vue组件封装 ——dialog对话框组件

     一.基础准备工作 1.创建一个基础的vue项目包 2.创建components文件夹,用于存放组件,新建dialog.vue组件,可以自己取个名字 <script> export def ...

  7. 微信小程序-收货地址 省市区联动 组件

    简单记录直接上代码思路自己理: addressEditor.wxml <!--pages/my/my-add-address/index.wxml--><view class=&qu ...

  8. 组件封装 - 骨架屏组件

    骨架屏组件的主要作用就是用来, 当后端的数据还没有返回的时候; 页面的数据还是空白的, 当后端数据加载完成之后. 现在用户就会看见一个效果就是 "闪屏" 效果, 原本这个地方是空白 ...

  9. Vue + Element UI 实现权限管理系统 前端篇(七):功能组件封装

    组件封装 为了避免组件代码的臃肿,这里对主要的功能部件进行封装,保证代码的模块化和简洁度. 组件结构 组件封装重构后,试图组件结构如下图所示 代码一览 Home组件被简化,包含导航.头部和主内容三个组 ...

最新文章

  1. iOS-查询数据库--指定数据表中的当前数据行的总数量
  2. 发条js调试工具_小工具大帮手,利用 @open-node/antman 实现 node.js 进程线上调试,无须重启...
  3. 事件监听 || v-on参数
  4. 程序直接控制I/O方式
  5. 实现带下拉菜单的工具栏按钮
  6. 【最优解法】1054 求平均值 (20分)_31行代码AC
  7. python编程学习_使用EduBlocks轻松学习Python编程
  8. HTML+CSS制作彩色波动
  9. psd imageset转换工具
  10. java解析excel 哪个好_java解析excel
  11. MQL5 COOKBOOK: 获取仓位属性
  12. 微信小程序:老人疯狂裂变引流视频推广微信小程序
  13. 老王利用这个方法引流3个满微信号,半年变现几十万,你学会了你也能做到
  14. 卫生事业单位计算机科学与技术,卫生事业单位考试-计算机科学与技术的知识点.doc...
  15. latex 行间公式大小(批量设置)
  16. VTK交互系统 2 交互器样式
  17. 扫码登录的安全性分析
  18. 逐行分析鸿蒙系统的 JavaScript 开发框架
  19. 多元统计分析最短距离法_多元统计分析第10讲(聚类分析:动态聚类)
  20. g729编码 c语言,非常流行的C语言实现的G.729压缩算法

热门文章

  1. java必会单词_java必会的英语单词
  2. 金融学期末复习重点准备
  3. educoder中Spark GraphX—构建图及相关操作
  4. GPT格式的磁盘扩容
  5. Gradle 配置阿里云代理 https,maven central 中央仓库,google,gradle plugin,jcenter
  6. Go: 模拟一张银行卡存、取、查的功能(综合练习)
  7. 查看手机应用最大运行内存
  8. pta中c语言编程问题答案,pta题库答案_浙大远程教育2015秋 程序设计基础C 求PTA实验代码题库liujiahai-C答案_淘题吧...
  9. 可怕!9岁男孩为买任天堂游戏机,竟然...
  10. 奔驰S400商务型升级前排通风座椅系统,夏天必备的功能