组件封装 - 省市区联动组件
首先, 講述一下這個組件需要實現的需求:
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>??
總結
以上是生活随笔為你收集整理的组件封装 - 省市区联动组件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android8.1 吉字节问题
- 下一篇: python爬取墨迹天气的8月份的温度情