文章目录
- 目标
- 过程与代码
- 安装依赖
- 结构样式
- 动态数据:默认数据今天明天
- 添加日历
- 修改样式
- 动态数据:显示日历中选择的数据
- 效果
- 总代码
- 修改或添加的文件
- formatDate.js
- home.vue
- main.js
目标
点击时间:
弹出日历供选择:
动态显示数据:
过程与代码
安装依赖
先安装一个很好用的日期库:
npm install dayjs
结构样式
目标:数据先是静态的。
html:
<div class="section time-range">
<div class="start">
<span>入住</span>
<div class="time">
6月13日
</div>
</div>
<div class="stay">共一晚</div>
<div class="end">
<span>离店</span>
<div class="time">
6月14日
</div>
</div>
</div>
css:
.search-box {
// search-box里的每个部分都加上section
// 都有类似的样式
.section {
display: flex;
padding: 0 20px;
color: #999;
}
.location {
height: 44px;
display: flex;
align-items: center;
padding: 0 20px;
color: #53565c;
.city {
// flex:1 === flex:1 1 auto 除了position之外的剩余部分都属于city
flex: 1;
}
.position {
width: 74px;
display: flex;
align-items: center;
.text {
font-size: 12px;
}
img {
width: 20px;
margin-left: 5px;
}
}
}
.time-range {
display: flex;
justify-content: space-between;
span{
font-size: 16px;
color: #53565c;
}
}
}
注意,我们令跟搜索有关的为search-box:
search-box里每个部分都添加section这个类:因为每个部分的样式都是相似的,比如都是flex布局,padding都是0 20px。
动态数据:默认数据今天明天
一般搜索票的默认数据:
- 开始时间:今天
- 结束时间:明天
如果我们直接使用new Date,得到的会是:Tue Jan 10 2023 22:05:28 GMT+0800 (中国标准时间)
而我们想要显示的是MM月DD日。显然我们需要写一个格式化的函数。一般这种工具类的代码会放在utils里。
formatMonthDay.js:
import dayjs from 'dayjs'
export function formatMonthDay(date) {
return dayjs(date).format('MM月DD日')
}
在home中获取今天和明天:要响应式
const today = new Date()
const startDay = ref(formatMonthDay(today))
const endDay = ref(formatMonthDay(today.setDate(today.getDate() + 1))) //明天写法
效果:
添加日历
Calendar 日历 - Vant 4 (gitee.io)
查看文档,对应的属性和事件分别为:
- :value=“date”:date为数组结构,数组第一项为开始时间,第二项为结束时间。
- showCalendar:是否显示日历
- type=range:选择日期范围的日历(属性)
- confirm:日期选择完成后触发(事件)
- show-confirm:为true则点击确认后再触发confirm事件,否则直接触发(属性)
代码:
<div class="section time-range" :value="date" @click="showCalendar = true">
<div class="start">
<span>入住</span>
<div class="time">
{{ startDay }}
</div>
</div>
<div class="stay">共一晚</div>
<div class="end">
<span>离店</span>
<div class="time">
{{ endDay }}
</div>
</div>
</div>
<!-- 日历 -->
<van-calendar v-model:show="showCalendar" type="range" @confirm="onConfirm" />
js:
// 日历
const date = ref('');
const showCalendar = ref(false);
const formatDate = (date) => `${date.getMonth() + 1}/${date.getDate()}`;
const onConfirm = (values) => {
const [start, end] = values;
showCalendar.value = false;
date.value = `${formatDate(start)} - ${formatDate(end)}`;
};
效果:
点击后显示:
修改样式
修改弹出的日期高度:–van-calendar-popup-height
圆角弹窗:
动态数据:显示日历中选择的数据
让页面显示的数据是在日历里选择的数据。
html中都使用模板字符串:
js:
// 日历
const date = ref('1');
const showCalendar = ref(false);
const formatDate = (date) => `${date.getMonth() + 1}/${date.getDate()}`;
const onConfirm = (values) => {
const [start, end] = values;
showCalendar.value = false;
startDay.value = formatMonthDay(start)
endDay.value = formatMonthDay(end)
date.value = getDiffDate(start, end)
};
注意,可以封装获取日期差的方法:
// end-start
export function getDiffDate(start, end) {
return dayjs(end).diff(start, 'day')
}
效果:
效果
达成。
总代码
修改或添加的文件
formatDate.js
封装跟日期相关的方法。
import dayjs from 'dayjs'
// 格式化“x月x日”
export function formatMonthDay(date) {
return dayjs(date).format('MM月DD日')
}
// end-start
export function getDiffDate(start, end) {
return dayjs(end).diff(start, 'day')
}
home.vue
增加日期选择、日历。
<template>
<div class="home">
<div class="nav-bar">
<div class="title">旅游App</div>
<div class="banner">
<img src="@/assets/img/home/banner.webp" alt="">
</div>
</div>
<div class="search-box">
<div class="section location">
<div class="city">
<router-link to="/city">{{ cityStore.currentCity.cityName }}</router-link>
</div>
<div class="position">
<div class="text">我的位置</div>
<img src="@/assets/img/home/icon_location.png" alt="">
</div>
</div>
<div class="section time-range" :value="date" @click="showCalendar = true">
<div class="start">
<span>入住</span>
<div class="time">
{{ startDay }}
</div>
</div>
<div class="stay">共{{ date }}晚</div>
<div class="end">
<span>离店</span>
<div class="time">
{{ endDay }}
</div>
</div>
</div>
<!-- 日历 -->
<van-calendar :round="false" v-model:show="showCalendar" type="range" @confirm="onConfirm"
:show-confirm="false" />
</div>
</div>
</template>
<script setup>
import useCityStore from '../../store/modules/city';
import { formatMonthDay, getDiffDate } from '@/utils/formatDate'
import { ref } from 'vue';
const cityStore = useCityStore()
// 日期
const today = new Date()
const startDay = ref(formatMonthDay(today))
const endDay = ref(formatMonthDay(new Date().setDate(today.getDate() + 1))) //明天写法
// 日历
const date = ref('1');
const showCalendar = ref(false);
const formatDate = (date) => `${date.getMonth() + 1}/${date.getDate()}`;
const onConfirm = (values) => {
const [start, end] = values;
showCalendar.value = false;
startDay.value = formatMonthDay(start)
endDay.value = formatMonthDay(end)
date.value = getDiffDate(start, end)
};
</script>
<style lang="less" scoped>
.home {
.nav-bar {
.title {
height: 46px;
// flex居中,以后左右有东西可以直接加
display: flex;
align-items: center;
justify-content: center;
color: var(--primary-color);
font-size: 16px;
font-weight: 700;
}
.banner {
// 图片本身大很多,让它大小刚好
img {
width: 100%;
}
}
}
.search-box {
--van-calendar-popup-height: 100%;
// search-box里的每个部分都加上section
// 都有类似的样式
.section {
display: flex;
padding: 0 20px;
color: #999;
}
.location {
height: 44px;
display: flex;
align-items: center;
padding: 0 20px;
color: #53565c;
.city {
// flex:1 === flex:1 1 auto 除了position之外的剩余部分都属于city
flex: 1;
}
.position {
width: 74px;
display: flex;
align-items: center;
.text {
font-size: 12px;
}
img {
width: 20px;
margin-left: 5px;
}
}
}
.time-range {
display: flex;
justify-content: space-between;
span {
font-size: 16px;
color: #53565c;
}
}
}
}
</style>
main.js
引入日历组件。