#创作灵感
公司业务需要,某个时间节点前可以选择到月,某个时间节点后只能选择季度
vue2 Vant2
javascript
import { Cascader, Field, Form, Popup, Button } from 'vant';
import 'vant/lib/index.css';
export default {
name: 'CascaderPage',
components: {
VanCascader: Cascader,
VanField: Field,
VanForm: Form,
VanPopup: Popup,
VanButton: Button
},
props: {
title: {
type: String,
default: '选择时间'
},
// 起始年份,默认从当前年往前5年
startYear: {
type: Number,
default: new Date().getFullYear() - 5
},
// 结束年份,默认从当前年往后5年
endYear: {
type: Number,
default: new Date().getFullYear() + 5
}
},
data() {
return {
showPopup: false,
cascaderValue: '',
fieldValue: '',
selectedResult: '',
fieldNames: {
text: 'label',
value: 'value',
children: 'children'
}
};
},
computed: {
// 生成级联选择的选项
options() {
const yearOptions = [];
// 生成年份选项
for (let year = this.startYear; year <= this.endYear; year++) {
const quarterOptions = [];
// 生成季度选项
for (let quarter = 1; quarter <= 4; quarter++) {
const quarterObj = {
label: `${year}年第${quarter}季度`,
value: `${year}-Q${quarter}`
// 移除默认children属性,避免空选项
};
// 判断是否需要生成月份选项
// 规则:2025年9月及之前需要月份,之后不需要
if (year < 2025) {
// 2025年之前,所有季度都需要月份
quarterObj.children = this.getMonthsByQuarter(quarter);
} else if (year === 2025) {
// 2025年,只有前3季度需要月份
if (quarter <= 3) {
quarterObj.children = this.getMonthsByQuarter(quarter);
}
}
// 2025年之后的年份,不生成月份选项
quarterOptions.push(quarterObj);
}
yearOptions.push({
label: `${year}年`,
value: year.toString(),
children: quarterOptions
});
}
return yearOptions;
}
},
methods: {
// 根据季度获取对应的月份
getMonthsByQuarter(quarter) {
const months = [];
let startMonth, endMonth;
switch (quarter) {
case 1:
startMonth = 1;
endMonth = 3;
break;
case 2:
startMonth = 4;
endMonth = 6;
break;
case 3:
startMonth = 7;
endMonth = 9;
break;
case 4:
startMonth = 10;
endMonth = 12;
break;
default:
return [];
}
for (let month = startMonth; month <= endMonth; month++) {
months.push({
label: `${month}月`,
value: month.toString()
});
}
return months;
},
// 处理选择变化 - 只在选择完最后一级选项时关闭
handleChange(value) {
const { selectedOptions, tabIndex } = value;
console.log('选择变化:', value, selectedOptions, tabIndex);
// 如果没有选择值,不处理
if (!selectedOptions || selectedOptions.length === 0) return;
// 获取当前选中的年份
const selectedYear = parseInt(selectedOptions[0].value);
// 构建正确格式的selectedValues数组
const selectedValues = [];
selectedValues.push(selectedOptions[0].value); // 年份
if (selectedOptions.length >= 2) {
// 对于季度,提取数字部分
const quarterStr = selectedOptions[1].value;
if (quarterStr.includes('-Q')) {
selectedValues.push(quarterStr.split('-Q')[1]); // 只提取季度数字
} else {
selectedValues.push(quarterStr);
}
}
if (selectedOptions.length === 3) {
selectedValues.push(selectedOptions[2].value); // 月份
}
// 检查是否已经选择到最后一级
if (selectedOptions.length === 2) {
// 情况1: 如果是2025年之后或2025年第4季度,选择完季度就关闭
const quarterValue = selectedOptions[1].value;
const quarter = parseInt(quarterValue.includes('-Q') ? quarterValue.split('-Q')[1] : quarterValue);
if (selectedYear > 2025 || (selectedYear === 2025 && quarter === 4)) {
this.onConfirm(selectedValues, selectedOptions);
}
// 其他情况:选择完季度后继续选择月份,不关闭
} else if (selectedOptions.length === 3) {
// 情况2: 选择完月份后关闭
this.onConfirm(selectedValues, selectedOptions);
}
},
// 确认选择
onConfirm(selectedValues, selectedOptions) {
if (!selectedValues || selectedValues.length === 0) return;
// 格式化选中的结果
const formattedResult = this.formatSelectedResult(selectedValues);
this.fieldValue = formattedResult;
this.selectedResult = `您选择了:${formattedResult}`;
this.showPopup = false;
},
// 格式化选中的结果
formatSelectedResult(selectedValues) {
if (!selectedValues || selectedValues.length === 0) return '';
// 处理selectedValues可能是字符串的情况
let values = Array.isArray(selectedValues) ? selectedValues : [selectedValues];
// 如果只有一个值,尝试从值中解析出年份
if (values.length === 1) {
const value = values[0];
// 检查是否是季度格式
if (typeof value === 'string' && value.includes('-Q')) {
const parts = value.split('-Q');
const year = parts[0];
const quarter = parts[1];
return `${year}年第${quarter}季度`;
}
// 只显示年份
return `${value}年`;
}
let result = '';
// 年份
result += `${values[0]}年`;
// 季度
if (values.length >= 2) {
let quarter;
if (typeof values[1] === 'string' && values[1].includes('-Q')) {
// 处理字符串格式的季度值
quarter = values[1].split('-Q')[1];
} else {
// 直接使用季度值
quarter = values[1];
}
result += `第${quarter}季度`;
}
// 月份 - 确保月份显示为两位数格式
if (values.length === 3) {
const month = parseInt(values[2]);
// 格式化月份为两位数
const formattedMonth = month < 10 ? `0${month}` : `${month}`;
result += `${formattedMonth}月`;
}
return result;
},
// 表单提交
onSubmit(values) {
if (!this.fieldValue) {
this.$toast('请选择时间');
return;
}
this.$toast(`提交成功:${this.fieldValue}`);
}
},
mounted() {
// 生命周期钩子:设置默认选中最后一项
try {
if (this.options.length > 0) {
const lastYear = this.options[this.options.length - 1];
if (lastYear.children && lastYear.children.length > 0) {
const lastQuarter = lastYear.children[lastYear.children.length - 1];
const selected = [
lastYear.value,
lastQuarter.value
];
// 如果有月份选项,也选中最后一个
if (lastQuarter.children && lastQuarter.children.length > 0) {
selected.push(lastQuarter.children[lastQuarter.children.length - 1].value);
}
this.cascaderValue = selected.join('/');
this.fieldValue = this.formatSelectedResult(selected);
this.selectedResult = `您选择了:${this.fieldValue}`;
}
}
} catch (error) {
console.error('设置默认选中项失败:', error);
}
}
};
HTML
<div class="cascader-container">
<h2>Vant 时间级联选择器示例</h2>
<van-form @submit="onSubmit">
<div class="field-container">
<van-field
v-model="fieldValue"
readonly
label="时间选择"
placeholder="请选择时间"
:value="fieldValue"
/>
<van-button
type="primary"
@click="showPopup = true"
style="margin-left: 10px; min-width: 100px;"
>
选择时间
</van-button>
</div>
</van-form>
<!-- Popup弹窗 -->
<van-popup
v-model="showPopup"
round
position="bottom"
:style="{ height: '80%' }"
>
<div class="popup-header">
<h3>{{ title }}</h3>
<van-button
type="text"
@click="showPopup = false"
style="position: absolute; right: 16px; top: 16px;"
>
取消
</van-button>
</div>
<van-cascader
v-model="cascaderValue"
:options="options"
:field-names="fieldNames"
@change="handleChange"
@close="showPopup = false"
placeholder=""
/>
</van-popup>
<div class="result-section" v-if="selectedResult">
<h3>选择结果</h3>
<p>{{ selectedResult }}</p>
</div>
</div>
css
.cascader-container {
padding: 20px;
max-width: 600px;
margin: 0 auto;
}
.field-container {
display: flex;
align-items: center;
}
.popup-header {
padding: 16px;
border-bottom: 1px solid #eee;
position: relative;
text-align: center;
}
.popup-header h3 {
margin: 0;
color: #333;
font-size: 16px;
}
.cascader-container h2 {
text-align: center;
margin-bottom: 20px;
color: #333;
}
.result-section {
margin-top: 30px;
padding: 15px;
background-color: #f5f5f5;
border-radius: 8px;
}
.result-section h3 {
margin-bottom: 10px;
color: #666;
}
.result-section p {
color: #333;
font-size: 16px;
}
vue3 vant4
HTML TS
<template>
<div class="date-cascader-container">
<!-- 添加激活按钮触发显示 -->
<van-button @click="show = true">选择时间</van-button>
<van-popup v-model:show="show" round position="bottom">
<van-cascader
v-model:selected-values="selectedValues"
:options="options"
:title="title"
@change="handleChange"
@close="show = false"
:field-names="fieldNames"
placeholder=""
/>
</van-popup>
<div v-if="selectedValues.length" class="selected-result">
已选择: {{ formatSelectedResult() }}
</div>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
// 先定义show变量
const show = ref(false);
// 选中的值
const selectedValues = ref([]);
// 配置级联选择的字段名
const fieldNames = {
text: 'label',
value: 'value',
children: 'children'
};
// 组件参数
const props = defineProps({
title: {
type: String,
default: '选择时间'
},
// 起始年份,默认从当前年往前5年
startYear: {
type: Number,
default: new Date().getFullYear() - 5
},
// 结束年份,默认从当前年往后5年
endYear: {
type: Number,
default: new Date().getFullYear() + 5
}
});
// 生成级联选择的选项
const options = computed(() => {
const yearOptions = [];
// 生成年份选项
for (let year = props.startYear; year <= props.endYear; year++) {
const quarterOptions = [];
// 生成季度选项
for (let quarter = 1; quarter <= 4; quarter++) {
const quarterObj = {
label: `${year}年第${quarter}季度`,
value: `${year}-Q${quarter}`
// 移除默认children属性,避免空选项
};
// 判断是否需要生成月份选项
// 规则:2025年9月及之前需要月份,之后不需要
if (year < 2025) {
// 2025年之前,所有季度都需要月份
quarterObj.children = getMonthsByQuarter(quarter);
} else if (year === 2025) {
// 2025年,只有前3季度需要月份
if (quarter <= 3) {
quarterObj.children = getMonthsByQuarter(quarter);
}
}
// 2025年之后的年份,不生成月份选项
quarterOptions.push(quarterObj);
}
yearOptions.push({
label: `${year}年`,
value: year.toString(),
children: quarterOptions
});
}
return yearOptions;
});
// 根据季度获取对应的月份
function getMonthsByQuarter(quarter) {
const months = [];
let startMonth, endMonth;
switch (quarter) {
case 1:
startMonth = 1;
endMonth = 3;
break;
case 2:
startMonth = 4;
endMonth = 6;
break;
case 3:
startMonth = 7;
endMonth = 9;
break;
case 4:
startMonth = 10;
endMonth = 12;
break;
default:
return [];
}
for (let month = startMonth; month <= endMonth; month++) {
months.push({
label: `${month}月`,
value: month.toString()
});
}
return months;
}
// 处理选择变化 - 只在选择完最后一级选项时关闭
const handleChange = ({ selectedValues, selectedOptions }) => {
console.log('选择变化:', selectedValues, selectedOptions);
// 如果没有选择值,不处理
if (!selectedOptions || selectedOptions.length === 0) return;
// 获取当前选中的年份
const selectedYear = parseInt(selectedOptions[0].value);
// 检查是否已经选择到最后一级
if (selectedOptions.length === 2) {
// 情况1: 如果是2025年之后或2025年第4季度,选择完季度就关闭
const quarter = parseInt(selectedOptions[1].value.split('-Q')[1]);
if (selectedYear > 2025 || (selectedYear === 2025 && quarter === 4)) {
show.value = false;
}
// 其他情况:选择完季度后继续选择月份,不关闭
} else if (selectedOptions.length === 3) {
// 情况2: 选择完月份后关闭
show.value = false;
}
};
// 格式化选中的结果
const formatSelectedResult = () => {
if (selectedValues.value.length === 0) return '';
let result = '';
// 年份
result += `${selectedValues.value[0]}年`;
// 季度
if (selectedValues.value.length >= 2) {
const quarter = selectedValues.value[1].split('-Q')[1];
result += `第${quarter}季度`;
}
// 月份
if (selectedValues.value.length === 3) {
result += `${selectedValues.value[2]}月`;
}
return result;
};
// 暴露选中的值
const emits = defineEmits(['update:modelValue']);
watch(selectedValues, (newVal) => {
emits('update:modelValue', newVal);
});
// 生命周期钩子:设置默认选中最后一项
onMounted(() => {
try {
if (options.value.length > 0) {
const lastYear = options.value[options.value.length - 1];
if (lastYear.children && lastYear.children.length > 0) {
const lastQuarter = lastYear.children[lastYear.children.length - 1];
const selected = [
lastYear.value,
lastQuarter.value
];
// 如果有月份选项,也选中最后一个
if (lastQuarter.children && lastQuarter.children.length > 0) {
selected.push(lastQuarter.children[lastQuarter.children.length - 1].value);
}
selectedValues.value = selected;
}
}
} catch (error) {
console.error('设置默认选中项失败:', error);
}
});
</script>
<style scoped>
.date-cascader-container {
margin: 20px;
min-height: 200px; /* 确保容器有足够高度 */
}
</style>