结构

popupBox_Content_calendar_YYYYMM_radio.vue
<template>
<view class="box">
<view style="height: calc(100%); overflow-y: hidden">
<scroll-view
scroll-y
style="height: 100%"
:scroll-top="scrollTop"
lower-threshold="200"
@scrolltolower="scrolltolower"
refresher-enabled
:refresher-triggered="refresherTriggered"
refresher-background="transparent"
@refresherrefresh="pullDownRefresh"
>
<template v-for="item in YearArr">
<view class="item">
<view class="year">
<view>{{ item.year }}</view>
</view>
<view class="months">
<template v-for="month in item.months">
<view
:class="[
'monthBox',
`Select_first_${selectdate == month ? true : false}`,
]"
:id="`item_${month}`"
@click="select(month)"
v-if="
dateMax == 'infinite'
? true
: [0, -1].includes(compare(dateMax, month))
"
>
<view class="value">{{ dayFormat(month) }}</view>
</view>
</template>
</view>
</view>
</template>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
const props = defineProps({
date: {
type: String,
default:
new Date().getFullYear() +
"-" +
(new Date().getMonth() + 1 + "").padStart(2, "0"),
},
dateMax: {
type: String,
default: () => {
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = (currentDate.getMonth() + 1 + "").padStart(2, "0");
return `${year}-${month}`;
},
},
});
const YearArr = ref([]);
const dateMax = ref(props.dateMax);
const selectdate = ref(props.date);
onLoad(() => {
let arr = [];
let year;
year = new Date(selectdate.value).getFullYear();
for (let index = 0; index < 3; index++) {
arr.unshift({
year,
months: [
year + "-01",
year + "-02",
year + "-03",
year + "-04",
year + "-05",
year + "-06",
year + "-07",
year + "-08",
year + "-09",
year + "-10",
year + "-11",
year + "-12",
],
});
year -= 1;
}
YearArr.value = arr;
setTimeout(() => {
scrollToElement(selectdate.value);
}, 500);
});
function select(date) {
selectdate.value = `${date}`;
}
function dayFormat(date) {
let day = new Date(date).getMonth() + 1 + "月";
return day;
}
function compare(date1, date2) {
let newDate1 = date1.replace(/-/g, "");
let newDate2 = date2.replace(/-/g, "");
if (newDate1 == newDate2) {
return 0;
}
if (newDate1 < newDate2) {
return 1;
}
if (newDate1 > newDate2) {
return -1;
}
}
const refresherTriggered = ref(false);
function pullDownRefresh() {
refresherTriggered.value = true;
let year = YearArr.value[0].year - 1;
setTimeout(() => {
refresherTriggered.value = false;
YearArr.value.unshift({
year,
months: [
year + "-01",
year + "-02",
year + "-03",
year + "-04",
year + "-05",
year + "-06",
year + "-07",
year + "-08",
year + "-09",
year + "-10",
year + "-11",
year + "-12",
],
});
}, 500);
}
function scrolltolower() {
if (
dateMax.value !== "infinite" &&
compare(YearArr.value[YearArr.value.length - 1].months[11],dateMax.value) !== 1
) {
return;
}
let year = YearArr.value[YearArr.value.length - 1].year + 1;
YearArr.value.push({
year,
months: [
year + "-01",
year + "-02",
year + "-03",
year + "-04",
year + "-05",
year + "-06",
year + "-07",
year + "-08",
year + "-09",
year + "-10",
year + "-11",
year + "-12",
],
});
}
const scrollTop = ref(0);
function scrollToElement(domid) {
console.log("domid", "#item_" + domid);
uni
.createSelectorQuery()
.select("#item_" + domid)
.boundingClientRect((rect) => {
if (rect) {
scrollTop.value = rect.top;
}
})
.exec();
document.querySelector("#item_" + domid).scrollIntoView(true);
}
defineExpose({
selectdate,
dateMax,
});
</script>
<style lang="scss" scoped>
.box {
// transform: scaleY(-1);
flex: 1;
position: relative;
overflow-y: hidden;
// padding-right: 20rpx;
}
.item {
// transform: scaleY(-1);
.year {
display: flex;
justify-content: center;
align-items: center;
height: 50px;
font-weight: 600;
}
.months {
display: flex;
justify-content: left;
flex-wrap: wrap;
.monthBox {
height: 100rpx;
width: 25%;
display: flex;
align-items: center;
justify-content: center;
// background-color:red;
}
.Select_true {
color: yellow;
}
.Select_disable_true {
position: relative;
&:before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 80rpx;
width: 80rpx;
border-radius: 50%;
background-color: #b5b5b5;
}
.value {
color: #fff;
z-index: 9;
}
}
.Select_first_true {
position: relative;
&:before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 80rpx;
width: 130rpx;
border-radius: 10rpx;
background-color: #337cfa;
}
.value {
color: #fff;
z-index: 9;
}
}
.Select_last_true {
color: blue;
}
}
}
</style>
popupBox_calendar_YYYYMM_radio.vue,使用弹窗容器包裹,完成最后的组件
<template>
<popupBox ref="YYYYMM_Popup" :onConfirmDisable="onConfirmDisable">
<template #title>
<view class="top">
<text class="title">选择月份</text>
<text class="remark"
>已选:{{ popupBox_Content_calendar_YYYYMM_Ref?.selectdate }}</text
>
</view>
</template>
<template #content>
<popupBox_Content_calendar_YYYYMM
ref="popupBox_Content_calendar_YYYYMM_Ref"
:date="date"
></popupBox_Content_calendar_YYYYMM>
</template>
</popupBox>
</template>
<script setup>
import popupBox_Content_calendar_YYYYMM from "./popupBox_Content_calendar_YYYYMM_radio.vue";
import { ref, computed, onMounted, watch, defineEmits, nextTick } from "vue";
const popupBox_Content_calendar_YYYYMM_Ref = ref(null);
const YYYYMM_Popup = ref(null);
const emit = defineEmits(["update:modelValue", "onConfirm"]);
const onConfirmDisable = computed(() => {
if (popupBox_Content_calendar_YYYYMM_Ref.value?.selectdate[0]) {
return false;
} else {
return true;
}
});
const date = ref("");
function open(params) {
if (params.date) {
date.value = params.date;
} else {
date.value =
new Date().getFullYear() +
"-" +
(new Date().getMonth() + 1 + "").padStart(2, "0");
}
nextTick(() => {
YYYYMM_Popup.value.open({
cancelText: "取消",
confirmText: "确认",
onOpen: function () {
if (params.dateMax) {
popupBox_Content_calendar_YYYYMM_Ref.value.dateMax = params.dateMax;
}
},
onConfirm: function () {
if (params.onConfirm) {
params.onConfirm(
popupBox_Content_calendar_YYYYMM_Ref.value.selectdate
);
}
emit(
"onConfirm",
popupBox_Content_calendar_YYYYMM_Ref.value.selectdate
);
YYYYMM_Popup.value.cancel();
},
});
});
}
defineExpose({
open,
});
</script>
<style scoped lang="scss">
:deep {
.popBox {
.content {
// transform: scaleY(-1);
display: flex;
}
}
}
.top {
.title {
font-size: 34.35rpx;
font-weight: 600;
}
.remark {
margin-left: 30rpx;
color: rgba(122, 122, 122, 1);
font-size: 26.71rpx;
}
}
</style>
使用及效果
<template>
<view @click="open">选月 (单选)</view>
<!-- 日历月单选 -->
<popupBox_calendar_YYYYMM_radio ref="YYYYMM_Popup_radio"></popupBox_calendar_YYYYMM_radio>
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
const date = ref("2023-12")
const YYYYMM_Popup_radio = ref(null);
function open() {
YYYYMM_Popup_radio.value.open({
date: date3.value,
dateMax: "2024-01",
onConfirm: (value) => {
date3.value = value;
},
});
}
</script>