对于一些可能会用到的组件进行封装,为了方便以后用的时候直接使用。 quarter.vue
<template>
<el-popover ref="quarterRef" popper-class="popper-quarter" placement="bottom-start" trigger="click" :width="props.popoverWidth" :hide-after="10">
<template #reference>
<el-input
ref="quarterValueRef"
:model-value="quarter"
:style="{ width: `${props.width}px` }"
:placeholder="props.placeholder"
:clearable="props.clearable"
:size="props.size"
@input="handleChange"
@clear="handleClear"
>
<template #prefix>
<el-icon :size="props.size" class="el-input__icon"><Calendar /></el-icon>
</template>
</el-input>
</template>
<template #default>
<div class="quarterList">
<div class="quarter-header">
<div class="quarter-header-left" @click="handleLastYear">
<el-icon :size="props.size" class="yearClass"><DArrowLeft /></el-icon>
</div>
<div class="quarter-header-center" @click="handleChangeYear">
<span class="yearClass">{{ yearRangeName }}</span>
</div>
<div class="quarter-header-right" @click="handleNextYear">
<el-icon :size="props.size" class="yearClass"><DArrowRight /></el-icon>
</div>
</div>
<div class="quarter-content">
<div
@click="handleClickItem(item.value)"
:style="{ width: type === 'quarter' ? '50%' : '25%' }"
class="quarter-content-item"
v-for="item in contentList"
:key="item.value"
>
{{ item.label }}
</div>
</div>
</div>
</template>
</el-popover>
</template>
<script setup lang="ts">
const props = defineProps({
modelValue: {
type: String,
required: true,
default: '',
},
placeholder: {
type: String,
default: '请选择季度',
},
clearable: {
type: Boolean,
default: false,
},
// input组件的宽度
width: {
type: Number,
default: 240,
},
// 弹出框的宽度
popoverWidth: {
type: Number,
default: 240,
},
size: {
type: String,
default: 'default', //'large' | 'default' | 'small'
},
//formmat的值为 'YYYYQQ' || 'YYYYCQ' 默认为'YYYYQQ'
//'YYYYQQ' 季度格式 2024-Q1
//'YYYYCQ' 中文季度格式2024-第一季度
formmat: {
type: String,
default: 'YYYYQQ',
},
//value_formmat的值为 'YYYYQQ' || 'YYYY-QQ' || 'YYYY/QQ' 默认为'YYYY-QQ'
//'YYYYQQ' 实际值为 '2024Q1'
//'YYYY-QQ' 实际值为 '2024-Q1'
//'YYYY/QQ' 实际值为 '2024/Q1'
value_formmat: {
type: String,
default: 'YYYY-QQ',
},
//分隔符默认为'-' 可自定义 比如'/'
splitter: {
type: String,
default: '-',
},
});
// 'update:modelValue',
const emit = defineEmits(['change']);
const quarterRef = ref();
const quarterValueRef = ref();
const quarter = ref(props.modelValue); // 季度值
const currentYear = ref(new Date().getFullYear()); //当前年份
const yearRangeName = ref(''); // 年份范围
const type = ref('quarter'); // 类型 默认'quarter' 季度,点击年时为 'year'
interface ContentList {
label: string;
value: number | string;
}
const contentList = ref<ContentList[]>([]); // 内容列表选项
onMounted(() => {
// yearRangeName.value = currentYear.value + '年';
yearRangeName.value = currentYear.value.toString();
getType();
});
/**
* 获取类型信息,并根据类型更新内容列表和年份范围名称
*
* @returns 无返回值
*/
const getType = () => {
if (type.value === 'quarter') {
contentList.value = [
{ label: '第一季度', value: 'Q1' },
{ label: '第二季度', value: 'Q2' },
{ label: '第三季度', value: 'Q3' },
{ label: '第四季度', value: 'Q4' },
];
// yearRangeName.value = currentYear.value + '年';
yearRangeName.value = currentYear.value.toString();
} else {
contentList.value = getCurrentYearsList();
// yearRangeName.value = contentList.value[0].value + '年' + ' ~ ' + contentList.value[contentList.value.length - 1].value + '年';
yearRangeName.value = contentList.value[0].value + ' ~ ' + contentList.value[contentList.value.length - 1].value;
}
};
//点击上一年
const handleLastYear = () => {
if (type.value === 'year') {
currentYear.value -= 10;
} else {
currentYear.value -= 1;
}
getType();
};
//点击下一年
const handleNextYear = () => {
if (type.value === 'year') {
currentYear.value -= 10;
} else {
currentYear.value += 1;
}
getType();
};
//点击年时触发选择年份列表
const handleChangeYear = () => {
type.value = 'year';
getType();
};
//获取年份列表
const getCurrentYearsList = () => {
// 初始化年份列表
const yearsList = [];
// 添加前5年的年份
for (let i = 4; i > 0; i--) {
yearsList.push({
label: (currentYear.value - i).toString(),
value: currentYear.value - i,
});
}
// 添加当前年份
yearsList.push({
label: currentYear.value.toString(),
value: currentYear.value,
});
// 添加后5年的年份
for (let i = 1; i <= 5; i++) {
yearsList.push({
label: (currentYear.value + i).toString(),
value: currentYear.value + i,
});
}
return yearsList;
};
//获取季度格式化后显示的值
const getQuarterFormmat = (val: any) => {
let resultVal = '';
if (props.formmat === 'YYYYQQ') {
resultVal = currentYear.value + props.splitter + val;
}
if (props.formmat === 'YYYYCQ') {
resultVal = currentYear.value + props.splitter + contentList.value.filter((item) => item.value === val)[0].label;
}
return resultVal;
};
//点击内容列表项
const handleClickItem = (item: any) => {
if (type.value === 'quarter') {
quarter.value = getQuarterFormmat(item);
// quarter.value = currentYear.value + '-' + item;
// emit('update:modelValue', quarter.value);
nextTick(() => {
quarterRef.value?.hide();
quarterValueRef.value?.blur();
});
}
if (type.value === 'year') {
currentYear.value = item;
type.value = 'quarter';
getType();
}
};
//获取季度格式化后的值
const getQuarterValue = (val: String) => {
let parts = val.split(props.splitter);
let transVal = '';
switch (parts[1]) {
case '第一季度':
transVal = 'Q1';
break;
case '第二季度':
transVal = 'Q1';
break;
case '第三季度':
transVal = 'Q1';
break;
case '第四季度':
transVal = 'Q1';
break;
default:
transVal = parts[1];
break;
}
let resultVal = '';
if (props.value_formmat === 'YYYYQQ') {
resultVal = parts[0] + transVal;
}
if (props.value_formmat === 'YYYY-QQ') {
resultVal = parts[0] + '-' + transVal;
}
if (props.value_formmat === 'YYYY/QQ') {
resultVal = parts[0] + '/' + transVal;
}
return resultVal;
};
//输入框内的值发生改变时触发
const handleChange = (event: any) => {
// emit('update:modelValue', getQuarterValue(event));
emit('change', getQuarterValue(event));
setTimeout(() => {
quarterRef.value?.hide();
quarterValueRef.value?.blur();
}, 10);
};
// 数据监听----------------------------------------------------
watch(
() => quarter.value,
(newVal) => {
handleChange(newVal);
},
{
deep: true,
immediate: true,
}
);
//清空输入框时关闭下拉框
const handleClear = () => {
quarter.value = '';
setTimeout(() => {
// quarterRef.value?.hide(); //不需要了,因为失去焦点时会自动关闭
quarterValueRef.value?.blur();
}, 0);
//nextTick不生效的
// nextTick(() => {
// quarterValueRef.value?.blur();
// quarterRef.value?.hide();
// });
};
</script>
<style lang="scss" scoped>
// .popper-quarter {
// padding: 0 !important;
// box-sizing: border-box;
// min-width: 240px !important;
// }
.quarterList {
width: 100%;
min-width: 200px;
}
.quarter-header {
height: 42px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 12px;
box-sizing: border-box;
border-bottom: 1px solid #e4e7ed;
font-size: 16px;
font-weight: 600;
.quarter-header-left,
.quarter-header-center,
.quarter-header-right {
cursor: pointer;
.yearClass {
vertical-align: middle;
}
}
}
.quarter-content {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
align-items: center;
margin: 15px 15px 0;
box-sizing: border-box;
.quarter-content-item {
padding: 0px 0px 20px;
box-sizing: border-box;
text-align: center;
font-size: 14px;
cursor: pointer;
&:hover {
color: #409eff;
}
}
}
</style>
父组件使用 parent.vue 1.先引入
const Quarter = defineAsyncComponent(() => import('./quarter.vue'));
const quarter = ref('2024-Q4'); //季度
const getQuearter = (val) => {
console.log(quarter.value, val, '-----------------');
};
2.使用
<Quarter v-model="quarter" :width="240" size="default" @change="getQuearter" :clearable="true" />
3.加全局样式控制显示样式
.popper-quarter {
padding: 0 !important;
box-sizing: border-box;
min-width: 240px !important;
}
文档说明
| 属性名 | 说明 | 类型 | 默认 |
|---|---|---|---|
| v-model | 绑定值 | string | '' |
| placeholder | 非选择时的占位内容 | string | '' |
| clearable | 是否显示清除按钮 | boolean | false |
| width | 组件展示的宽度 | number | 240 |
| popoverWidth | 选中弹出框展示的宽度 | number | 240 |
| size | 输入框尺寸 | 'large' / 'default' / 'small' | 'default' |
| formmat | 展示内容格式化 | 'YYYYQQ' / 'YYYYCQ' | 'YYYYQQ' |
| value_formmat | 绑定值的格式化 | 'YYYYQQ' / 'YYYY-QQ' / 'YYYY/QQ' | 'YYYY-QQ' |
| splitter | 分隔符 | string(如'/'或'-') | '-' |