vue實(shí)現(xiàn)城市列表選擇功能
成果展示
最后的成果就是下面所展示的內(nèi)容,因?yàn)間if圖沒(méi)有做,只能截圖所展示,接下來(lái),會(huì)帶著大家一步一步的完成下面功能,腳手架搭建和node安裝在本次案例不會(huì)講解,如果了解,可以在我的博客園找到有詳細(xì)介紹



準(zhǔn)備工作:
引入axios插件,調(diào)用better-scroll第三方插件,本地json文件,可以參考目錄中的city.json,有條件的也可以自己去扒
功能分析
1.獲取json數(shù)據(jù)展示城市列表 。
2.側(cè)邊字母定位滾動(dòng)到相應(yīng)的位置。
3.實(shí)現(xiàn)搜索城市
接下來(lái)我們開(kāi)始對(duì)組件進(jìn)行劃分:本次案例中,總共劃分為五個(gè)組件,下面就是組件的劃分圖

創(chuàng)建city組件,通過(guò)父組件獲取數(shù)據(jù),傳遞給子組件
<template>
<div class="city">
<CityHeader></CityHeader> //頭部
<Search :list="cities"></Search> //搜索
<List :hot="hotCity" :letter="letter" :list="cities"></List> //城市列表
<Alphabet @chang="handleLetterChang" :list="cities"></Alphabet> //A-Z
</div>
</template>
<script>
import axios from 'axios'
import CityHeader from './components/Header'
import Search from './components/Search'
import List from './components/List'
import Alphabet from './components/Alphabet'
export default {
data () {
return {
cities:{}, // 城市列表
hotCity:[], //熱門(mén)城市
letter: '' // A-Z
}
},
components: {
CityHeader,
Search,
List,
Alphabet
},
methods:{
getCityInfo () {
axios.get('/api/city.json').then(this.getCityInfoSucc)
},
getCityInfoSucc(res){
res = res.data
if (res.ret && res.data) {
const data = res.data
this.hotCity = data.hotCities
this.cities = data.cities
}
console.log(this.cities)
},
handleLetterChang(letter) { //接受子組件傳過(guò)來(lái)的
// console.log(letter)
this.letter = letter
}
},
mounted () {
this.getCityInfo ()
}
}
</script>
<style scoped lang="stylus">
</style>

把得到的數(shù)據(jù)分次傳遞個(gè)對(duì)應(yīng)的子組件,這樣有利于網(wǎng)站優(yōu)化,不用頻繁的請(qǐng)數(shù)據(jù)
<template>
<div class="city">
<CityHeader></CityHeader>
<Search :list="cities"></Search>
<List :hot="hotCity" :letter="letter" :list="cities"></List>
<Alphabet @chang="handleLetterChang" :list="cities"></Alphabet>
</div>
</template>
export default {
data () {
return {
cities:{}, // 城市列表
hotCity:[], //熱門(mén)城市
letter: '' // A-Z
}
},
components: {
CityHeader,
Search,
List,
Alphabet
},
methods:{
getCityInfo () {
axios.get('/api/city.json').then(this.getCityInfoSucc) //請(qǐng)求本地配置的mock數(shù)據(jù)
},
getCityInfoSucc(res){
res = res.data
if (res.ret && res.data) {
const data = res.data
this.hotCity = data.hotCities
this.cities = data.cities
}
}
},
mounted () {
this.getCityInfo ()
}
}
創(chuàng)建頭部組件,
<template>
<div class="header">
城市選擇
<router-link to="/">
<div class="iconfont back-icon"></div>
</router-link>
</div>
</template>
<script>
export default {
}
</script>
<style scoped lang="stylus">
@import '~styles/varibles.styl';
@import '~styles/mixins.styl';
.header
overflow: hidden
height $headerHeight
line-height: $headerHeight
text-align: center
color: #fff
background: $bgColor
font-size: .4rem
.back-icon
position: absolute
left: 0
top: 0
width: .64rem
font-size: .4rem
text-align: center
color: #fff
</style>
創(chuàng)建搜索組件頁(yè)面,接受父組件傳遞的數(shù)據(jù),引入better-scroll第三方插件,實(shí)現(xiàn)列表滾動(dòng)
<template>
<div>
<div class="search">
<input v-model="keyword" class="search-input" type="text" placeholder="輸入城市名或者拼音" />
</div>
<div class="search-content" ref="search" v-show="keyword">
<ul>
<li class="serach-item border-bottom" v-for="item in listItem" :key="item.id">{{item.name}}</li>
<li v-show="hasNoData" class="serach-item border-bottom">沒(méi)有搜索到匹配的數(shù)據(jù)</li>
</ul>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
props: {
list: Object,
},
data() {
return {
keyword:'',
listItem:[],
timer:null
}
},
computed: {
hasNoData() {
return !this.listItem.length //沒(méi)有搜索的條件是否顯示
}
},
watch: {
keyword () {
if (this.timer) {
clearTimeout(this.timer)
}
if(!this.keyword) { //清空
this.listItem = ""
return
}
this.timer = setTimeout(() => {
const result = []
for (let i in this.list) {
this.list[i].forEach((value) => { //匹配搜索的條件
if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
result.push(value)
}
})
}
this.listItem= result
},100)
}
},
mounted () {
this.scroll = new BScroll(this.$refs.search)
}
}
</script>
<style scoped lang="stylus">
@import '~styles/varibles.styl'
@import '~styles/mixins.styl'
.search
height: .72rem
padding: 0 .1rem
background:$bgColor
.search-input
box-sizing: border-box
width:100%
height: .62rem
line-height: .62rem
text-align: center
border-radius: .06rem
padding: 0 .1rem
color: #666
.search-content
z-index: 1
overflow:hidden
position:absolute
top: 1.58rem
left: 0
right: 0
bottom: 0
background: #eee
.serach-item
line-height: .62rem
padding-left:.2rem
color:#666
background: #fff
</style>
創(chuàng)建城市列表組件,引入better-scroll插件,實(shí)現(xiàn)列表滾動(dòng),通過(guò)watch監(jiān)聽(tīng)letter,實(shí)現(xiàn)字母與城市列表滾動(dòng)
<template>
<div class="list" ref="wrapper">
<div>
<div class="area">
<div class="title border-topbottom">當(dāng)前城市</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">鄭州</div>
</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">熱門(mén)城市</div>
<div class="button-list">
<div class="button-wrapper" v-for="item in hot" :key="item.id">
<div class="button">{{item.name}}</div>
</div>
</div>
</div>
<div class="area"
v-for="(item,key) in list"
:ref="key"
:key="key">
<div class="title border-topbottom">{{key}}</div>
<ul class="item-list">
<li class="item border-bottom"
v-for="listInner in item"
:key="listInner.id"
>{{listInner.name}}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
props: {
hot: Array,
list: Object,
letter:String
},
mounted () {
this.scroll = new BScroll(this.$refs.wrapper)
},
watch:{
letter () { //監(jiān)聽(tīng)列表滾動(dòng)事件 A-Z
if(this.letter) {
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
}
}
}
</script>
<style scoped lang="stylus">
@import '~styles/varibles.styl';
@import '~styles/mixins.styl';
.border-topbottom
&:before
background: #ccc
&:after
background:#ccc
.border-bottom
&:before
background: #ccc
.list
overflow: hidden
position:absolute
top:1.58rem
left:0
right:0
bottom:0
.title
line-height: .54rem;
background: #eee;
padding-left: .2rem;
color: #666;
font-size: .26rem;
.button-list
overflow:hidden
padding: .1rem .6rem .1rem .1rem
.button-wrapper
float:left
width:33.33%
.button
margin: .1rem
padding: .1rem 0
text-align: center
border: .02rem solid #ccc
border-radius: .06rem
.item-list
.item
line-height: .76rem
color:#212121
padding-left: .2rem
font-size: .28rem
text-overflow: ellipsis
white-space: nowrap
</style>
創(chuàng)建字母組件,點(diǎn)擊字母,左邊列表城市想對(duì)應(yīng),通過(guò)this.$emit事件,子組件在觸發(fā)的事件傳遞給父組件,父組件通過(guò)子組件傳遞的事件,在傳遞給List組件,
<template>
<div class="list">
<li class="item"
:ref="item"
@click="handeClick"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend= "handleTouchEnd"
v-for="item of letter"
:key="item">{{item}}</li>
</div>
</template>
<script>
export default {
props: {
list: Object
},
data () {
return {
touchstart:false,
startY:0,
timer: null
}
},
updated () {
this.startY = this.$refs['A'][0].offsetTop
},
computed: {
letter () {
const letter =[]
for (let i in this.list) { //循環(huán)A-Z
letter.push(i)
}
return letter
}
},
methods: {
handeClick(e) {
this.$emit('chang',e.target.innerText) //傳給父組件City
},
handleTouchStart () {
// 手指放上
this.touchstart = true
},
handleTouchMove (e) {
// 手指移動(dòng)
if(this.touchstart) {
if(this.timer) {
clearInterval(this.timer)
}
this.timer = setTimeout(() => {
const touchY = e.touches[0].clientY -79 //到藍(lán)色頭部的距離
const index = Math.floor((touchY - this.startY ) / 20)
if(index >=0 && index < this.letter.length) {
this.$emit('chang',this.letter[index])
}
},16)
}
},
handleTouchEnd () {
// 手指離開(kāi)
this.touchstart = false
}
}
}
</script>
<style scoped lang="stylus">
@import '~styles/varibles.styl';
@import '~styles/mixins.styl';
.list
display: flex
flex-direction:column
justify-content: center
position:absolute
top: 1.58rem
right: 0
bottom: 0
width: .4rem
.item
line-height:.44rem
text-align: center
color: $bgColor
list-style:none
</style>
總結(jié)
以上所述是小編給大家介紹的vue實(shí)現(xiàn)城市列表選擇功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Vue proxyTable配置多個(gè)接口地址,解決跨域的問(wèn)題
這篇文章主要介紹了Vue proxyTable配置多個(gè)接口地址,解決跨域的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
解決獲取數(shù)據(jù)后this.$refs.xxx.toggleRowSelection無(wú)效的問(wèn)題
這篇文章主要介紹了解決獲取數(shù)據(jù)后this.$refs.xxx.toggleRowSelection無(wú)效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
Vue動(dòng)態(tài)組件component標(biāo)簽的用法大全
這篇文章主要介紹了Vue動(dòng)態(tài)組件component標(biāo)簽的用法,在Vue中,可以通過(guò)component標(biāo)簽的is屬性動(dòng)態(tài)指定標(biāo)簽,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08
Vue實(shí)現(xiàn)返回頂部按鈕實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Vue實(shí)現(xiàn)返回頂部按鈕的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Ant Design Vue pro 動(dòng)態(tài)路由的實(shí)現(xiàn)和打包方式
這篇文章主要介紹了Ant Design Vue pro 動(dòng)態(tài)路由的實(shí)現(xiàn)和打包方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
Vue 路由組件向app.vue主文件傳值的方式(兩種常見(jiàn)方式)
在Vue.js中,可以使用路由傳參的方式向App.vue主頁(yè)面?zhèn)鬟f數(shù)據(jù),有多種方法可以實(shí)現(xiàn)這一目標(biāo),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-11-11
Vue頁(yè)面反復(fù)刷新的常見(jiàn)問(wèn)題及解決方案
Vue.js 是一個(gè)流行的前端框架,旨在通過(guò)其響應(yīng)式的數(shù)據(jù)綁定和組件化的開(kāi)發(fā)模式簡(jiǎn)化開(kāi)發(fā),然而,在開(kāi)發(fā) Vue.js 應(yīng)用時(shí),頁(yè)面反復(fù)刷新的問(wèn)題可能會(huì)對(duì)用戶體驗(yàn)和開(kāi)發(fā)效率產(chǎn)生負(fù)面影響,本文將深入探討 Vue 頁(yè)面反復(fù)刷新的常見(jiàn)原因,并提供詳細(xì)的解決方案2024-09-09
vue-cli3 取消eslint校驗(yàn)代碼的解決辦法
這篇文章主要介紹了vue-cli3 取消eslint校驗(yàn)代碼的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01

