效果图:
一、问题1:每个月的1号,样式上的起始位置
样式上来说实际困难点在于每个月的1号对应的位置:
解决方式就是判断1号是周几,就在前面放几个空盒子,
二、问题2 : 状态样式控制
定义一个结构来存储各种状态值,以达到改变样式的目的
dateList=[
{
month:'2025.01'
dayList:[
{
number: "1",//年月日的日
date: "2025.01.01",
startIndex: "flag",//flag:起租 结束 标识 zhong:介于起租 和结束中间数据标识
disable: false,//是否可以点击 true:不能点击 false:可以点击
text: "起租",
},
....以此类推
]
},
{
month:'2025.02'
....以此类推
},
]
三、代码 vue3
<template>
<view class="calendar_box">
<up-navbar
leftIconSize="32"
:placeholder="true"
bgColor="#fff"
style="height: 100rpx; width: 100%"
title=""
:autoBack="true"
></up-navbar>
<!-- 日历 -->
<view class="calendar_week_box">
<!-- 周 -->
<view class="calendar_yellow_tishi">
提示:至低起租日为{{ leaseToday }},请确认您的选择!
</view>
<view class="calendar_week_item_box">
<view class="calendar_week_item">日</view>
<view class="calendar_week_item">一</view>
<view class="calendar_week_item">二</view>
<view class="calendar_week_item">三</view>
<view class="calendar_week_item">四</view>
<view class="calendar_week_item">五</view>
<view class="calendar_week_item">六</view>
</view>
<!-- 日期 -->
<!-- {{ daysBetween(leaseStartDate,leaseEndDate) }}--{{ leaseStartDate}}--{{leaseEndDate }} -->
<view class="calendar_date_list_box">
<view
class="calendar_date_list_item"
v-for="item in dateList"
:key="item.month"
>
<view class="calendar_date_nian_yue">{{ item.month }}</view>
<view class="calendar_date_ri_box">
<view
v-for="(j, index) in item.dayList"
:key="index + item.month"
:style="j.disable ? 'color:#BFBFBF' : ''"
@tap="j.disable | !j.number ? '' : selectDate(item.month, index)"
:class="
j.startIndex == 'flag'
? 'calendar_date_ri_item calendar_date_ri_active'
: 'calendar_date_ri_item'
"
:id="j.startIndex == 'zhong' ? (j.number ? 'zhong' : '') : ''"
>
<view class="text">{{ j.text }}</view>
{{ j.number }}
</view>
</view>
</view>
</view>
<!-- 按钮 -->
<view class="calendar_add_button_box">
<view class="calender_price_box" v-if="daysBetween(leaseStartDate,leaseEndDate)">
<view class="calender_price_shifu">
实付租金
<image src="/static/img/goods/$.png"></image>
<view>
<text class="bigprice">126.</text>
<text class="smallprice">49</text>
</view>
</view>
<view class="calender_price_num_down">
<view class="calender_price_yajin">
总租金:
<image src="/static/img/goods/$.png"></image>
<text class="yajinprice">126.49</text>
</view>
<view class="calender_price_day">共7天,18.07元/天</view>
</view>
<view class="calender_price_kuaidi">
快递时间、开始时间、结束时间均是预计时间,具体时间以实际为准。
</view>
</view>
<view class="calendar_add_button">
<view class="address_but" @tap="toBackPage">确认</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { onLoad, onShow, onUnload } from "@dcloudio/uni-app";
import { computed, nextTick, ref, reactive } from "vue";
/**
* 跳转页面---地址
* */
function toBackPage(type) {
uni.navigateBack({
delta: 1,
});
}
let leaseToday = ref("");
getstartDay();
function getstartDay() {
// 获取当前日期
const today = new Date();
// 创建一个新的日期对象,表示今天的日期
const tomorrow = new Date(today);
// 将日期设置为明天
tomorrow.setDate(today.getDate() + 3);
// 格式化日期为 YYYY-MM-DD
const year = tomorrow.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0"); // 月份从0开始,所以需要加1
const day = String(tomorrow.getDate()).padStart(2, "0");
leaseToday.value = `${year}-${month}-${day}`;
}
// 起租时间和结束时间
let leaseStartDate = ref("");
let leaseEndDate = ref("");
/**
* 计算租赁的天数
* */
function daysBetween(dateStr1, dateStr2) {
if (!dateStr1 & !dateStr2) {
return 0;
}
// 将日期字符串转换为 Date 对象
const date1 = new Date(dateStr1);
const date2 = new Date(dateStr2);
// 获取时间戳(毫秒数)
const timeDifference = Math.abs(date2 - date1);
// 将时间戳转换为天数
const dayDifference = Math.ceil(timeDifference / (1000 * 60 * 60 * 24));
return dayDifference + 1;
}
/**
* 生成一个包含从当前月份开始往后12个月的日期列表,并根据要求处理了以下内容:
确定每个月的第一天是星期几,从而决定前面需要填充多少个空对象。
标记今天的日期,并设置相应的属性。
为特定日期(如起租和结束)添加 startIndex 和 text 字段。
* */
function generateDateList() {
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth();
const dateList = [];
function createMonthData(year, month) {
const monthStr = `${year}.${String(month + 1).padStart(2, "0")}`;
const daysInMonth = new Date(year, month + 1, 0).getDate();
const firstDayOfWeek = new Date(year, month, 1).getDay(); // 获取当月第一天是星期几
const dayList = [];
// 根据当月第一天是星期几,填充前面的空数据
for (let i = 0; i < firstDayOfWeek; i++) {
dayList.push({ number: "" });
}
for (let i = 1; i <= daysInMonth; i++) {
const dateStr = `${year}.${String(month + 1).padStart(2, "0")}.${String(
i
).padStart(2, "0")}`;
const isToday =
year === today.getFullYear() &&
month === today.getMonth() &&
i === today.getDate();
const isMing =
year === today.getFullYear() &&
month === today.getMonth() &&
i === today.getDate() + 1;
const isHout =
year === today.getFullYear() &&
month === today.getMonth() &&
i === today.getDate() + 2;
const dayObj = {
number: isToday ? "今天" : String(i),
date: dateStr,
startIndex: "",
disable: isToday || isMing || isHout || new Date(dateStr) <= today,
text: "",
};
dayList.push(dayObj);
}
return { month: monthStr, dayList };
}
// 生成从当前月份开始的往后12个月的数据
for (let i = 0; i < 12; i++) {
const month = (currentMonth + i) % 12;
const year = currentMonth + i >= 12 ? currentYear + 1 : currentYear;
dateList.push(createMonthData(year, month));
}
return dateList;
}
/**
* dateList=[
* {
* month:'2025.01'
* dayList:[
* {
number: "1",//年月日的日
date: "2025.01.01",
startIndex: "flag",//flag:起租 结束 标识 zhong:介于起租 和结束中间数据标识
disable: false,//是否可以点击 true:不能点击 false:可以点击
text: "起租",
},
....以此类推
* ]
* },
* {
* month:'2025.02'
* ...
* },
* ]
*
* */
let dateList = reactive(generateDateList());
/**
* 点击 选择起租时间
* 特殊日期的处理,例如起租和结束
* */
let clickNum = ref(0);
function selectDate(month, index) {
clickNum.value++;
// console.log(month, index, "---时间");
dateList.forEach((item) => {
if (item.month == month) {
if (clickNum.value % 2 == 0) {
// 偶数
item.dayList[index].text = "结束";
item.dayList[index].startIndex = "flag";
} else {
// 遍历 dateList 并将每个对象的 startIndex 属性设置为空字符串
leaseStartDate.value=''
leaseEndDate.value=''
dateList.forEach((n) => {
n.dayList.forEach((m, z) => {
m.startIndex = "";
m.text = "";
});
});
item.dayList[index].text = "起租";
item.dayList[index].startIndex = "flag";
}
lisyZhong(item.dayList);
}
});
}
/**
*给租赁期间的时间添加样式
* */
function lisyZhong() {
// 找到所有 startIndex 为 "flag" 的索引
let flagIndices = [];
let flagMonth = [];
let arr = [];
dateList.forEach((monthData, n) => {
monthData.dayList.forEach((day, index) => {
if (day.startIndex === "flag") {
flagIndices.push(index);
arr.push(n);
// console.log("---前-", arr);
// 使用 Set 对数组进行去重
flagMonth = [...new Set(arr)];
// 对去重后的数组进行排序
flagMonth.sort((a, b) => a - b);
// console.log("-后---", flagMonth);
}
});
});
let startIdx = "";
let endIdx = "";
if (flagIndices.length >= 2) {
startIdx = flagIndices[0] + 1;
endIdx = flagIndices[1];
}
flagMonth.forEach((item, index) => {
if (flagMonth.length == 1) {
leaseStartDate.value = dateList[item].dayList[startIdx - 1].date;
leaseEndDate.value = dateList[item].dayList[endIdx].date;
dateList[item].dayList[startIdx - 1].text = "起租";
// dateList[item].dayList[startIdx - 2].text = "快递";
// dateList[item].dayList[startIdx - 3].text = "快递";
dateList[item].dayList[endIdx].text = "结束";
for (let i = startIdx; i < endIdx; i++) {
dateList[item].dayList[i].startIndex = "zhong";
}
}
if (flagMonth.length > 1) {
if (index == 0) {
leaseStartDate.value = dateList[item].dayList[startIdx - 1].date;
dateList[item].dayList[startIdx - 1].text = "起租";
dateList[item].dayList.forEach(() => {
for (let a = startIdx; a < dateList[item].dayList.length; a++) {
dateList[item].dayList[a].startIndex = "zhong";
}
});
} else {
leaseEndDate.value = dateList[item].dayList[endIdx].date;
dateList[item].dayList[endIdx].text = "结束";
dateList[item].dayList.forEach(() => {
for (let a = 0; a < endIdx; a++) {
dateList[item].dayList[a].startIndex = "zhong";
}
});
}
}
});
findAndAssignTargetDays();
}
/**
* 查找快递的时间
* */
function findAndAssignTargetDays() {
for (let i = 0; i < dateList.length; i++) {
const month = dateList[i];
for (let j = 0; j < month.dayList.length; j++) {
const day = month.dayList[j];
if (day.startIndex === "flag" && day.number !== "") {
let targetDays;
if (j >= 2) {
// 返回当前 dayList 的前两个数据
targetDays = month.dayList.slice(j - 2, j);
} else if (i > 0) {
// 获取前一个 month 的 dayList
const previousMonth = dateList[i - 1];
const previousDayList = previousMonth.dayList;
targetDays = previousDayList.slice(-2);
} else {
// 如果这是第一个 month 且没有前一个 month,则返回空数组
return [];
}
// 检查找到的两个数据的 number 是否为空,如果为空则继续往前找
while (targetDays.some((d) => d.number === "")) {
if (i > 0) {
const previousMonth = dateList[--i];
const previousDayList = previousMonth.dayList;
targetDays = previousDayList.slice(-2);
} else {
return []; // 如果没有更多的 dayList 可以检查,则返回空数组
}
}
// 将找到的两个数据的 text 属性赋值为 '快递'
targetDays.forEach((targetDay) => {
targetDay.text = "快递";
});
return targetDays;
}
}
}
// 如果没有找到 startIndex: "flag",则返回空数组
return [];
}
</script>
<style lang="less" scoped>
.calendar_box {
position: relative;
min-width: 750rpx;
height: 100vh;
background-color: #fff;
color: #000000;
box-sizing: border-box;
display: flex;
flex-direction: column;
// justify-content: space-between;
// 周
.calendar_week_box {
background: #ffffff;
box-shadow: 0rpx 7rpx 10rpx 0rpx #f6f8f8;
.calendar_yellow_tishi {
height: 90rpx;
line-height: 90rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 26rpx;
color: #ff9348;
text-align: center;
}
.calendar_week_item_box {
padding: 0 28rpx;
box-sizing: border-box;
height: 90rpx;
line-height: 90rpx;
display: flex;
justify-content: space-around;
font-family: PingFangSC, PingFang SC;
font-weight: bold;
font-size: 24rpx;
color: #333333;
box-shadow: 0px 4px 8px #eeeeee;
}
}
// 按钮
.calendar_add_button_box {
width: 100%;
position: fixed;
bottom: 0;
background-color: #fff;
.calendar_add_button {
height: 114rpx;
box-sizing: border-box;
padding: 18rpx 32rpx 12rpx;
border-top: 1px solid #dddddd;
.address_but {
height: 84rpx;
line-height: 84rpx;
background: #00c8be;
border-radius: 42rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #ffffff;
text-align: center;
}
}
.calender_price_box {
padding:19rpx 30rpx ;
border-top: 1px solid #dddddd;
height: 168rpx;
background: #ffffff;
width: 100%;
box-sizing: border-box;
display: flex;
justify-content: space-between;
flex-direction: column;
.calender_price_shifu {
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 28rpx;
color: #262626;
line-height: 40rpx;
display: flex;
align-items: center;
justify-content: flex-start;
.bigprice {
font-size: 50rpx;
color: #ea4444;
}
.smallprice {
font-size: 30rpx;
color: #ea4444;
}
image {
width: 18rpx;
height: 24rpx;
}
}
.calender_price_num_down {
display: flex;
align-items: center;
justify-content: space-between;
.calender_price_day {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20rpx;
color: #8c8c8c;
line-height: 28rpx;
}
.calender_price_yajin {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20rpx;
color: #ea4444;
line-height: 28rpx;
display: flex;
align-items: center;
justify-content: flex-end;
image {
width: 12rpx;
height: 16rpx;
}
}
}
.calender_price_kuaidi {
padding-top: 8rpx;
height: 28rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20rpx;
color: #bfbfbf;
line-height: 28rpx;
}
}
}
// 日期
.calendar_date_list_box {
height: calc(100vh - 98rpx - 98rpx - 114rpx - 100rpx);
overflow-y: auto;
.calendar_date_list_item {
padding-bottom: 35rpx;
}
.calendar_date_nian_yue {
height: 125rpx;
line-height: 125rpx;
font-family: PingFangSC, PingFang SC;
font-weight: bold;
font-size: 32rpx;
color: #333333;
text-align: center;
}
.calendar_date_ri_box {
margin: 0 28rpx;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
gap: 21rpx;
overflow: hidden;
.calendar_date_ri_item {
margin-bottom: 6rpx;
height: 80rpx;
width: 80rpx;
display: flex;
// align-items: center;
flex-direction: column;
justify-content: center;
text-align: center;
// background-color: #e99898;
.text {
font-family: PingFangSC, PingFang SC;
font-weight: bold;
font-size: 20rpx;
color: #c1c1c1;
}
}
.calendar_date_ri_active {
background-color: #00c8be;
color: #fff;
position: relative;
border-radius: 10rpx;
.text {
font-family: PingFangSC, PingFang SC;
font-weight: bold;
font-size: 20rpx;
color: #fff;
}
&:last-child {
// &::after {
// content: "8888";
// display: block;
// position: absolute;
// top: -10rpx;
// left: 50%;
// width: 100rpx;
// height: 20rpx;
// background-color: #e27e7e;
// }
}
.jingtao_modal {
position: absolute;
top: -45rpx;
left: 50%;
width: 364rpx;
line-height: 78rpx;
height: 78rpx;
background: #4c4c4c;
border-radius: 16rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #ffffff;
}
}
#zhong {
background: #e5f9f8;
position: relative;
&::after {
content: "";
height: 100%;
width: 21rpx;
position: absolute;
right: -21rpx;
background-color: #e5f9f8;
}
&::before {
content: "";
height: 100%;
width: 21rpx;
position: absolute;
left: -21rpx;
background-color: #e5f9f8;
}
}
}
}
}
</style>