城市选择组件
描述:由于之前需要写一个订票的页面,于是乎在网上搜索了些控件自己对比了下,仿着其中一个组件写了一个,属于重复造轮子,但是目的主要是记录自己的成长,以及方便日后查找
有图有真相,先上图看下效果
大概就是上图这个样子,点击input框后,弹出城市选择组件,点击其中一个具体的城市或者空表区域隐藏组件(有个小bug,点击空白区域可能不太灵敏)
直接上代码
<template>
<div class="row main" @click="hideDialog" style="height: 100%;">
<div class="q-pa-lg" style="margin-left: 25%;margin-right: 25%;margin-top: 10px;">
<q-tabs style="width: 100%;">
<!-- 目标 -->
<q-tab slot="title" label="国内机票" style="width: 200px;" />
<q-tab slot="title" label="国际机票" style="width: 200px;" />
<q-tab slot="title" label="火车票" style="width: 200px;" />
</q-tabs>
<div id="startCityDialog" class="city" style="padding: 10px; border: solid 1px #ccccc;">
<div id="startPanel">
<span>出发城市:</span>
<el-input type="text" @focus=" showStartCityDialog" v-model=" startCity" style="width: 300px;height: 30px;" />
<div class="city-components" v-if="showStartCity" style="margin-top: 10px;">
<ul class="filter-tabar clearfix">
<li v-for="(item,index) in cityListKey" :class="{active:upCityListIndex==index}" @mouseover="upCityListKey(index)">{{item}}</li>
</ul>
<div class="city-content">
<ul v-for="item in upCityList" class="clearfix">
<label for="">{{item.ckey}}</label>
<li v-for="ritem in item.cityList" @click=" selectStartCity(ritem)">{{ritem.airportName}}</li>
</ul>
</div>
</div>
</div>
<div style="margin-top: 20px;margin-left: 0px;" id="endPanel">
<span >到达城市:</span>
<el-input type="text" @focus=" showEndCityDialog" v-model=" endCity" style="width: 300px;height: 30px;" />
<div class="city-components" v-if=" showEndCity" style="margin-top: 10px;">
<ul class="filter-tabar clearfix">
<li v-for="(item,index) in cityListKey" :class="{active:upCityListIndex==index}" @mouseover="upCityListKey(index)">{{item}}</li>
</ul>
<div class="city-content">
<ul v-for="item in upCityList" class="clearfix">
<label for="">{{item.ckey}}</label>
<li v-for="ritem in item.cityList" @click=" selectEndCity(ritem)">{{ritem.airportName}}</li>
</ul>
</div>
</div>
</div>
<div style="margin-top: 20px;">
<span>出发日期:</span>
<el-date-picker v-model="startDate" style="width: 300px;height: 30px;" class="filter-item" type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd" />
</div>
<div style="margin-top: 20px;">
<span >到达日期:</span>
<el-date-picker v-model="endDate" style="width: 300px;height: 30px;" class="filter-item" type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd" />
</div>
</div>
</div>
</div>
</template>
<script>
import { dataList } from 'src/utils/data'
export default {
name: 'internalTicket',
data() {
return {
dataList,
startCity: '',
endCity: "",
startDate: new Date(),
endDate: new Date(),
showStartCity: false,
showEndCity: false,
upCityListIndex: '',
upCityList: ''
}
},
computed: {
cityListData() {
let map = {}; // 处理过后的数据对象
let temps = []; // 临时变量
//这一步就获取了
// map:{
// 'A':[{
// airportCode:'',
// airportName:''
// },{
// ..
// }],
// 'B':[{{
// airportCode:'',
// airportName:''
// },
// ...
// }
this.dataList.map(item => {
if(item.airportCode) {
let ekey = item.airportCode.charAt(0).toUpperCase(); // 根据key值的第一个字母分组,并且转换成大写
temps = map[ekey] || []; // 如果map里面有这个key了,就取,没有就是空数组
temps.push({
airportCode: item.airportCode,
airportName: item.cityName
});
map[ekey] = temps;
}
})
let list = [];
//获取到list {ckey: "I", cityList: Array(3)}
for(let gkey in map) {
list.push({
ckey: gkey,
cityList: map[gkey]
})
}
console.log('list', list)
list = list.sort((li1, li2) => li1.ckey.charCodeAt(0) - li2.ckey.charCodeAt(0));
let chunk = 4;
let result = [];
for(var i = 0, j = list.length; i < j; i += chunk) {
result.push(list.slice(i, i + chunk));
}
console.log('result', result)
return result;
},
cityListKey() {
let cityListKey = [];
this.cityListData.map(item => {
let ckeys = '';
item.map(ritem => {
ckeys += ritem.ckey;
})
cityListKey.push(ckeys);
})
console.log("cityListKey", cityListKey) // ["ABCD", "EFGH", "IJKL", "MNOP", "RSTU", "WXYZ"]
this.upCityList = this.cityListData[0]
return cityListKey;
}
},
methods: {
upCityListKey(index) {
this.upCityListIndex = index;
this.upCityList = this.cityListData[index];
},
hideCityDialog(event) {
this.showStartCity = false;
},
showStartCityDialog() {
this.showStartCity = true;
},
showEndCityDialog() {
this.showEndCity = true;
},
selectStartCity(ritem) {
this.startCity = ritem.airportName
this.showStartCity = false;
},
selectEndCity(ritem) {
this.endCity = ritem.airportName
this.showEndCity = false;
},
hideDialog(event) {
var sp1 = document.getElementById("startPanel");
var sp2 = document.getElementById("endPanel");
//点击的是到达城市区域
if(!sp1.contains(event.target) && sp2.contains(event.target)) {
this.showStartCity = false;
}//点击的是出发城市区域
else if(!sp2.contains(event.target) && sp1.contains(event.target)){
this.showEndCity = false;
}else{
this.showStartCity = false;
this.showEndCity = false;
}
}
}
}
</script>
<style scoped lang="stylus">
#app {
/*background: #efefef;*/
}
* {
margin: 0;
padding: 0;
}
.city-wap {
color: #3b4f62;
.clearfix {
&:after {
content: '';
display: block;
clear: both;
}
}
p {
background: #fff;
margin-bottom: 10px;
padding: 0 12px;
}
.search {
position: fixed;
top: 0;
box-shadow: 0 1px 3px 0 rgba(59, 79, 98, 0.1);
width: 100%;
height: 50px;
input {
line-height: 50px;
width: 100%;
border: none;
box-shadow: none;
padding: 0 10px;
&:focus {
outline: none;
}
}
}
.city-list {
.block-60 {
height: 60px;
}
ul {
padding: 0 10px;
li {
list-style: none;
display: inline-block;
margin-right: 10px;
width: 29%;
margin-bottom: 8px;
line-height: 35px;
text-align: center;
color: #333;
border-radius: 3px;
background: #fff;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 2px;
}
}
}
.filter {
position: fixed;
right: 3px;
top: 60px;
font-size: 15px;
div {
margin-top: 2px;
text-align: center;
}
}
.active-key {
position: fixed;
width: 100px;
height: 100px;
line-height: 100px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 100;
background: #dedede;
color: #fff;
border-radius: 100%;
text-align: center;
font-size: 40px;
}
}
.city {
position: relative;
.city-components {
opacity: 1;
position: absolute;
z-index: 10;
/*background-color: #0066CC;*/
width: 500px;
box-shadow: 0 0 4px 0 rgba(117, 117, 117, 1);
background-color: #ffffff;
border-radius: 2px;
padding: 20px 21px;
.clearfix {
&:after {
content: '';
display: block;
clear: both;
}
}
li {
list-style: none;
}
ul {
padding: 0;
margin: 0;
}
.filter-tabar {
border-bottom: 1px solid #d7d7d7;
cursor: pointer;
li {
text-align: center;
/*padding: 0 14px;*/
float: left;
padding-bottom: 14px;
font-size: 14px;
margin: 0 8px;
margin-bottom: -1px;
position: relative;
&.active {
border-bottom: 1px solid #2577e3;
color: #2577e3
}
}
}
.city-content {
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 13px 0 13px;
label {
display: block;
margin-bottom: 5px !important;
margin-left: 0 !important;
color: #5f5f5f !important;
margin-top: 5px;
}
li {
padding: 6px 0 6px;
float: left;
text-align: left;
font-size: 14px;
min-width: 56px;
margin-right: 24px;
cursor: pointer;
}
}
}
}
</style>
主要包含两个ul ,第一个ul主要存放ABCD等的titlebar,第二个ul主要放城市以及城市类型,代码比较简单,很容易看懂
注意的点:
hideDialog(event) {
var sp1 = document.getElementById("startPanel");
var sp2 = document.getElementById("endPanel");
//点击的是到达城市区域
if(!sp1.contains(event.target) && sp2.contains(event.target)) {
this.showStartCity = false;
}//点击的是出发城市区域
else if(!sp2.contains(event.target) && sp1.contains(event.target)){
this.showEndCity = false;
}else{
this.showStartCity = false;
this.showEndCity = false;
}
}
数据源大概是这个样子:
const dataList = [{
"airportCode": "YIE",
"cityInfo": "AES-阿尔山-YIE",
"cityName": "阿尔山市",
"airportName": "伊尔施",
"status": 0,
"lat": 47.3155940318,
"lng": 119.9293992017
}, {
"airportCode": "AKA",
"cityInfo": "AK-安康-AKA",
"cityName": "安康",
"airportName": "五里铺",
"status": 0,
"lat": 32.7132899844,
"lng": 108.9462270884
}]
数据源地址: