转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 需求
本篇,我们要完成城市选择页剩余部分页面的布局,包括搜索输入框、城市选择(包含“当前城市”、“热门城市”和根据城市名称拼音首字母而进行排列的城市名)和右侧的字母表三部分:
2 搜索输入框布局
搜索输入框部分非常简单,只有一个带圆角的输入框,背景色与主题色相同:
1️⃣在 city 下的 components 中新建搜索输入框组件 Search.vue :
<template>
<div>this is Search.</div> <!-- 1️⃣-②:在模板中添加文字内容; -->
</template>
<script>
export default {
name: 'CitySearch' /* 1️⃣-①:给 Search.vue 命名为 CitySearch; */
}
</script>
<style lang="stylus" scoped>
</style>
1️⃣-③:打开 city 下的 City.vue ,使用 Search.vue ;
<template>
<div>
<city-header></city-header>
<city-search></city-search> <!-- 1️⃣-⑥:使用组件 Search.vue。 -->
</div>
</template>
<script>
import CityHeader from './components/Header'
import CitySearch from './components/Search' /* 1️⃣-④:从当前目录下的 components 中
引入组件 Search.vue; */
export default {
name: 'City',
components: {
CityHeader,
CitySearch /* 1️⃣-⑤:注册局部组件 Search.vue; */
}
}
</script>
<style>
</style>
保存后,返回页面查看,内容正确显示,控制台无报错:
2️⃣打开 city 下 components 中的 Search.vue 编写结构与样式:
<template>
<div class="search"> <!-- 2️⃣-①:给 div 添加一个类名 search,div 中有一个 input 标签; -->
<!-- 2️⃣-②:给 input 标签添加类名 search-input;
2️⃣-③:添加 placeholder 内容为“输入城市名或拼音”; -->
<input class="search-input" type="text" placeholder="输入城市名或拼音">
</div>
</template>
<script>
export default {
name: 'CitySearch'
}
</script>
<style lang="stylus" scoped>
/* 2️⃣-④:从 styles 下引入 varibles.styl; */
@import '~styles/varibles.styl'
.search /*
2️⃣-⑤:设置 .search 高度为 0.72rem,左右 padding 为 0.1rem 增加输入框与两边的间距,
背景色为 $bgColor;
*/
height: .72rem
padding: 0 .1rem
background: $bgColor
.search-input /*
2️⃣-⑥:设置 .search-input 宽度为 100%;设置 box-sizing 为 border-box,
让宽度包含 padding;左右 padding 为 0.1rem 增加内容与两边的间距;设置高度
和行高为 0.62rem,字体颜色为 #666,文字居中显示,输入框有 0.06rem 的圆角。
*/
box-sizing: border-box
width: 100%
padding: 0 .1rem
height: .62rem
line-height: .62rem
color: #666
text-align: center
border-radius: .06rem
</style>
保存后,返回页面查看,输入框样式没有问题,当输入的内容特别多时,文字与输入框也保持了一定间距:
3 城市选择列表布局
3️⃣在 city 下的 components 中新建城市选择列表组件 List.vue :
<template>
<div>this is List.</div> <!-- 3️⃣-②:在模板中添加文字内容; -->
</template>
<script>
export default {
name: 'CityList' /* 3️⃣-①:给 List.vue 命名为 CityList; */
}
</script>
<style lang="stylus" scoped>
</style>
3️⃣-③:打开 city 下的 City.vue ,使用 List.vue ;
<template>
<div>
<city-header></city-header>
<city-search></city-search>
<city-list></city-list> <!-- 3️⃣-⑥:使用组件 List.vue。 -->
</div>
</template>
<script>
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List' /*
3️⃣-④:从当前目录下的 components 中
引入组件 List.vue;
*/
export default {
name: 'City',
components: {
CityHeader,
CitySearch,
CityList /* 3️⃣-⑤:注册局部组件 List.vue; */
}
}
</script>
<style>
</style>
保存后,返回页面查看,内容正确显示,且控制台无报错:
仔细分析城市选择列表部分,大致有三个区域,每个区域有对应的标题和内容。第一个区域和第二个区域结构与样式相同,标题分别为“当前城市”和“热门城市”,内容展示标题所对应的“城市名按钮”。第三个区域标题为城市名拼音首字母,内容为以当前首字母开头的城市名称:
4️⃣打开 city 下 components 中的 List.vue 编写结构与样式:
<template>
<div class="list"> <!-- 4️⃣-①:最外层 div 类名为 list,里面包裹三个类名为 area 的 div; -->
<div class="area"> <!-- 4️⃣-②:第一个 .area 包含两个 div,分别为 标题 .title 和按钮列表
.button-list; -->
<!-- 4️⃣-⑧:给标题添加 border-topbottom 类名,上、下各添加一像素边框; -->
<div class="title border-topbottom">当前城市</div>
<div class="button-list"> <!-- 4️⃣-③:.button-list 中,用类名为 button-wrapper 的
div 包裹城市 .button; -->
<div class="button-wrapper">
<div class="button">北京</div>
</div>
</div>
</div>
<div class="area"> <!-- 4️⃣-④:第二个 .area 与第一个 .area 相同,标题内容更改为“热门城市”,
.button-list 中添加为 5 个城市; -->
<!-- 4️⃣-⑨:给标题添加 border-topbottom 类名,上、下各添加一像素边框; -->
<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>
</div>
<div class="area"> <!-- 4️⃣-⑤:第三个 .area 包含两个 div,一个为标题(即城市名拼音首字母),
一个为当前首字母的城市名列表; -->
<div class="title border-topbottom">A</div> <!-- 4️⃣-⑩:给标题添加 border-topbottom
类名,上、下各添加一像素边框; -->
<ul class="item-list"> <!-- 4️⃣-⑥:城市名列表类名为 item-list,列表里边的每一个类名为
.item 的 li 标签即以当前首字母对应的城市名; -->
<li class="item border-bottom">阿拉尔</li> <!-- 4️⃣-⑯:添加类名 border-bottom,
给每一个 item 添加一像素边框; -->
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
</ul>
</div>
<div class="area"> <!-- 4️⃣-⑱:增加“B”城市选择区域; -->
<div class="title border-topbottom">B</div>
<ul class="item-list">
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'CityList'
}
</script>
<style lang="stylus" scoped>
/* 4️⃣-⑪:在 border-topbottom 的伪元素上设置颜色为 #ccc,让一像素边框颜色加深; */
.border-topbottom
&:before
border-color: #ccc
&:after
border-color: #ccc
.border-bottom /* 4️⃣-⑰:设置 border-bottom 一像素边框颜色为 #ccc; */
&:before
border-color: #ccc
.title /*
4️⃣-⑦:标题增加 0.2rem 的 padding-left,高度为 0.54rem,背景色为 #eee,
字体颜色为 #666,字体大小调整为 0.26rem;
*/
padding-left: .2rem
line-height: .54rem
background: #eee
color: #666
font-size: .26rem
.button-list /*
4️⃣-⑬:.button-list 添加 overflow: hidden 触发 BFC;设置上、下、左 padding
为 0.1rem,右 padding 为 0.6rem,预留字母列表的空间;
*/
overflow: hidden
padding: .1rem .6rem .1rem .1rem
.button-wrapper /* 4️⃣-⑫:.button-wrapper 左浮动,宽度为 33.33%; */
float: left
width: 33.33%
.button /*
4️⃣-⑭:每个按钮的 margin 为 0.1rem;上、下 padding 为 0.1rem;
给按钮添加 0.02rem 的实线浅灰边框,边框添加 0.06rem 的圆角;
*/
margin: .1rem
padding: .1rem 0
text-align: center
border: .02rem solid #ccc
border-radius: .06rem
.item-list /*
4️⃣-⑮:item-list 中的每一个 item 行高为 0.76rem;padding-left 为 0.2rem,
与其他内容对齐;
*/
.item
line-height: .76rem
padding-left: .2rem
</style>
❌保存后,返回页面查看效果,城市选择列表布局,基本完成。但,当页面上下滚动时,是整个页面内容一起滚动:
而我们要实现的效果,是头部和搜索输入框保持固定,仅城市选择列表内容滚动:
❓如何实现页面滚动时,头部和搜索输入框固定不动,仅城市选择列表内容滚动?
答:我们可以引入一个“BetterScroll”第三方插件。通过这个插件,可以让城市选择列表区域实现与原生 App 类似的拖拽效果。
5️⃣打开终端,在项目目录下运行命令 npm install better-scroll --save 安装 BetterScroll:
5️⃣-①:查看项目下方文档,文档内容指出 HTML 结构为由一层容器 wrapper 包裹内容 content ,内容区域可滚动。使用 BetterScroll 时,只需在项目中引入插件, new 一个 BScroll 实例并传入容器的 DOM 元素或 DOM 选择器;
5️⃣-②:打开 city 下 components 中的 List.vue ;
🔗前置知识:《深入理解 Vue 组件——① 使用组件的细节点》
<template>
<div class="list" ref="wrapper"> <!-- 5️⃣-③:在最外层 div 上添加名为 wrapper 的 ref
属性(我们通过 ref 属性获取 DOM); -->
<div> <!-- 5️⃣-④:使用一个 div 包裹列表区域的所有内容,让整个城市选择列表区域的 HTML 结构
符合 BetterScroll 的要求; -->
<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>
</div>
<div class="area">
<div class="title border-topbottom">A</div>
<ul class="item-list">
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
<li class="item border-bottom">阿拉尔</li>
</ul>
</div>
<div class="area">
<div class="title border-topbottom">B</div>
<ul class="item-list">
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
<li class="item border-bottom">北京</li>
</ul>
</div>
</div>
</div>
</template>
<script>
/* 5️⃣-⑥:引入 better-scroll 插件; */
import BScroll from 'better-scroll'
export default {
name: 'CityList',
mounted () { /*
5️⃣-⑦:在 mounted 中(即当页面挂载完毕后),创建一个 scroll 实例属性,它等于
new 一个 BScroll,并传入名为 wrapper 的 DOM 节点。
*/
this.scroll = new BScroll(this.$refs.wrapper)
}
}
</script>
<style lang="stylus" scoped>
.border-topbottom
&:before
border-color: #ccc
&:after
border-color: #ccc
.border-bottom
&:before
border-color: #ccc
.list /*
5️⃣-⑤:给 .list 增加样式,使列表内容固定;添加 overflow: hidden,当内容溢出时隐藏;
让 .list 绝对定位,top 为 1.58rem,预留出头部和搜索输入框的高度,left、right、bottom
都为 0;
*/
overflow: hidden
position: absolute
top: 1.58rem
left: 0
right: 0
bottom: 0
/* ❗️原有的所有内容都在 .list 中,所以全部缩进一个单位。 */
.title
padding-left: .2rem
line-height: .54rem
background: #eee
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
padding-left: .2rem
</style>
保存后,返回页面查看,控制台无报错,头部和搜索框固定,城市选择列表区域可以滚动:
4 字母表布局
字母表的每个字母是从数据中动态获取的,它的布局相对简单:
6️⃣在 city 下的 components 中新建字母表组件 Alphabet.vue :
<template>
<ul class="list"> <!-- 6️⃣-②:template 中最外层 ul 类名为 list,里面包含的 li 标签类名为
item,内容是字母(后面由 Ajax 获取到数据后进行循环); -->
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
</ul>
</template>
<script>
export default {
name: 'CityAlphabet' /* 6️⃣-①:给 Alphabet.vue 命名为 CityAlphabet; */
}
</script>
<style lang="stylus" scoped>
</style>
6️⃣-③:打开 city 下的 City.vue ,使用 Alphabet.vue ;
<template>
<div>
<city-header></city-header>
<city-search></city-search>
<city-list></city-list>
<city-alphabet></city-alphabet> <!-- 6️⃣-⑥:使用组件 List.vue。 -->
</div>
</template>
<script>
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet' /* 6️⃣-④:从当前目录下的 components 中
引入组件 Alphabet.vue; */
export default {
name: 'City',
components: {
CityHeader,
CitySearch,
CityList,
CityAlphabet /* 6️⃣-⑤:注册局部组件 Alphabet.vue; */
}
}
</script>
<style>
</style>
保存后,返回页面查看,控制台无报错,可以看到多个“A”:
7️⃣返回 city 下 components 中的 Alphabet.vue 编写样式:
<template>
<ul class="list">
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
</ul>
</template>
<script>
export default {
name: 'CityAlphabet'
}
</script>
<style lang="stylus" scoped>
/* 7️⃣-③:从 styles 下引入 varibles.styl 文件; */
@import '~styles/varibles.styl'
.list
position: absolute /*
7️⃣-①:让 .list 绝对定位,top 为 1.58rem,与城市选择列表对齐;right、
bottom 为 0,宽度为 0.4rem;
*/
top: 1.58rem
right: 0
bottom: 0
width: .4rem
display: flex /* 7️⃣-②:使用 flex 布局,让字母纵向居中; */
flex-direction: column
justify-content: center
.item /* 7️⃣-④:字母行高为 0.4rem,内容水平方向居中,颜色为 $bgColor。 */
line-height: .4rem
text-align: center
color: $bgColor
</style>
保存后,返回页面查看:
以上,我们完成了城市选择页所有布局。
🏆本篇总结:
- 在城市选择列表组件中,新引入了一个第三方插件“BetterScroll”进行布局;
- 在使用插件时,通过特殊属性
ref来获取 DOM。
下一篇中,我们将完成城市选择页的动态数据渲染。
祝好,qdywxs ♥ you!