项目背景
在uniapp开发中要求在移动端设计一个以周为单位的日历组件,习惯了用组件的我,突然在uniapp插件中没有找到我的理想型。那只复习一下Date相关的文档,再手撸一个了。
实现思路
要想实现一周日历,首先肯定得获取到当前一周的日期,再与星期做一个一一对应,这样就形成了一个基本日历。将其封装成一个子组件,方便在父组件中调用。
设定一个周星期
默认是从周日到周六,我们采用固定的形式:
['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat']
获取当天是几号
<template>
<view>
今天是:{{ dayOfMonth }}号
</view>
</template>
<script>
export default {
data() {
return {
dayOfMonth: null, // 当天日期
};
},
onLoad() {
// 获取当前几号
const today = new Date();
this.dayOfMonth = today.getDate();
},
};
</script>
运行结果:今天是22号
获取一周日期
在这个地方,需要注意几点,为了方便渲染,这里将信息进行了一个拼接处理。我们封装了一个getWeekDates()方法,参数就是当前日期startDate。在这其中有一个方法叫toISOString(),这是方法的作用是使用ISO标准,返回Date对象的标准字符串格式,形式如:“2024-11-22T06:42:38.163Z”。
getWeekDates(startDate) {
const weekDates = [];
for (let i = 0; i < 7; i++) {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
weekDates.push({
date: date.toISOString().split('T')[0],
weekday: ['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat'][date.getDay()],
selected: this.selectedDate === date.toISOString().split('T')[0],
});
}
return weekDates;
},
初始化当前周
初始化当前周的作用是基于当前日期计算一周内所有日期,并将日期和星期做一个映射处理。
initCurrentWeek() {
const today = this.today;
const startOfWeek = new Date(today);
startOfWeek.setDate(today.getDate() - today.getDay());
this.currentWeek = this.getWeekDates(startOfWeek);
this.selectedDate = today.toISOString().split('T')[0];
},
日期选择
默认日期当天高亮,这里主要通过对比周日历中得日期,与系统获取得当前日期是否相同,如果相同就设置一个高亮样式进行标记。手动日期选择,点击某一天,设置高亮。
selectDay(index) {
this.selectedDate = this.currentWeek[index].date;
this.currentWeek = this.currentWeek.map((day, i) => ({
...day,
selected: i === index,
}));
this.$emit('dateSelected', this.selectedDate);
},
样式设计
主要采用flex布局,让其自适应宽度。
日期组件源码
<template>
<view class="week-calendar">
<view class="week-month">
<view class="month-title">
<text style="padding-right: 10px;">{{month}}</text>
<text>{{MONTH_EN[month]}}</text>
</view>
<text class="month-tips">Never Put Off Until Tomorrow </text>
</view>
<view class="week">
<block v-for="(day, index) in currentWeek" :key="index">
<view class="day" @click="selectDay(index)">
<view class="day-week">{{ day.weekday }}</view>
<view class="day-day">
<text class="day-text" :class="{ selected: day.selected }">{{ day.date.split('-')[2] }}</text>
</view>
<view class="day-tip">
<text class="day-tip-color" :class="{'tip-day-text': setDayTip(day.date)}"></text>
</view>
</view>
</block>
</view>
</view>
</template>
<script>
import {
MONTH_EN
} from '../../../utils/strategy.js'
export default {
props: {
todo_list: {
type: Array,
required: true
}
},
data() {
return {
MONTH_EN,
today: new Date(),
currentWeek: [],
selectedDate: new Date().toISOString().split('T')[0],
month: new Date().toISOString().split('T')[0].split('-')[1]
}
},
created() {
this.initCurrentWeek();
},
methods: {
// 初始化当前周
initCurrentWeek() {
const today = this.today;
const startOfWeek = new Date(today);
startOfWeek.setDate(today.getDate() - today.getDay());
this.currentWeek = this.getWeekDates(startOfWeek);
this.selectedDate = today.toISOString().split('T')[0];
},
// 获取一周日期
getWeekDates(startDate) {
const weekDates = [];
for (let i = 0; i < 7; i++) {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
weekDates.push({
date: date.toISOString().split('T')[0],
weekday: ['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat'][date.getDay()],
selected: this.selectedDate === date.toISOString().split('T')[0],
});
}
return weekDates;
},
// 选择某一天
selectDay(index) {
this.selectedDate = this.currentWeek[index].date;
this.currentWeek = this.currentWeek.map((day, i) => ({
...day,
selected: i === index,
}));
this.$emit('dateSelected', this.selectedDate);
},
setDayTip(day) {
return this.todo_list.some((item) => {
return item.remind_time.indexOf(day) != -1
})
}
}
}
</script>
<style scoped>
.week-calendar{
height: 190px;
width: 100%;
position: fixed;
top: 0;
background: #fff;
z-index: 10000;
}
.week-month {
padding: 20px;
}
.month-title {
font-size: 28px;
font-weight: bold;
margin-bottom: 4px;
}
.month-tips {
color: #868499;
font-size: 12px;
}
.week {
height: 88px;
font-size: 14px;
display: flex;
justify-content: space-around;
border: 1px solid #c8c7cc;
border-radius: 24upx;
background-color: #fff;
}
.day {
flex: 1;
text-align: center;
align-items: center;
}
.day-week {
padding: 10px 5px;
color: #8990ad;
}
.day-text {
display: inline-block;
padding: 4px;
}
.day-text.selected {
background-color: #ed471d;
color: #fff;
border-radius: 50%;
}
.day-tip{
height: 8px;
}
.day-tip-color{
width: 8px;
height: 4px;
display: inline-block;
}
.tip-day-text{
background: #fca00d;
}
</style>
父组件调用
<template>
<view class="todo">
<DateWeek :todo_list="todo_list" @dateSelected="onDateSelected" style="margin-bottom: 20px;"></DateWeek>
</view>
</template>
<script>
import DateWeek from './dateweek.vue';
export default {
components: {
DateWeek
}
}
</script>
效果图展示
设置日期标注
眼尖的朋友可能发现了,为啥会有的日期下面会有一个黄色的小标识,比图2024-11-18,2024-11-20。其实它是这样实现的,这和业务功能有关,原理就是提取数据中的日期,再与当前周组件日期进行对比,如果相同,那么就设计一个特殊的css啦。
setDayTip(day) {
return this.todo_list.some((item) => {
return item.remind_time.indexOf(day) != -1
})
}
总结
JavaScript和UniApp中,日期的获取和处理在语法上大致相同,主要是对日期的获取以及转换,特别要注意的一点就是日期的格式最好使用标准的ISO 86001作为日期格式,确保跨平台兼容。