• vue实现城市列表选择


    成果展示

    最后的成果就是下面所展示的内容,因为gif图没有做,只能截图所展示,接下来,会带着大家一步一步的完成下面功能,脚手架搭建和node安装在本次案例不会讲解,如果了解,可以在我的博客园找到有详细介绍

    准备工作:

     引入axios插件,调用better-scroll第三方插件,本地json文件,可以参考目录中的city.json,有条件的也可以自己去扒

    功能分析

    1.获取json数据展示城市列表 。

    2.侧边字母定位滚动到相应的位置。

    3.实现搜索城市

    接下来我们开始对组件进行划分:本次案例中,总共划分为五个组件,下面就是组件的划分图

    创建city组件,通过父组件获取数据,传递给子组件

    <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:[], //热门城市
                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) { //接受子组件传过来的
    //            console.log(letter)
                this.letter = letter
            }
        },
        mounted () {
            this.getCityInfo ()
        }
    }
    </script>
    
    <style scoped lang="stylus">
    
    </style>
    City组件

    把得到的数据分次传递个对应的子组件,这样有利于网站优化,不用频繁的请数据

    <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:[], //热门城市
                letter: ''  // A-Z
            }
        },
        components: {
            CityHeader,
            Search,
            List,
            Alphabet
        },
        methods:{
            getCityInfo () {
                axios.get('/api/city.json').then(this.getCityInfoSucc)   //请求本地配置的mock数据
            },
            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 ()
        }
    }

    创建头部组件,

    <template>
        <div class="header">
            城市选择
            <router-link to="/">
                <div class="iconfont back-icon">&#xe624;</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
             .64rem
            font-size: .4rem
            text-align: center
            color: #fff
    </style>

    创建搜索组件页面,接受父组件传递的数据,引入better-scroll第三方插件,实现列表滚动

    <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">没有搜索到匹配的数据</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  //没有搜索的条件是否显示
            }
        },
        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
            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>

    创建城市列表组件,引入better-scroll插件,实现列表滚动,通过watch监听letter,实现字母与城市列表滚动

    <template>
        <div class="list" ref="wrapper">
            <div>
                <div class="area">
                    <div class="title border-topbottom">当前城市</div>
                    <div class="button-list">
                        <div class="button-wrapper">
                            <div class="button">郑州</div>
                        </div>
                    </div>
                </div>
                <div class="area">
                    <div class="title border-topbottom">热门城市</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 () {  //监听列表滚动事件 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
                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>

    创建字母组件,点击字母,左边列表城市想对应,通过this.$emit事件,子组件在触发的事件传递给父组件,父组件通过子组件传递的事件,在传递给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) { //循环A-Z
                    letter.push(i)
                }
                return letter
            }
        },
        methods: {
            handeClick(e) {
                this.$emit('chang',e.target.innerText) //传给父组件City
            },
            handleTouchStart () {
                // 手指放上
                this.touchstart = true
            },
            handleTouchMove (e) {
                // 手指移动
                if(this.touchstart) {
                    if(this.timer) {
                        clearInterval(this.timer)
                    }
                    this.timer = setTimeout(() => {
                        const touchY = e.touches[0].clientY -79   //到蓝色头部的距离
                        const index = Math.floor((touchY - this.startY ) / 20)
                        if(index >=0 && index < this.letter.length) {
                            this.$emit('chang',this.letter[index])
                        }
                    },16)
                }
            },
            handleTouchEnd () {
                // 手指离开
                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
         .4rem
        .item
            line-height:.44rem
            text-align: center
            color: $bgColor
            list-style:none
    </style>

     以上都是所有本次的内容,如果喜欢可以关注一下

  • 相关阅读:
    Android连载7-动语添加碎片
    JavaScript连载6-转化为Number和Boolean类型、运算符
    Java连载111-timer定时器、反射机制概述
    用conda创建虚拟环境的一些常用命令
    Java内存分析
    Java语言中的Class类
    线程协作
    LeetCode刷题笔记第26题
    LeetCode刷题笔记第20题(括号匹配)
    LeetCode刷提笔记第1332题
  • 原文地址:https://www.cnblogs.com/zhoulifeng/p/9316490.html
Copyright © 2020-2023  润新知