自己女朋友有记账的习惯,就想着开发一个记账app给她用,想起来支付宝的账单页面日期选择组件不错,本想偷个懒到网上找一些资源直接使用的,翻了一下代码都不是很全,索性就自己封装了一个,对应的uview依赖全局注册一下,代码就可以直接使用了。
效果图如下,完美复刻:
代码如下:
<template>
<view>
<u-popup :show="isShow" mode="bottom" @close="close" @open="open" :closeOnClickOverlay="true">
<view class="popup-container">
<u-tabs :list="tabList" @click="clickTab" class="tabs" :scrollable="false"></u-tabs><!-- 交易時間 -->
<view v-if="currentTab === 'tab2'">
<text class="heading">交 易 時 間</text>
<view class="buttons-group">
<button class="button button--primary" :class="{ selected: tradeDate === '3m' }"
@click="setDateRange('3m')">近三月</button>
<button class="button button--primary" :class="{ selected: tradeDate === '6m' }"
@click="setDateRange('6m')">近半年</button>
<button class="button button--primary" :class="{ selected: tradeDate === '1y' }"
@click="setDateRange('1y')">近一年</button>
</view>
</view><!-- 自定義清除時間按鈕 -->
<view v-if="currentTab === 'tab2'" class="custom-time-container">
<text class="custom-time">自 定 義</text>
<uni-icons type="trash" size="18" @click="clearCustomTime"></uni-icons>
</view><view v-if="currentTab === 'tab2'" class="custom-time-container-text">
<text class="custom-time-text">最長可查找時間跨度一年的數據</text>
</view><!-- 月份选择内容 -->
<view class=" content" v-if="currentTab === 'tab1'">
<picker-view :value="dateValue" indicator-style="height: 50px;" style="width: 100%; height: 300px;">
<picker-view-column>
<view v-for="(year, index) in years" :key="index" class="picker-item">{{ year }}</view>
</picker-view-column>
<picker-view-column>
<view v-for="(month, index) in months" :key="index" class="picker-item">{{ month }}
</view>
</picker-view-column>
</picker-view>
</view><!-- 自定义时间内容 -->
<view class="date-range-container" v-if="currentTab === 'tab2'">
<view class="date-input" :class="{ selected: currentSelect === 'start' }"
@click="selectDate('start')">
<text>{{ startDateText }}</text>
</view>
<text class="date-separator">至</text>
<view class="date-input" :class="{ selected: currentSelect === 'end' }" @click="selectDate('end')">
<text>{{ endDateText }}</text>
</view>
</view><view class="content-range" v-if="currentTab === 'tab2'">
<picker-view :value="pickerValue" indicator-style="height: 50px;"
style="width: 100%; height: 150px;" @change="pickerChange">
<picker-view-column>
<view v-for="(year, index) in years" :key="index" class="picker-item">{{ year }}</view>
</picker-view-column>
<picker-view-column>
<view v-for="(month, index) in months" :key="index" class="picker-item">{{ month }}</view>
</picker-view-column>
<picker-view-column>
<view v-for="(day, index) in days" :key="index" class="picker-item">{{ day }}</view>
</picker-view-column>
</picker-view>
</view>
</view><!-- 确认按钮 -->
<view class="header-buttons">
<button @click="confirm" class="button button--confirm">確 認</button>
</view>
</u-popup>
</view>
</template><script>
export default {
data() {
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
const currentMonth = currentDate.getMonth();return {
isShow: false,
dateValue: [currentYear - 2020, currentMonth], // 默认选中当前年份和月份
startDateValue: [currentYear - 2020, currentMonth, currentDate.getDate() - 1], // 默认选中当前日期
startDateText: this.$moment().format('YYYY-MM-DD'), // 新增 startDateTextendDateValue: [currentYear - 2020, currentMonth, currentDate.getDate() - 1], // 默认选中当前日期
endDateText: '',pickerValue: [currentYear - 2020, currentMonth, currentDate.getDate() - 1], // 当前选择器的值
currentSelect: 'start', // 用于标记当前选择的是开始日期还是结束日期tradeDate: '',
years: Array.from({
length: 180
}, (v, i) => 2020 + i), // 生成2020到2200年
months: Array.from({
length: 12
}, (v, i) => i + 1), // 生成1到12月
days: Array.from({
length: 31
}, (v, i) => i + 1), // 生成1到31日
tabList: [{
name: '月份选择',
value: 'tab1'
},
{
name: '自定义时间',
value: 'tab2'
}
],
currentTab: 'tab1'
};
},methods: {
open() {
this.isShow = true;
this.currentTab = 'tab1';
this.tradeDate = ''; // 确保打开组件时,tradeDate 被重置
},close() {
this.isShow = false;
},clickTab(item) {
this.currentTab = item.value;
},confirm() {
if (this.currentTab === 'tab1') {
const year = this.years[this.dateValue[0]];
const month = this.months[this.dateValue[1]];
this.$emit('dateSelected', {
year,
month
});
} else if (this.currentTab === 'tab2') {
const startYear = this.years[this.startDateValue[0]];
const startMonth = this.months[this.startDateValue[1]];
const startDay = this.days[this.startDateValue[2]];
const endYear = this.years[this.endDateValue[0]];
const endMonth = this.months[this.endDateValue[1]];
const endDay = this.days[this.endDateValue[2]];
this.$emit('dateRangeSelected', {
startYear,
startMonth,
startDay,
endYear,
endMonth,
endDay
});
}
this.close();
},setDateRange(range) {
let now = new Date();
let start = new Date(now); // 复制当前日期用于计算起始日期if (range === '3m') {
start.setMonth(now.getMonth() - 3);
} else if (range === '6m') {
start.setMonth(now.getMonth() - 6);
} else if (range === '1y') {
start.setFullYear(now.getFullYear() - 1);
}// 更新开始时间
this.startDateText = start.toISOString().split('T')[0];
this.startDateValue = [
this.years.indexOf(start.getFullYear()),
this.months.indexOf(start.getMonth() + 1),
this.days.indexOf(start.getDate())
];// 检查并更新结束时间为当前日期
const todayText = now.toISOString().split('T')[0];
if (this.endDateText !== todayText) {
this.endDateText = todayText;
this.endDateValue = [
this.years.indexOf(now.getFullYear()),
this.months.indexOf(now.getMonth() + 1),
this.days.indexOf(now.getDate())
];
}this.tradeDate = range;
if (this.currentSelect === 'start') {
this.pickerValue = [...this.startDateValue];
} else if (this.currentSelect === 'end') {
this.pickerValue = [...this.endDateValue];
}
},clearCustomTime() {
// 清空选中状态
this.tradeDate = '';
// 清空开始和结束时间
this.startDateText = '';
this.endDateText = '';const now = new Date();
this.startDateValue = [
this.years.indexOf(now.getFullYear()),
this.months.indexOf(now.getMonth() + 1),
this.days.indexOf(now.getDate())
];
this.endDateValue = [...this.startDateValue];
this.pickerValue = [...this.startDateValue];
},selectDate(type) {
this.currentSelect = type;
if (type === 'start' && this.startDateText === '') {
const now = new Date();
this.startDateValue = [
this.years.indexOf(now.getFullYear()),
this.months.indexOf(now.getMonth() + 1),
this.days.indexOf(now.getDate())
];
this.startDateText = now.toISOString().split('T')[0];
} else if (type === 'end' && this.endDateText === '') {
const now = new Date();
this.endDateValue = [
this.years.indexOf(now.getFullYear()),
this.months.indexOf(now.getMonth() + 1),
this.days.indexOf(now.getDate())
];
this.endDateText = now.toISOString().split('T')[0];
}if (type === 'start') {
this.pickerValue = [...this.startDateValue];
} else if (type === 'end') {
this.pickerValue = [...this.endDateValue];
}
},pickerChange(e) {
const value = e.target.value;
if (this.currentSelect === 'start') {
this.startDateValue = value;
this.startDateText =
`${this.years[value[0]]}-${this.months[value[1]].toString().padStart(2, '0')}-${this.days[value[2]].toString().padStart(2, '0')}`;
} else if (this.currentSelect === 'end') {
this.endDateValue = value;
this.endDateText =
`${this.years[value[0]]}-${this.months[value[1]].toString().padStart(2, '0')}-${this.days[value[2]].toString().padStart(2, '0')}`;
}
}
}
};
</script><style lang="scss" scoped>
.popup-container {
position: relative;
width: auto;
height: auto;
padding: 20px 20px;
background-color: #fff;
}.header-buttons {
display: flex;
justify-content: space-between;
padding: 0 20px;
margin-bottom: 10px;
}.button {
background-color: transparent;
border: none;
color: #007bff;
border-radius: 5px;
padding: 5px 15px;
font-size: 16px;
text-align: center;
}.button--confirm {
align-self: flex-end;
background-color: #007bff;
color: #fff;
width: 100%;
}.tabs {
width: 100%;
margin-bottom: 20px;
}.content {
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
height: 300px;
}.content-range {
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}.picker-item {
text-align: center;
line-height: 50px;
/* 确保与 indicator-style 的高度匹配 */
display: flex;
justify-content: center;
align-items: center;
height: 50px;
/* 确保与 indicator-style 的高度匹配 */
}.heading {
text-align: center;
font-size: 15px;
margin-bottom: 10px;
}.buttons-group {
display: flex;
margin-bottom: 20px;
width: 100%;
}.button--primary {
background-color: white;
/* 未选中状态背景色为白色 */
color: #000000;
border-radius: 5px;
padding: 0;
border: 1px solid rgba(0, 0, 0, 0.2);
/* 淡淡的黑色边框 */
height: 25px;
width: 60px;
margin-right: 10px;
margin-left: 0px;
margin-top: 25px;
display: flex;
justify-content: center;
/* 文字居中 */
align-items: center;
/* 文字居中 */
}.button--primary.selected {
border: 1px solid #5555ff;
/* 选中状态边框为淡蓝色 */
color: white;
/* 选中状态文字颜色为白色 */
background-color: #5555ff;
/* 选中状态背景色为淡蓝色 */
}.custom-time-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}.custom-time-container-text {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}.custom-time {
font-size: 15px;
margin-left: 0px;
}.custom-time-text {
font-size: 15px;
margin-left: 0px;
color: #ffaa00;
}.date-range-container {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
margin-top: 20px;
}.date-input {
border-bottom: 2px solid #ccc;
padding: 5px 10px;
margin: 0 10px;
cursor: pointer;
text-align: center;
width: 150px;
/* 固定宽度确保开始时间和结束时间一致 */
height: 30px;
/* 设置高度以确保一致 */
line-height: 30px;
/* 设置行高以居中内容 */
}.date-input.selected {
border-bottom: 2px solid #007bff;
color: #007bff;
}.date-separator {
margin: 0 10px;
}.picker-view-container {
margin-top: 10px;
}
</style>