Files

345 lines
10 KiB
Vue
Raw Permalink Normal View History

<template>
<uni-popup
ref="popup"
type="bottom"
borderRadius="10px 10px 0 0"
background-color="#FFFFFF"
:mask-click="maskClick"
>
<view class="popup-content">
<view class="popup-header">
<view class="btn-cancel" @click="cancel">取消</view>
<view class="title">{{ title }}</view>
<view class="btn-confirm" @click="confirm">确认</view>
</view>
<view class="popup-list">
<picker-view
indicator-style="height: 84rpx;"
:value="selectedIndex"
@change="bindChange"
class="picker-view"
>
<picker-view-column>
<view
v-for="(year, index) in years"
:key="index"
class="item"
:class="{ 'item-active': selectedIndex[0] === index }"
>
<text>{{ year }}</text>
<text></text>
</view>
</picker-view-column>
<picker-view-column>
<view
v-for="(month, index) in months"
:key="index"
class="item"
:class="{ 'item-active': selectedIndex[1] === index }"
>
<text>{{ month }}</text>
<text></text>
</view>
</picker-view-column>
<picker-view-column>
<view
v-for="(day, index) in days"
:key="index"
class="item"
:class="{ 'item-active': selectedIndex[2] === index }"
>
<text>{{ day }}</text>
<text></text>
</view>
</picker-view-column>
</picker-view>
</view>
</view>
</uni-popup>
</template>
<script>
export default {
name: 'datePicker',
data() {
return {
maskClick: false,
title: '选择日期',
confirmCallback: null,
cancelCallback: null,
changeCallback: null,
selectedIndex: [0, 0, 0],
selectedDate: '',
// 日期数据
years: [],
months: [],
days: [],
// 配置
startYear: 0,
endYear: 0,
};
},
created() {
this.initDateData();
},
methods: {
// 初始化日期数据
initDateData() {
const currentYear = new Date().getFullYear();
this.startYear = currentYear - 50; // 往前50年
this.endYear = currentYear + 10; // 往后10年
// 生成年份
this.years = [];
for (let i = this.startYear; i <= this.endYear; i++) {
this.years.push(i);
}
// 生成月份
this.months = [];
for (let i = 1; i <= 12; i++) {
this.months.push(i);
}
// 初始天数(默认当前年月)
this.updateDays(this.years[0], this.months[0]);
},
// 根据年月更新天数
updateDays(year, month) {
const daysInMonth = new Date(year, month, 0).getDate();
this.days = [];
for (let i = 1; i <= daysInMonth; i++) {
this.days.push(i);
}
},
open(config = {}) {
const {
title = '选择日期',
success,
cancel,
change,
maskClick = false,
defaultDate = '',
} = config;
this.reset();
this.title = title;
if (typeof success === 'function') this.confirmCallback = success;
if (typeof cancel === 'function') this.cancelCallback = cancel;
if (typeof change === 'function') this.changeCallback = change;
this.maskClick = maskClick;
// 设置默认选中
this.setDefaultDate(defaultDate);
this.$nextTick(() => {
this.$refs.popup.open();
});
},
close() {
this.$refs.popup.close();
},
// 设置默认日期
setDefaultDate(dateStr) {
if (!dateStr) {
// 没有默认日期,使用当前日期
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
const day = now.getDate();
this.selectedIndex = [
this.years.findIndex(y => y === year),
this.months.findIndex(m => m === month),
this.days.findIndex(d => d === day)
];
} else {
// 解析日期字符串 (支持 YYYY-MM-DD 格式)
const [year, month, day] = dateStr.split('-').map(Number);
this.selectedIndex = [
this.years.findIndex(y => y === year),
this.months.findIndex(m => m === month),
this.days.findIndex(d => d === day)
];
}
// 确保索引有效
this.selectedIndex = this.selectedIndex.map((index, i) =>
index === -1 ? 0 : index
);
this.updateSelectedDate();
},
bindChange(e) {
this.selectedIndex = e.detail.value;
// 检查是否需要更新天数
const oldDaysLength = this.days.length;
const selectedYear = this.years[this.selectedIndex[0]];
const selectedMonth = this.months[this.selectedIndex[1]];
this.updateDays(selectedYear, selectedMonth);
// 如果天数变化且当前选择的日期超过新月份的天数,调整日期索引
if (this.days.length !== oldDaysLength && this.selectedIndex[2] >= this.days.length) {
this.selectedIndex[2] = this.days.length - 1;
}
this.updateSelectedDate();
// 触发change回调
this.changeCallback && this.changeCallback(this.selectedDate, this.selectedIndex);
},
// 更新选中的日期字符串
updateSelectedDate() {
const year = this.years[this.selectedIndex[0]];
const month = this.months[this.selectedIndex[1]].toString().padStart(2, '0');
const day = this.days[this.selectedIndex[2]].toString().padStart(2, '0');
this.selectedDate = `${year}-${month}-${day}`;
},
cancel() {
this.clickCallback(this.cancelCallback);
},
confirm() {
this.clickCallback(this.confirmCallback);
},
async clickCallback(callback) {
if (typeof callback !== 'function') {
this.$refs.popup.close();
return;
}
try {
const result = await callback(this.selectedDate, this.selectedIndex);
if (result !== false) {
this.$refs.popup.close();
}
} catch (error) {
console.error('callback 执行出错:', error);
}
},
reset() {
this.maskClick = false;
this.confirmCallback = null;
this.cancelCallback = null;
this.changeCallback = null;
this.selectedIndex = [0, 0, 0];
this.selectedDate = '';
this.title = '选择日期';
},
// 设置日期范围
setDateRange(startYear, endYear) {
this.startYear = startYear;
this.endYear = endYear;
// 重新生成年份
this.years = [];
for (let i = this.startYear; i <= this.endYear; i++) {
this.years.push(i);
}
// 重置选中索引
this.selectedIndex[0] = 0;
this.updateDays(this.years[0], this.months[0]);
},
},
};
</script>
<style lang="scss" scoped>
.popup-content {
color: #000000;
height: 50vh;
}
.popup-list {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
justify-content: space-evenly;
flex: 1;
overflow: hidden;
.picker-view {
width: 100%;
height: calc(50vh - 100rpx);
margin-top: 20rpx;
.uni-picker-view-mask {
background: rgba(0, 0, 0, 0);
}
.item {
line-height: 84rpx;
height: 84rpx;
text-align: center;
font-weight: 400;
font-size: 32rpx;
color: #cccccc;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
}
.item-active {
color: #333333;
}
.uni-picker-view-indicator:after {
border-color: #e3e3e3;
}
.uni-picker-view-indicator:before {
border-color: #e3e3e3;
}
}
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 40rpx 40rpx 10rpx 40rpx;
.title {
font-weight: 500;
font-size: 36rpx;
color: #333333;
text-align: center;
}
.btn-cancel {
font-weight: 400;
font-size: 32rpx;
color: #666d7f;
line-height: 38rpx;
}
.btn-confirm {
font-weight: 400;
font-size: 32rpx;
color: #256bfa;
}
}
2025-12-05 18:09:45 +08:00
</style>
<style lang="scss" scoped>
@media(min-width: 800px){
.popup-header {
.title {
font-size: 40rpx;
}
.btn-cancel {
font-size: 36rpx;
}
.btn-confirm {
font-size: 36rpx;
}
}
}
</style>