整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

用Python画江苏省地图,实现各地级市数据可视化

用Python画江苏省地图,实现各地级市数据可视化

万万没想到我昨天发布的用Python画中国地图,实现各省份数据可视化这篇文章获得了很多朋友的喜欢,目前已获得了66次转发,314次收藏,也让我涨了60个左右粉丝。

小嘚瑟

虽然这点成绩跟很多大V不能比,但我已经很知足,与此同时也感受到了责任所在,生怕文章内容有误,耽误大家,所以我今天反反复复看昨天的文章,让我欣慰的是的确没有什么错误,但是我个人觉得还是有些点没讲到位,仍有不少可以细说的地方。所以今天再举一个用Python画江苏省地图并实现各地级市数据可视化的例子。

第一步:安装pyecharts

在上篇文章中,我告诉大家我所安装的pyecharts是0.1.9.4版本,但是并没有说为什么,估计很多人也没有当回事,其实这版本号是有讲究的,因为pyecharts在0.3.2版本后,为了缩减项目本身的体积以及维持 pyecharts 项目的轻量化运行,pyecharts 将不再自带地图 js 文件。也就是说如果你安装的pyecharts是0.3.2之后的版本,那么就要根据你想画的地图类型安装相应的模块。

pip install echarts-countries-pypkg          # 世界地图和 213 个国家,包括中国地图
pip install echarts-china-provinces-pypkg    # 23 个省,5 个自治区
pip install echarts-china-cities-pypkg       # 370 个中国城市
pip install echarts-china-counties-pypkg     # 2882 个中国县·区
pip install echarts-china-misc-pypkg         # 11 个中国区域地图,比如华南、华北

此外,不同版本的pyecharts,在代码上的使用也稍有区别,如果你使用的版本和我不一样,那么直接按我代码操作的话,大概率是会报错的。

第二步:读取数据

与上篇文章一样,我的数据放在Excel的表格中,内容是去年(2019年)江苏各地级市的平均房价

HousePrice.xlsx文件

使用xlrd(没有就通过pip install xlrd安装)读取Excel表格中的数据

from pyecharts import Map
import xlrd
# 第一种方式,使用xlrd读取Execel表格中数据
data=xlrd.open_workbook('HousePrice.xlsx')
table=data.sheet_by_name('Sheet1')
city=table.col_values(0)[1:]
num=table.col_values(1)[1:]

如果没有相应的Excel文件,并且嫌麻烦不想新建一个的话呢,也可以直接定义一个字典,其中键是城市名,值是对应的房价,然后再把相应的数据取出来

# 第二种方式,直接自己写一个字典,然后取出相应数据
HousePrice_data={'南京市': 18633.0, '无锡市': 14052.0, '徐州市': 7855.0, '常州市': 16669.0,
                   '苏州市': 21350.0, '南通市': 12326.0, '连云港市': 8463.0, '淮安市': 7038.0,
                   '盐城市': 8455.0, '扬州市': 13418.0, '镇江市': 10367.0, '泰州市': 9147.0, '宿迁市': 7979.0}
city=list(HousePrice_data.keys())
num=list(HousePrice_data.values())

这里需要注意的一点是,每个城市名后面要加上"市"这个字,因为源代码默认规定要有“市”,必须与其对应。(不同于上篇文章,上篇文章中所读取的各省份、直辖市、自治区名称中,并没有添加“省”、“市区”、“自治区”这些后缀)

第三步:画图

JiangSuMap=Map(width=1200, height=600) 
JiangSuMap.add(name="房价信息",
               attr=city,
               value=num,
               visual_range=[7038, 21350],
               maptype='江苏',
               is_visualmap=True,
               visual_text_color='#000')
JiangSuMap.render(path="江苏地图.html")
  • 其中参数name指的是显示在地图正上方的标题,
  • attr就是一个包含了各地级市名称的列表,
  • value就是包含了各地级市房价的列表,
  • visual_range指的是整个数据中的数值范围,
  • maptype就是指的地图类型,
  • is_visualmap代表是否显示颜色
  • 这里需要注意的是,maptype=‘江苏’,不需要加"省"字。另外,我新增了一个参数visual_text_color,该参数是用来显示数字刻度的,细心的朋友会发现上篇文章中所生成的地图左下角并没有数字刻度。

    在运行上面的代码后,会生成一个“江苏地图.html”的文件,打开后如图所示:

    无各地级市名称

    如果想保存为图片,可以点击地图右侧的下载按钮,因为隔着比较远,我在录屏的时候并没有把下载按钮录进来,但大家在自己电脑上肯定会看见的。

    第四步:额外操作

    第一点:大家可以看到上图中各城市名称只有在被鼠标选中的情况下,才能显示,并且所保存的图片也不会显示各城市份名称,如果想显示各城市名称,就要修改html文件,推荐使用notepad++(一个文本编辑软件,直接去百度上下载,很简单的)打开“江苏地图.html”文件,然后在第1923行的那个series中添加"label":{ "normal":{ "show":true}},并保存,如下:

    修改html文件

    然后再打开江苏地图.html,就会显示各地级市名称了,如下图:

    有各地级市名称

    第二点:大家可以看到地图的默认色系是从冷色系过渡到暖色系,也就是从蓝到黄再到红,如果想更改地图显示的颜色,可以添加visual_range_color这个参数,该参数接受一个列表,列表里存放的是颜色过渡信息。比如我存放了三个土黄色系的颜色:#FDAA78、#BB6C31、 #A84104。

    JiangSuMap=Map(width=1200, height=600) 
    JiangSuMap.add(name="房价信息",
                   attr=city,
                   value=num,
                   visual_range=[7038, 21350],
                   maptype='江苏',
                   is_visualmap=True,
                   visual_text_color='#000',
                   visual_range_color=['#FDAA78', '#BB6C31', '#A84104'])
    JiangSuMap.render(path="江苏地图.html")

    上面的代码和第三步中的代码区别就是添加了一个visual_range_color参数,对应的效果如下:

    土黄色系地图

    关注微信公众号“Python小镇”,发现更多干货知识!

    于企业多地点业务而言,SEO 策略与业务策略的协调对于成功至关重要。

    无论企业是经营特许经营模式、零售连锁店,还是以服务区业务的形式运营多个中心,您的本地 SEO方法都需要量身定制,以满足您的特定目标。它还需要具有足够的可扩展性和效率,以便在获得长期投资回报的同时进行维护。

    另一个关键要求是,您的内容方法要为用户和 Google 创造足够的价值,以使其高于索引质量阈值。

    这意味着超越本地 SEO 的标准最佳实践 ,并创建可持续提高品牌知名度和转化率的本地 SEO 活动。

    协调 SEO 与业务战略

    多地点经营的企业有不同的目标。

    虽然多地点管理的基础相同,但您的方法需要与整体战略相结合并与整体业务目标保持一致。

    例如,在多个城镇、城市和州经营服务业务的多家运营商的战略特许经营业务与在多个州拥有数百家分店的大型仓储式商店有所不同。

    成功指标也各不相同。通常,企业本地 SEO 活动的 KPI 属于以下类别之一:

    • 提高各个地点的知名度和客流量。
    • 将本地意向搜索引导至在线商店进行直接投放,或将来与本地商店进行互动。
    • 以上两者的结合。

    企业对“成功”的定义将极大地影响您为用户创建选择架构的方法以及报告成功的方式。

    批量创建本地页面的方法

    多年来,我们描述和制作多区域服务页面的方法发生了变化。

    十年前,我们会将质量低下的版本(只有细微修改且内容基本相同)称为门页,而谷歌随着时间的推移逐渐降低了门页的价值。

    近年来,随着程序化 SEO(pSEO)的日益普及,这种方法已经成为大规模创建这些页面的流行方法。

    本地服务页面的程序化内容创建

    对于运营数百或数千个门店的企业来说,程序化或部分程序化内容创建可能是一个有吸引力的选择。

    程序化 SEO(简称 pSEO)可让您大规模生成大量内容。这种方法已帮助许多企业扩大规模,但如果所创建的页面无法提供足够独特的价值主张,让 Google 不愿投入资源,那么这种方法也会带来问题。

    如果我们看一下本地服务页面的两个常见网站架构,我们通常有一个中央服务页面,然后是本地服务页面,或者有一个充当区域设置服务页面网关的中央页面 - 例如商店定位器。

    图片来自作者,2024 年 7 月

    根据您的业务类型,您可能会默认选择一种结构,但两者都可能带来挑战。

    使用中央服务页面结构,您可能会遇到创建独特价值主张的问题,并确保每个页面都具有足够的差异化并高于 Google 索引的质量阈值。

    商店定位器页面方法可能会导致 PageRank 分布以及内部链接到不同位置的方式出现问题。大多数用户友好的商店位置应用程序不会加载 HTML 链接,因此虽然可以直观地链接到所有商店,但 Google 无法抓取这些链接。

    然而,这两种方法的共同问题是如何捕获位置周围“更广泛”的搜索。

    本地内容价值主张

    当本地页面最适合位置时,它们会发挥最大的帮助。

    从历史上看,我看到一些公司通过在页面上“夸大”该地区的额外信息来做到这一点,比如一两段关于当地基础设施、学校和运动队的段落——如果你想让人们访问你的五金店或询问你的上门安全装配服务,这些信息都与你无关。

    仅仅更改 URL、H1、标题标签和整个正文中的位置名称也是不够的。

    当这种情况发生时,谷歌实际上会看到近似重复的页面,这些页面在与用户查询相关的价值主张上几乎没有区别。

    这种情况的症状是,当页面在 Search Console 中显示为未编入索引时,Google 要么选择覆盖用户声明的规范,要么停留在“已发现”或“已抓取”的阶段,而当前尚未编入索引。

    本地服务和位置页面之间总会存在一定程度的重复。Google 对此并不介意。某些内容在多个页面上重复并不意味着其质量低下。

    创造价值主张差异化

    这是我倾向于采用部分程序化方法的地方。

    程序化可以满足 70% (+) 的页面内容;它可以涵盖您针对特定位置的服务产品、定价和公司信息。

    该页面的剩余百分比是手动的,但允许您创建与其他页面的价值主张差异化。

    假设您是一家跨州快递服务公司,拥有多条市场路线,您在德克萨斯州的主要配送中心位于奥斯汀、圣安东尼奥和达拉斯,而您想要瞄准尤利斯的潜在客户。

    您为尤利斯提供的服务与您为普弗拉格维尔、凯尔和利安德的客户提供的服务相同 - 因此每个位置页面的这些部分在所有位置上都是相同的。

    但是尤利斯由达拉斯枢纽提供服务,而其他机场则由奥斯汀枢纽提供服务——这是您要强调的第一个内容差异点。

    然后,您可以使用来自企业内部的数据和关键字研究,用旅行时间数据充实这些页面。

    在尤利斯寻找快递服务的客户可能正在寻找从尤利斯到奥斯汀或从尤利斯到休斯顿的服务——因此,将此构建到本地页面并提供从目的地到热门地点的时间估算,可以显示出本地的专业性并帮助客户更好地了解服务和计划。

    您的业?务数据还将帮助您识别客户类型。例如,在尤利斯预订的许多工作可能针对的是搬出校园居住的大学生,因此这又是针对可以包含在页面上的客户群的更具本地化的定位。

    内部链接

    当涉及到内部链接时,使用伪 HTML 站点地图可以帮助实现这一点,它不仅可以充当通过页面的干净内部链接,而且还对用户有益,并允许您创建其他登录页面来定位县或地区级搜索。

    十年前,在一个房产查找页面上,我所在的团队构建了“县 > 镇/市”的页面结构模式,同时将相关位置拉入登录页面。

    作者截图,2024 年 7 月

    从视觉上看,这只是一种更“手动”的方法,让用户可以从非特定位置的页面筛选到他们所在的当地区域。

    Google 商业资料链接

    另一个经常被忽视的关键组件是将 Google 商业资料(GBP) 直接链接到网站上的相关位置页面。

    我遇到过许多跨国公司和国内公司,他们链接回自己的公司主页,有时还带有一个参数来突出显示用户点击的是哪个 GBP——但这既是糟糕的网络架构,也是糟糕的用户选择架构。

    如果用户正在寻找 XYZ 中的服务/商店,他们不希望在点击网站链接时看到主页或通用信息页面。

    就用户选择架构而言,从这里,用户可以导航到不同的商店或页面,并错过与他们相关的关键信息,否则这些信息可能会推动销售或询问。

    谷歌的本地算法

    除了 Google 的核心算法和更通用的搜索排名信号外,Google 还发布了专门针对本地查询的更新。主要有两个:

    • Pigeon 2014:此次更新旨在通过将本地搜索结果与一般搜索排名信号更紧密地结合起来,提供更相关、更准确的本地搜索结果。用户接近度(作为信号)也得到了提升。
    • Possum 2016:此次更新旨在提高位于城市边界外的商家的排名,使搜索结果更符合用户与商家的距离。还引入了基于地址的过滤,以避免重复列出共享同一地址的商家(例如虚拟办公室)。

    这些更新使企业更难以欺骗自己在当地市场的存在,并且可能无法提供符合或满足搜索者需求的价值主张。

    据传,谷歌似乎优先对提供最全面信息的企业进行排名。

    这包括开业日期、现场餐饮选择(如果适用)、特殊营业时间、业务类别、服务列表以及定义服务区域和服务类型。

    Google 商业档案的重要性

    遵循指南是必须的,但即便如此,你仍可能会违反 Google 的自动检测检查。

    与一家在亚洲拥有多个办事处的国际软件公司合作,在共享办公室中租用了许多楼层。

    我们假设,Google 偶尔会检测到共享地址并误认为它们是虚拟办公室/虚假地址,而Possum 算法更新旨在减少这种情况。

    当您与拥有大量实际位置的企业组织合作时,通过内部利益相关者管理以及了解 GBP 如何适应并贡献于总体目标和生态系统,Google 商家资料管理方法可能会变得更加复杂。

    报告英镑数据

    根据您的目标,报告成功的方式将因活动而异。

    通过 Google API,您可以访问展示次数的列表级数据,以及不同用户互动的细分(从 GSC 镜像指标推断展示次数和点击次数)。

    非典型的 Google Business Profile 报告仪表板。(作者截图,2024 年 7 月)

    在我看来,任何在多个城镇、城市、县或州运营的企业都需要拥有某种形式的 GBP 监控和报告可见性,而不仅仅是在 Google Search Console 和其他分析平台中跟踪参数化 URL(假设您在 GBP 网站链接上使用参数)。

    市选择页需求展示

    首先明确这是我们即将需要开发的城市选择页面:

    城市选择页路由配置

    在gitee的分支栏点击新建分支city-router,然后记得将本地master分支切换到city-router分支。如何切换呢?步骤如下:第一步:使用git pull将创建的city-router分支拉到本地;第二步:执行git checkout city-router切换到city-router分支;第三步:使用git status查看一下当前的分支状态:(出现这个说明分支切换成功)

    $ git status
    On branch city-router
    Your branch is up to date with 'origin/city-router'.
    
    nothing to commit, working tree clean
    

    接下来开始城市选择页面路由的配置。打开src/router/index.js文件,往其中添加新的url路径:

    import Vue from 'vue'
    import Router from 'vue-router'
    import Home from '@/pages/home/Home'
    import City from '@/pages/city/City'
    
    Vue.use(Router)
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'Home',
          component: Home
        },
        {
          path: '/city',
          name: 'City',
          component: City
        }
      ]
    })
    

    接着去pages下面新建city文件夹,然后在city文件夹中新建一个City.vue文件,里面的代码如下:

    <template>
      <div>city</div>
    </template>
    
    <script>
    export default {
      name: 'City'
    }
    </script>
    
    <style lang="stylus" scoped>
    
    </style>
    

    我们是希望点击右上角的北京也就是城市区域,可以跳转至城市列表页:

    那么需要在Header.vue中进行跳转链接的配置,而前面说过链接跳转使用<router-link>标签结合to属性,则跳转链接区域代码修改为:

        <router-link to="/city">
          <div class="header-right">
            {{ city }}
            <span class="iconfont iconjiantouarrow486 arrow-icon"></span>
          </div>
        </router-link>
    

    to属性中的/city就是我们前面在router/index.js中配置的url链接。但是此时页面跳转区域会变为黑色:

    那是因为<router-link>会默认给元素添加一个颜色:color: #25a4bb;,此时你可以给这个.header-right属性增加color: #fff;使之变成白色就可以解决这个问题了,经过测试发现页面跳转测试通过。

    城市选择页面开发

    由于去哪网城市选择页面包含境内和境外且两者除了内容不同外,其余所有信息均相同,因此本篇笔记只介绍境内城市页面的开发。

    在city文件夹中新建一个components文件,用于存放组件,对于city文件来说City.vue是整个city页面最外层的一个容器组件,里面肯定会嵌套其他的组件,因此把其他小组件都放在components文件中,然后在City.vue中进行引用即可,City.vue中的代码如下:

    <template>
      <city-header></city-header>
    </template>
    
    <script>
    import CityHeader from './components/Header'
    
    export default {
      name: 'City',
      components: {
        CityHeader
      }
    }
    </script>
    
    <style lang="stylus" scoped>
    
    </style>
    

    接着在src/pages/home/compontents文件夹中新建Header.vue文件里面的代码如下:

    <template>
      <div class="header">
        城市选择
        <router-link to="/">
          <div class="iconfont iconfanhui header-icon"></div>
        </router-link>
      </div>
    </template>
    
    <script>
    export default {
      name: 'CityHeader'
    }
    </script>
    
    <style lang="stylus" scoped>
    @import "~styles/varibles.styl"
    
    .header
      position: relative
      overflow: hidden
      height: $headerHeight
      line-height: $headerHeight
      text-align: center
      color: #fff
      background: $bgColor
      font-size: .32rem
      .header-icon
        position: absolute
        top: 0
        left: 0
        width: .64rem
        text-align: center
        font-size: .4rem
        color: #fff
    </style>
    

    注意一下以下代码片段:

    <router-link to="/">
          <div class="iconfont iconfanhui header-icon"></div>
    </router-link>
    

    其实就是点击左边的箭头页面跳转至首页:

    注意到height: $headerHeight,这是因为对于行高和高度而言,我们发现多处地方都是.86rem,因此考虑将其提出单独成为一个变量,因此你可以在tyles/varibles.styl文件中新增:$headerHeight=.86rem即可。

    注意考虑到效率的问题,现在可能是直接把完成后的整个代码粘贴出来,后期会根据情况进行细化介绍。

    最后就是将我们的代码上传到city-router分支,并且将其与master分支进行合并,相应的步骤如下:

    git status
    git add .
    git commit -m 'city router'
    git push
    git checkout master
    git merge city-router
    git push
    

    如此关于城市选择页路由配置就到此为止,后续是搜索框的布局。

    搜索框布局

    在gitee的分支栏点击新建分支city-search,然后记得将本地master分支切换到city-search分支。如何切换呢?步骤如下:第一步:使用git pull将创建的city-search分支拉到本地;第二步:执行git checkout city-search切换到city-search分支;第三步:使用git status查看一下当前的分支状态:(出现这个说明分支切换成功)

    $ git status
    On branch city-search
    Your branch is up to date with 'origin/city-search'.
    
    nothing to commit, working tree clean
    

    接下来开始搜索框页面的配置。在city/components文件夹下面新建Search.vue,里面的初始代码为:

    <template>
    <div>Search</div>
    </template>4
    
    <script>
    export default {
      name: 'CitySearch'
    }
    </script>
    
    <style lang="stylus" scoped>
    
    </style>
    

    接着去City.vue中引入这个组件:

    <template>
      <div>
        ...
        <city-search></city-search>
      </div>
    </template>
    
    <script>
      ...
    import CitySearch from './components/Search'
    
    export default {
      name: 'City',
      components: {
        ...
        CitySearch
      }
    }
    </script>
    
    <style lang="stylus" scoped>
    
    </style>
    

    接下来回到Search.vue文件开始修改样式:

    <template>
      <div class="search">
        <input class="search-input" type="text" placeholder="输入城市名称或者拼音"/>
      </div>
    </template>
    
    <script>
    export default {
      name: 'CitySearch'
    }
    </script>
    
    <style lang="stylus" scoped>
    @import "~styles/varibles.styl"
    .search
      height: .72rem
      padding: 0 .1rem
      background: $bgColor
      .search-input
        box-sizing: border-box
        width: 100%
        height: .62rem
        line-height: .62rem
        padding: 0 .1rem
        text-align: center
        border-radius: 0.06rem
        color: #666
    </style>
    

    之后的效果如图:

    关于box-sizing请点击,或者去W3c上面查阅相关资料。

    最后就是将我们的代码上传到city-search分支,并且将其与master分支进行合并,相应的步骤如下:

    git status
    git add .
    git commit -m 'add search to city'
    git push
    git checkout master
    git merge city-search
    git push
    

    如此关于城市搜索框的介绍完毕,后续是城市列表布局的开发。

    城市列表页开发

    在gitee的分支栏点击新建分支city-list,然后记得将本地master分支切换到city-list分支。在city/components文件夹下面新建List.vue,并在City.vue文件中进行引用。List.vue中的代码如下:

    <template>
      <div class="list">
        <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">
              <div class="button">北京</div>
            </div>
            <div class="button-wrapper">
              <div class="button">上海</div>
            </div>
            <div class="button-wrapper">
              <div class="button">广州</div>
            </div>
            <div class="button-wrapper">
              <div class="button">深圳</div>
            </div>
            <div class="button-wrapper">
              <div class="button">重庆</div>
            </div>
            <div class="button-wrapper">
              <div class="button">天津</div>
            </div>
          </div>
        </div>
        <div class="area">
          <div class="title border-topbottom">A</div>
          <div class="item-list">
            <div class="item border-bottom">阿拉尔</div>
            <div class="item border-bottom">阿拉善盟</div>
            <div class="item border-bottom">阿勒泰</div>
            <div class="item border-bottom">安庆</div>
            <div class="item border-bottom">安顺</div>
          </div>
        </div>
    
        <div class="area">
          <div class="title border-topbottom">A</div>
          <div class="item-list">
            <div class="item border-bottom">阿拉尔</div>
            <div class="item border-bottom">阿拉善盟</div>
            <div class="item border-bottom">阿勒泰</div>
            <div class="item border-bottom">安庆</div>
            <div class="item border-bottom">安顺</div>
          </div>
        </div>
    
        <div class="area">
          <div class="title border-topbottom">A</div>
          <div class="item-list">
            <div class="item border-bottom">阿拉尔</div>
            <div class="item border-bottom">阿拉善盟</div>
            <div class="item border-bottom">阿勒泰</div>
            <div class="item border-bottom">安庆</div>
            <div class="item border-bottom">安顺</div>
          </div>
        </div>
    
        <div class="area">
          <div class="title border-topbottom">A</div>
          <div class="item-list">
            <div class="item border-bottom">阿拉尔</div>
            <div class="item border-bottom">阿拉善盟</div>
            <div class="item border-bottom">阿勒泰</div>
            <div class="item border-bottom">安庆</div>
            <div class="item border-bottom">安顺</div>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'CityList'
    }
    </script>
    
    <style lang="stylus" scoped>
    @import "~styles/varibles.styl"
    /**复写border.css中的 .border-topbottom的样式**/
    .border-topbottom
      &:before
        border-color: #ccc
      &:after
        border-color: #ccc
    .border-bottom
      &:before
        border-color: #ccc
    /**list这个类用于固定标题,也就是说让List组件的顶部外边距腾出固定的位置用于显示Header和Search组件**/
    .list
      overflow: hidden
      position: absolute
      top: 1.58rem
      left: 0
      right: 0
      bottom: 0
      background: red
      .title
        line-height: .44rem
        background: #eee
        padding-left: .2rem
        color: #666
        font-size: .26rem
      .button-list //不添加overflow: hidden那么下面的button就无法撑起这个高度
        overflow: hidden
        padding: .1rem .6rem .1rem .1rem  //右边留出30px像素空间用于字母表显示
        .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
        line-height: .76rem
        color: #666
        padding: .2rem
    </style>
    

    这里面有几个注意点:第一个就是下面的代码:

    .border-topbottom
      &:before
        border-color: #ccc
    

    它用于复写border.css中的.border-topbottom的样式,其中的&:before是Css中的before选择器,:before选择器在被选元素的内容前面插入内容,请使用 content 属性来指定要插入的内容。举个例子,如在每个<p>元素的内容之前插入新内容:

    p:before
    { 
    content:"hello,vuejs!:";
    }
    

    其次就是list这个类用于固定标题,也就是说让List组件的顶部外边距腾出固定的位置用于显示Header和Search组件。仔细看现在的样子:

    现在我们已经把Header和Search组件所占用的空间给让出来了,但是有一个问题就是红色区域是list类所能显示的范围,也就是说红色区域后面的都是显示不了的:

    有人可能会问怎么显示不了,因为红色区域就是<div class="list">所占用的区域背景:

    .list
      position: absolute
      top: 1.58rem
      left: 0
      right: 0
      bottom: 0
      background: red
    

    这样是不行的,我们需要滑动右侧的滚轮使城市列表详细显示出来,这就需要使用到后面介绍的BetterScroll了。

    字母滚动条

    在前面为了保证button撑起可以显示Header和Search组件的高度,我们给.list类添加了以下两行代码:

    .list
      overflow: hidden
      position: absolute
    

    这样就会使得页面无法滚动,想要实现可以滚动的效果可以借助于第三方插件better-scroll,先使用以下命令进行安装better-scroll:npm install better-scroll --save,然后阅读文档发现DOM结构应当类似如下结构:

    <div class="wrapper">
      <ul class="content">
        <li>...</li>
        <li>...</li>
        ...
      </ul>
      <!-- you can put some other DOMs here, it won't affect the scrolling
    </div>
    

    也就是说我们是几个area都放在一个list类中,这是不行的,需要在最外层再添加一个div,将所有的area放在一个div中。阅读英文文档(中文文档点这里):

    因为上面的li就是我们在代码中定义的area,因此需要包裹两层结构:

    继续阅读文档:

    文档的意思是说使用better-scroll其实就是创建Bscroll对象,而这个对象需要传入一个DOM节点作为参数。还记得以前讲过当你需要直接操作某个DOM节点的时候,Vue提供了ref属性,然后你只需要使用$refs来获取DOM节点。

    因此第一步获取到wrapper类节点DOM:

      <div class="list" ref="wrapper">
    

    第二步导入Bscroll类及创建该对象:

    <script>
    import BScroll from 'better-scroll'
    export default {
      name: 'CityList',
      mounted () {
        this.scroll=new BScroll(this.$refs.wrapper)
      }
    }
    </script>
    

    只需这两步就完成了页面滚动的效果:

    之前显示不了的白色区域也全变成了红色区域,现在去掉.list中的background属性。

    现在有一个小的细节就是“当前城市”的高度有点低,可以将.title的line-height属性由.44rem调整为.54rem。

    这样城市列表页的滚动效果就完成了,最后就是将我们的代码上传到city-list分支,并且将其与master分支进行合并,相应的步骤如下:

    git status
    git add .
    git commit -m 'add city scroll'
    git push
    git checkout master
    git merge city-list
    git push
    

    关于better-scroll,可以查看作者的这篇文章:当 better-scroll 遇见 Vue。

    城市字母表

    右侧的城市字母表就是我们即将需要实现的功能:

    在gitee的分支栏点击新建分支city-alphabet,然后记得将本地master分支切换到city-alphabet分支。在city/components文件夹下面新建Alphabet.vue,并在City.vue文件中进行引用。Alphabet.vue中的代码如下:

    <template>
    <ul class="list">
      <li class="item">A</li>
      <li class="item">B</li>
      <li class="item">C</li>
      <li class="item">D</li>
      <li class="item">E</li>
      <li class="item">F</li>
      <li class="item">G</li>
      <li class="item">H</li>
    </ul>
    </template>
    
    <script>
    export default {
      name: 'CityAlphabet'
    }
    </script>
    
    <style lang="stylus" scoped>
    @import "~styles/varibles.styl"
    .list
      display: flex
      flex-direction: column
      justify-content: center
      /** 以上三行代码是让字母表竖直居中 **/
      position: absolute
      top: 1.58rem
      right: 0
      bottom: 0
      width: .4rem
      /*background: red*/
      .item
        /** 以下三行代码是让字母表水平居中和显示正常 **/
        line-height: .4rem
        text-align: center
        color: $bgColor
    </style>
    

    注意几点:1、以下三行代码是让字母表竖直方向上居中:

      display: flex
      flex-direction: column
      justify-content: center
    

    display:flex是一种布局方式,它既可以应用于容器中,也可以应用于行内元素。Flex是Flexible Box的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。了解更多可以点击这里Css display:flex属性。

    flex-direction:表示弹性盒子中的元素的排列顺序。可以按行、列、按行反向排序、或者按列反向排序等。此处我们的字母表是按列排序,不过使用这个参数的前提是需要有display: flex(即将其设置为弹性盒子) 否则无法实现预期效果,了解更多可以点击这里CSS flex-direction属性。

    justify-content:用于设置或检索弹性盒子元素在主轴(横轴)方向上的对齐方式。 如果使用align-content属性对齐交叉轴上的各项(垂直)。其实就是元素的水平对齐方式是左对齐、右对齐、居中对齐、两端对齐还是环绕对齐。此处的字母表是水平居中对齐各个元素,因此应当设为center,了解更多可以点击这里CSS justify-content 属性。

    这样关于字母表的布局就介绍完了,后续是介绍如何将数据动态渲染到页面上。不过在此之前先把本组件的代码上传至gitee上,即将代码上传到city-alphabet分支,并且将其与master分支进行合并,相应的步骤如下:

    git status
    git add .
    git commit -m 'city css finish'
    git push
    git checkout master
    git merge city-alphabet
    git push
    

    页面动态数据渲染

    在gitee的分支栏点击新建分支city-ajax,然后记得将本地master分支切换到city-ajax分支。这里的城市列表信息获取其实和前面首页信息获取非常相似。

    我们在City.vue文件中引入axios且在html页面加载完全后使用生命周期函数 mounted加载Ajax数据。mounted生命周期函数是在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作:

    <template>
      <div>
        <city-header></city-header>
        <city-search></city-search>
        <city-list></city-list>
        <city-alphabet></city-alphabet>
      </div>
    </template>
    
    <script>
    import axios from 'axios'
    import CityHeader from './components/Header'
    import CitySearch from './components/Search'
    import CityList from './components/List'
    import CityAlphabet from './components/Alphabet'
    
    export default {
      name: 'City',
      components: {
        CityHeader,
        CitySearch,
        CityList,
        CityAlphabet
      },
      methods: {
        getCityInfo () {
          axios.get('api/city.json')
            .then(this.getCityInfoResource)
        },
        getCityInfoResource (res) {
          console.log(res)
        }
      },
      mounted () {
        this.getCityInfo()
      }
    }
    </script>
    
    <style lang="stylus" scoped>
    @import "~styles/varibles.styl"
    </style>
    

    运行结果为:

    这里为了将ajax获取到的数据进行展示,我们使用了父子组件正向传值的方式。所有待展示的数据已经准备好了,在static/envy/city.json文件中,里面包括:hotCities、cities。数据展示操作步骤如下,第一步打开City.vue文件(前三部均在该文件内操作),新增数据返回对象data:

    data () {
        return {
          cities: {},
          hotCities: []
        }
      },
    

    第二步修改getCityInfoResource函数代码:

    getCityInfoResource (res) {
          res=res.data
          if (res.ret && res.data) {
            const data=res.data
            this.cities=data.cities
            this.hotCities=data.hotCities
          }
        }
    

    第三步进行数据动态绑定:

    <template>
      <div>
        <city-header></city-header>
        <city-search></city-search>
        <city-list :cities="cities" :hotCities="hotCities"></city-list>
        <city-alphabet :cities="cities"></city-alphabet>
      </div>
    </template>
    

    第四步打开List.vue文件,修改script代码为:

    <script>
    import BScroll from 'better-scroll'
    export default {
      name: 'CityList',
      props: {
        cities: Object,
        hotCities: Array
      },
      mounted () {
        this.scroll=new BScroll(this.$refs.wrapper)
      }
    }
    </script>
    

    且使用文本插值语法展示热门城市:

          <div class="area">
            <div class="title border-topbottom">热门城市</div>
            <div class="button-list">
              <div class="button-wrapper" v-for="item of hotCities" :key="item.id">
                <div class="button">{{ item.name }}</div>
              </div>
            </div>
          </div>
    

    然后继续使用文本插值语法展示所有城市:

    <div class="area" v-for="(item, key) of cities" :key="key">
            <div class="title border-topbottom">{{key}}</div>
            <div class="item-list">
              <div class="item border-bottom" v-for="innerItem of item" :key="innerItem.id">{{innerItem.name}}</div>
            </div>
          </div>
    

    请注意这个cities是一个对象,里面包含了数组,而数组中又包含了对象:

    因此需要使用双层循环,第一层遍历获取诸如'A':[]类的信息,遍历数组的格式是(item,index) of array,而遍历对象的格式是(item,key,index) of object,所以使用对应的格式进行遍历即可。注意双层循环的时候,如果父级的:key与子级的:key值一样是可以的,只要父级的:key不重复,而与子级的:key重复是没有关系的。

    第五步打开Alphabet.vue组件,开始填充城市字母表的数据。先使用props来接收父组件传递的数据:

    <script>
    export default {
      name: 'CityAlphabet',
      props: {
        cities: Object
      }
    }
    </script>
    

    接着就是修改template中的信息了:

    <template>
    <ul class="list">
      <li class="item" v-for="(item,key) of cities" :key="key">{{key}}</li>
    </ul>
    </template>
    

    刷新一下浏览器,最后的城市效果如下:

    最后就是将我们的代码上传到city-ajax分支,并且将其与master分支进行合并,相应的步骤如下:

    git status
    git add .
    git commit -m 'city ajax'
    git push
    git checkout master
    git merge city-ajax
    git push
    

    如此关于城市列表页的内容就介绍完毕,后续是如何实现点击右侧的字母就能跳转至对象的首字母城市,也就是兄弟组件之间的联动。