闲云旅游项目开发-(第四篇:机票首页/机票搜索功能(`el-autocomplete`远程搜索组件)/moment.js的使用/日期选择组件el-date-picker)
目录
相关组件介绍
一 机票首页布局
二 封装搜索组件
1. 组件布局
2.目标思路
3.步骤
3.1 基本功能
3.2 自动补全
3.3获取真正的推荐数据
3.4 添加城市代号
3.5 处理日期格式
3.6 使用了momentjs,这是一个第三方的包,需要先下载并且导入。
其他功能
优化
三 搜索功能
1.思路
2.实现步骤
获取城市数据
调用城市查询接口
下拉数据选中赋值
搜索跳转
城市切换
往返
3.总结
四 特价机票
1. 思路
2.实现步骤
2.1 特价机票布局
2.2 请求数据接口
五 效果图
相关组件介绍
1.el-autocomplete
远程搜索自动补全组件
组件文档:https://element.eleme.cn/#/zh-CN/component/input#yuan-cheng-sou-suo
2.el-date-picker
日期选择组件
组件文档:https://element.eleme.cn/#/zh-CN/component/date-picker#xuan-ze-ri
一 机票首页布局
pages/air/index.vue
的代码如下 :
<template><section class="container"><h2 class="air-title"><span class="iconfont iconfeiji"></span><i>国内机票</i></h2><!-- 搜索广告栏 --><el-row type="flex" justify="space-between"><!-- 搜索表单 --><div>搜索</div><!-- banner广告 --><div class="sale-banner"><img src="http://157.122.54.189:9093/images/pic_sale.jpeg"></div></el-row><!-- 广告 --><el-row type="flex" class="statement"><el-col :span="8"><i class="iconfont iconweibiaoti-_huabanfuben" style="color:#409EFF;"></i><span>100%航协认证</span></el-col><el-col :span="8"><i class="iconfont iconbaozheng" style="color:green;"></i><span>出行保证</span></el-col><el-col :span="8"><i class="iconfont icondianhua" style="color:#409EFF;"></i><span>7x24小时服务</span></el-col></el-row><h2 class="air-sale-title"><span class="iconfont icontejiajipiao"></span><i>特价机票</i></h2><!-- 特价机票 --><div class="air-sale"></div></section>
</template><script>
export default {}
</script><style scoped lang="less">
.air-sale{border: 1px #ddd solid;padding:20px;margin-bottom:50px;.air-sale-pic{> div{width:225px;height:140px;position: relative;overflow: hidden;img{width:100%;}.layer-bar{position:absolute;bottom:0;left:0;background: rgba(0,0,0,0.5);color:#fff;height:30px;line-height: 30px;width:100%;box-sizing: border-box;padding: 0 15px;font-size: 14px;span:last-child{font-size:18px;}}}}
}.air-sale-group{margin-top:20px;padding-top:8px;border-right:1px #eee solid;&:last-child{border-right:none;}.air-sale-row{font-size:12px;color:#666;margin-bottom:8px;.air-sale-price{color:orange;font-size: 20px;}}
}.container{width:1000px;margin:0 auto;
}.air-title{margin:15px 0;font-size:20px;font-weight: normal;color:orange;span{font-size:20px;}
}.statement{margin:15px 0;border:1px #ddd solid;background:#f5f5f5;height: 58px;padding:10px 0;box-sizing:border-box;> div{text-align: center;line-height: 38px;border-right:1px #ddd solid;&:last-child{border-right: none;}*{vertical-align: middle;}i{font-size:30px;}}
}.air-sale-title{margin:15px 0;font-size:20px;font-weight: normal;color:#409EFF;span{font-size:20px;}
}
</style>
二 封装搜索组件
1. 组件布局
新建机票搜索表单组件components/air/searchForm.vue
,并替换以下布局代码 :
<template><div class="search-form"><!-- 头部tab切换 --><el-row type="flex" class="search-tab"><span v-for="(item, index) in tabs" :key="index"@click="handleSearchTab(item, index)":class="{active: index === currentTab}"><i :class="item.icon"></i>{{item.name}}</span></el-row><el-form class="search-form-content" ref="form" label-width="80px"><el-form-item label="出发城市"><!-- fetch-suggestions 返回输入建议的方法 --><!-- select 点击选中建议项时触发 --><el-autocomplete:fetch-suggestions="queryDepartSearch"placeholder="请搜索出发城市"@select="handleDepartSelect"class="el-autocomplete"></el-autocomplete></el-form-item><el-form-item label="到达城市"><el-autocomplete:fetch-suggestions="queryDestSearch"placeholder="请搜索到达城市"@select="handleDestSelect"class="el-autocomplete"></el-autocomplete></el-form-item><el-form-item label="出发时间"><!-- change 用户确认选择日期时触发 --><el-date-picker type="date" placeholder="请选择日期" style="width: 100%;"@change="handleDate"></el-date-picker></el-form-item><el-form-item label=""><el-button style="width:100%;" type="primary" icon="el-icon-search"@click="handleSubmit">搜索</el-button></el-form-item><div class="reverse"><span @click="handleReverse">换</span></div></el-form> </div>
</template><script>
export default {data(){return {tabs: [{icon: "iconfont icondancheng", name: "单程"},{icon: "iconfont iconshuangxiang", name: "往返"}],currentTab: 0,}},methods: {// tab切换时触发handleSearchTab(item, index){},// 出发城市输入框获得焦点时触发// value 是选中的值,cb是回调函数,接收要展示的列表queryDepartSearch(value, cb){cb([{value: 1},{value: 2},{value: 3},]);},// 目标城市输入框获得焦点时触发// value 是选中的值,cb是回调函数,接收要展示的列表queryDestSearch(value, cb){cb([{value: 1},{value: 2},{value: 3},]);},// 出发城市下拉选择时触发handleDepartSelect(item) {},// 目标城市下拉选择时触发handleDestSelect(item) {},// 确认选择日期时触发handleDate(value){},// 触发和目标城市切换时触发handleReverse(){},// 提交表单是触发handleSubmit(){}},mounted() {}
}
</script><style scoped lang="less">
.search-form{border:1px #ddd solid;border-top:none;width:360px;height:350px;box-sizing: border-box;
}.search-tab{span{display: block;flex:1;text-align: center;height:48px;line-height: 42px;box-sizing: border-box;border-top:3px #eee solid;background:#eee;}.active{border-top-color: orange;background:#fff;}i{margin-right:5px;font-size: 18px;&:first-child{font-size:16px;}}
}.search-form-content{padding:15px 50px 15px 15px;position: relative;.el-autocomplete{width: 100%;}
}.reverse{position:absolute;top: 35px;right:15px;&:after,&:before{display: block;content: "";position: absolute;left:-35px;width:25px;height:1px;background:#ccc;}&:after{top:0;}&:before{top:60px;}span{display: block;position:absolute;top: 20px;right:0;font-size:12px;background: #999;color:#fff;width:20px;height:20px;line-height: 18px;text-align: center;border-radius: 2px;cursor: pointer;&:after,&:before{display: block;content: "";position: absolute;left:10px;width:1px;height:20px;background:#ccc;}&:after{top:-20px;}&:before{top:20px;}}
}
</style>
创建完成后在pages/air/index.vue
中导入组件
<template>
<!-- 其他代码... --><!-- 搜索广告栏 --><el-row type="flex" justify="space-between"><!-- 搜索表单 --><SearchForm/><!-- banner广告 --></el-row><!-- 其他代码... -->
</template>
<script>
import SearchForm from "@/components/air/searchForm";export default {components: {SearchForm},
}
</script>
2.目标思路
通过用户输入数据, 组合成需要的参数 点击搜索, 跳转页面后, 传递到机票搜索结果页面,再发送请求
需要的参数包括
出发地 / 出发地代码 departCity /departCode
到达地 / 到达地代码 destCity / destCode
出发时间 departDate
3.步骤
3.1 基本功能
三个输入框绑定数据
点击按钮, 打印出出全部数据作为参考
3.2 自动补全
先用死数据模拟搜索建议,显示自动补全的两个步骤
- 声明fetch-suggestion 定义获取建议的函数
- 在函数当中,可以接受两个参数
- element-ui传进来当前的输入值
- element-ui提供的封装好的显示列表函数,这个函数我们在形参可以随意命名,只要我们执行这个函数,并且带上结果数组就会显示一个建议列表
出发地输入框弹出搜索建议
到达地输入框弹出搜索建议
3.3获取真正的推荐数据
使用 ajax 获取数据
这个接口如果没有参数会获取到100条数据
修改数据格式,,再进行回调渲染
自动补全搜索建议的数组里面的对象,必须有value才可以显示出来
配合组件的要求 (每个都需要有 value)
3.4 添加城市代号
选择一个选项的时候, 城市名作为 value 无需处理
但是城市代码code必须主动放入数据当中
获取搜索建议列表时, 除了 value 值用来渲染列表需要以外, 还需要将当前城市的 code 放进去备用
form 数据有两个属性储存城市代号 departCode / destCode
每当选中一个选项时, 同时将城市代号更新到 form 中
监听 select 事件,函数可以接受一个固定的参数, 就是我们选中的那个城市的数据对象
讲这个数据对象里面的 sort 放入 code 城市代码当中即可
3.5 处理日期格式
使用日期选择组件(基本用法,用 v-model 绑定数据)
选了日期之后, 输出的是一个日期对象
需要转换为合适的格式
可以尝试自己将日期对象改成 YYYY-MM-DD 的格式
饿了么也有一个功能 使用
value-format
指定绑定值的格式, 可以使用
时间格式转换的时机每次用户修改日期的时候自动触发 (监听 change 事件), 并且修改用户时间格式
3.6 使用了momentjs
,这是一个第三方的包,需要先下载并且导入。
下载命令:
npm install --save moment
在组件中引入
import moment from "moment";
传入日期对象即可改变格式
moment(value).format("YYYY-MM-DD");
其他功能
优化
封装获取建议函数,, 将获取出发地搜索建议和获取到达地搜索建议的函数合并
输入后再激活输入建议 :trigger-on-focus="false"
自动高亮第一个选项 : highlight-first-item ="true" (如果不生效请注意elementui 版本)
数据来自第三方, 某些城市缺失代码, 删除获取不到 sort 的数据
其实可以过滤, 获取简易列表时, 将所有不带有 sort 数据的城市去掉
城市名称不应带有市字, 在获取列表的时候直接替换掉市字即可
利用 replace 方法, 将数据 name 中最后一个 市字 替换成 空字符串
处理用户没有选择城市,造成缺失代码的问题
搜索得出建议选项的时候,默认将列表的第一项 code 数据放入 form
默认给一个值, 如果用户自己选了, 再去覆盖
思考:如果用户非要写一个市字, 可以在以下几个时间点进行处理, 只要最终发请求前处理完毕没有市字即可
失去焦点
跳转页面的时候
页面跳转后提交搜索 ajax 的时候
(建议在搜索时才处理,将这个处理隐藏起来不让用户感知)
三 搜索功能
1.思路
从结果出发,首先来看下我看点击表单后会发生什么事?
点击表单的搜索按钮后会跳转到该页面,并且提供了5
个用于查询的参数,暂且不考虑目前有没该页面,我们可以先考虑如何给页面提供这些参数。
参数列表(重要)
:
departCity
出发城市(注意没有市
字)departCode
出发城市代码destCity
到达城市(注意没有市
字)destCode
到达城市代码departDate
出发日期
可以从查找机票城市的接口找到城市相关数据,在用户输入城市的同时获得上面的数据。
接口:http://157.122.54.189:9095/airs/city
返回结果:
{data: [{code: "440100000000",created_at: "2019-04-02 08:18:16",id: 197,level: "2",name: "广州市",parentCode: "440000000000",sort: "CAN",updated_at: 1558617184703}];total: 1}
2.实现步骤
获取城市数据
在data中新增变量存储表单数据 ,在data中定义需要提交的是5
个参数
<script>
export default {data(){return {// 其他代码...form: {departCity: "", // 出发城市departCode: "", // 出发城市代码destCity: "", // 到达城市destCode: "", // 到达城市代码departDate: "", // 日期字符串},}},
}
</script>
注意在template
的表单中使用v-model
双向绑定数据到form
的属性。
比如出发城市v-model="form.departCity"
:
<el-autocomplete:fetch-suggestions="queryDepartSearch"placeholder="请搜索出发城市"@select="handleDepartSelect"class="el-autocomplete"v-model="form.departCity"></el-autocomplete>
日期departDate
只需要在el-date-picker
组件中使用v-model="form.departDate"
进行绑定即可获得了。
调用城市查询接口
使用实时查询的方式调用查询城市的接口,由于出发城市和到达城市都需要调用这个查询接口,所以把查询操作封装到一个独立函数来调用。
<script>
export default {// 其他代码...methods: {// 出发城市输入框获得焦点时触发// value 是选中的值,cb是回调函数,接收要展示的列表async queryDepartSearch(value, cb){const arr = await this.querySearchAsync(value)if(arr.length > 0){// 不在下拉列表中选择,则默认选择第一项this.form.departCity = arr[0].value;this.form.departCode = arr[0].sort;}cb(arr)},// 目标城市输入框获得焦点时触发// value 是选中的值,cb是回调函数,接收要展示的列表async queryDestSearch(value, cb){const arr = await this.querySearchAsync(value)if(arr.length > 0){// 不在下拉列表中选择,则默认选择第一项this.form.destCity = arr[0].value;this.form.destCode = arr[0].sort;}cb(arr)},// 查询城市接口的方法,返回promise// queryString是查询关键字querySearchAsync(queryString) {return new Promise((resolve, reject) => {// 如果关键字是空,则直接返回if(!queryString){return resolve([]);}this.$axios({url: `/airs/city`,params: {name: queryString}}).then(res => {const {data} = res.data;// 下拉提示列表必须要有value字段const arr = data.map(v => {return {...v,value: v.name.replace("市", "")}});resolve(arr);});});},// 其他代码...}}
</script>
下拉数据选中赋值
针对城市下拉框选项选中的事件处理,我们应该把选中的选项
当做是当前的数据
<script>export default {// 其他代码...methods: {// 其他代码...// 出发城市下拉选择时触发// item代表当前选中项handleDepartSelect(item) {this.form.departCity = item.value;this.form.departCode = item.sort;},// 目标城市下拉选择时触发// item代表当前选中项handleDestSelect(item) {this.form.destCity = item.value;this.form.destCode = item.sort;},// 确认选择日期时触发handleDate(value){this.form.departDate = moment(value).format("YYYY-MM-DD");},// 其他代码...}}
</script>
搜索跳转
下面来实现页面跳转,需要在URL
中把5
个参数都到带过去给搜索结果页/air/flights
<script>export default {// 其他代码...methods: {// 其他代码...// 提交表单是触发handleSubmit(){// 表单验证数据const rules = {depart: {value: this.form.departCity, message: "请选择出发城市"},dest: {value: this.form.destCity, message: "请选择到达城市"},departDate: {value: this.form.departDate, message: "请选择出发时间"},}let valid = true; // 表单验证结果Object.keys(rules).forEach(v => {// 只要有一个结果不通过,就停止循环if(!valid) return;const item = rules[v];// 数据字段为空if(!item.value){valid = false;this.$confirm(item.message, '提示', {confirmButtonText: '确定',showCancelButton: false,type: 'warning'})}});// 不通过验证,不需要往下执行if(!valid) return;this.$router.push({path: "/air/flights",query: this.form})}}}
</script>
/air/flights
页面暂时还没创建,不过只要能从URL
中看到参数正确传递即可。
城市切换
这是个把出发城市和到达城市对换位置的功能,实现起来非常简单,把form
的数据对调换就可以了
调换的事件函数
<script>export default {// 其他代码...methods: {// 其他代码...// 触发和目标城市切换时触发handleReverse(){const { departCity, departCode, destCity, destCode} = this.form;this.form.departCity = destCity;this.form.departCode = destCode;this.form.destCity = departCity;this.form.destCode = departCode;},}}
</script>
往返
目前接口不支持往返,需要添加一个提示
// tab切换时触发
handleSearchTab(item, index){if(index === 1){this.$confirm("目前暂不支持往返,请使用单程选票!", '提示', {confirmButtonText: '确定',showCancelButton: false,type: 'warning'})}
},
3.总结
关键点在于如何获取跳转链接需要的
5
个参数!
el-autocomplete
组件的使用(查看文档)使用
momentjs
进行时间转换表单自定义验证
四 特价机票
1. 思路
思路: 其实就是获取数据布局, 点击的结果就是模拟一个搜索
特价机票布局 :注意链接跳转时的数据拼接
请求数据接口 :在机票首页 created/mounted 生命周期里面获取数据 赋值到 data sales 然后遍历渲染页面即可
2.实现步骤
2.1 特价机票布局
新增特价机票布局和模拟数据。
pages/air/index.vue
<template><section class="container"><!-- 其他代码... --><!-- 特价机票 --><div class="air-sale"><el-row type="flex" class="air-sale-pic" justify="space-between"><el-col :span="6" v-for="(item, index) in sales" :key="index"><nuxt-link :to="`/air/flights?departCity=${item.departCity}&departCode=${item.departCode}&destCity=${item.destCity}&destCode=${item.destCode}&departDate=${item.departDate}`"><img :src="item.cover"/><el-row class="layer-bar" type="flex" justify="space-between"><span>{{item.departCity}}-{{item.destCity}}</span><span>¥699</span></el-row></nuxt-link></el-col></el-row></div></section>
</template><script>
// 其他代码...
export default {data(){return {sales: [{cover: "https://gss0.bdstatic.com/94o3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D220/sign=9154c841bcfd5266a32b3b169b199799/3812b31bb051f8199687c7e0d0b44aed2f73e7fe.jpg",departCity: "广州",departCode: "CAN",departDate: "2019-06-17",destCity: "上海",destCode: "SHA",price: 760}]}},// 其他代码...
}
</script>
注意跳转链接的nuxt-link :to="/air/flights?xxxx
的参数拼接
2.2 请求数据接口
<script>
import SearchForm from "@/components/air/searchForm";export default {data(){return {sales: [] // 去除模拟数据}},mounted(){this.$axios({url: `/airs/sale`}).then(res => {this.sales = res.data.data;});},// 其他代码...
}
</script>
五 效果图
闲云旅游项目开发-(第四篇:机票首页/机票搜索功能(`el-autocomplete`远程搜索组件)/moment.js的使用/日期选择组件el-date-picker)相关推荐
- 闲云旅游项目开发-(第一篇:使用Element-ui实现主页轮播图)
业务需求: 初始化布局 页头页脚公共组件 首页轮播图 一 初始化默认全局布局 nuxtjs 提供了一个公共组件 layouts/default.vue,相当于以前的 app.vue .该布局组件默认作 ...
- 闲云旅游项目开发-(第二篇:实现登录功能,使用vuex的store管理数据)
目录 一 登录注册页布局 二 登录功能 1.思路 2.实现步骤 2.1 新建登录表单组件 2.2 表单数据绑定及验证 2.3 登录接口 3.总结 三 使用vuex/store管理数据 1.思路 2. ...
- 项目开发流程_绿维文旅:旅游项目开发模式与流程
一.旅游项目开发模式 旅游综合开发是立足旅游项目自有资源基础,以旅游产业为主导,以市场为导向,以资本为驱动,以资源整合为核心,通过集中土地.资本.技术.交通.劳动力等生产要素,推进土地开发.交通建设. ...
- 基于GBT28181:SIP协议组件开发-----------第四篇SIP注册流程eXosip2实现(一)
原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3945294.html. 上章节讲解了利用自主开发的组件SIP组件l ...
- 仿LOL项目开发第四天
---恢复内容开始--- 仿LOL项目开发第四天 by草帽 上节讲了几乎所有的更新版本的逻辑,那么这节课我们来补充界面框架的搭建的讲解. 我们知道游戏中的每个界面都有自己的一个类型:比如登陆界面,创建 ...
- Agv、Rgv 车辆控制调度系统开发第四篇
Agv.Rgv 车辆控制调度系统开发第四篇 车辆调度模拟器 前言 一.车辆模拟器是什么? 二.如何做模拟器 1.动作仿真模拟器 2.完全仿真模拟器 总结 下期预告 系列文章链接 其他文章 新篇章 前言 ...
- 动力节点CRM项目开发【准备篇】
CRM项目开发[准备篇] (参考B站视频CRM项目) 01-基本技术框架 视图层(View):展示数据,负责跟用户交互 html,css,js,jquery,bootstrap 控制层(Control ...
- 重构ElementUI解决DatePicker日期选择组件修改父组件placement参数问题[Vue.js项目实践: 新冠自检系统]
新冠疫情自我检测系统网页设计开发文档 Sylvan Ding 的第一个基于 Vue.js 的项目. 本项目所提供的信息,只供参考之用,不保证信息的准确性.有效性.及时性和完整性,更多内容请查看国家卫健 ...
- vue日期选择组件_一个Vue组件,为波斯开发人员提供日期选择器
vue日期选择组件 Vue波斯日期选择器 (vue persian datepicker) This is a Jalali date picker component for Vue. 这是Vue的 ...
最新文章
- pandas使用pct_change函数计算数据列的百分比变化:计算当前元素和前一个元素之间的百分比变化(包含NaN值的情况以及数据填充方法)
- 2021毛纲源常考题型解题方法_总结2021年考研数学六大常考题型
- centos下pg_dump的服务器版本不匹配问题
- mysql数据库varchar的区别_MySQL数据库char与varchar的区别分析及使用建议
- 如何按 value 对 dictionary 进行排序?
- sqlserver注释巧清理
- css 友情链接效果,友链样式与位置很重要!
- 【java8新特性】——默认方法(五)
- 小爬爬1:jupyter简单使用爬虫相关概念
- Python写数据结构:循环队列
- Visual Studio 开发(三):Visual Studio 使用时常见问题解决方案
- latex 图、表 中英文标题
- 2D渲染pixi项目实战总结
- 将MATLAB任务栏变成白色的图标恢复
- 正六面体染色(java)
- Milvus 群星闪耀时|又一个小目标达成 :社区正式突破 15,000 星!
- 2021-10-21《能源与节能》能源环保省级期刊
- Python输入一个字符串,输出其中每个字符的出现次数。要求使用标准库collotections中的Counter类...
- 微信小程序中使用阿里矢量字体图标
- 爬取微信公众号详情页
热门文章
- 《重学Java系列》之 泛型(下)
- HW-2022 0day Nday漏洞 汇总 0728
- 兮米安装包制作工具绿色版
- 《Java SE实战指南》05-09:语句(Statements)
- Linux 的基本使用(上)· 背景故事 · 环境搭建 · 常用命令 vim less cp mv等(基本能覆盖 Linux 日常使用80%场景)· Linux 文件结构的意义 · 相对路径和绝对路径
- 分类型变量python聚类分析_用python对包含分类变量和数值变量的数据进行聚类的最佳方法是什么...
- 关于mysql错误的是_下面关于MySQL描述错误的是( )。_学小易找答案
- 爱情公寓第二季 MKV高清下载地址 普清地址也有哦!
- JAVA集合一些面试题
- 位图索引Bitmap indexes(数据库索引)