完善ue
This commit is contained in:
255
src/views/ueScreen/components/date.vue
Normal file
255
src/views/ueScreen/components/date.vue
Normal file
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<div class="year-month-picker">
|
||||
<!-- 年份选择器 -->
|
||||
<div class="picker-group">
|
||||
<div class="picker-input" @click="isYearOpen = !isYearOpen" :class="{ 'open': isYearOpen }">
|
||||
<span class="value">{{ selectedYear }}年</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
<ul class="options" v-show="isYearOpen">
|
||||
<li v-for="year in years" :key="year" :class="{ 'selected': year === selectedYear }"
|
||||
@click="handleYearSelect(year)">
|
||||
{{ year }}年
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 月份选择器 -->
|
||||
<div class="picker-group">
|
||||
<div class="picker-input" @click="isMonthOpen = !isMonthOpen" :class="{ 'open': isMonthOpen }">
|
||||
<span class="value">{{ selectedMonth }}月</span>
|
||||
<span class="arrow"></span>
|
||||
</div>
|
||||
<ul class="options" v-show="isMonthOpen">
|
||||
<li v-for="month in 12" :key="month" :class="{ 'selected': month === selectedMonth }"
|
||||
@click="handleMonthSelect(month)">
|
||||
{{ month }}月
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
year: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
month: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
startYear: {
|
||||
type: Number,
|
||||
default: 2000,
|
||||
},
|
||||
endYear: {
|
||||
type: Number,
|
||||
default: new Date().getFullYear(),
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:year', 'update:month', 'change']);
|
||||
|
||||
// 计算年份列表
|
||||
const years = computed(() => {
|
||||
const yearList = [];
|
||||
for (let y = props.startYear; y <= props.endYear; y++) {
|
||||
yearList.push(y);
|
||||
}
|
||||
return yearList;
|
||||
});
|
||||
|
||||
// 内部状态
|
||||
const selectedYear = ref(props.year);
|
||||
const selectedMonth = ref(props.month);
|
||||
const isYearOpen = ref(false);
|
||||
const isMonthOpen = ref(false);
|
||||
|
||||
// 监听props变化,同步到内部状态
|
||||
watch(
|
||||
() => [props.year, props.month],
|
||||
([newYear, newMonth]) => {
|
||||
selectedYear.value = newYear;
|
||||
selectedMonth.value = newMonth;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 当内部值变化时,通知父组件
|
||||
const notifyParent = () => {
|
||||
emit('update:year', selectedYear.value);
|
||||
emit('update:month', selectedMonth.value);
|
||||
emit('change', { year: selectedYear.value, month: selectedMonth.value });
|
||||
};
|
||||
|
||||
// 处理年份选择
|
||||
const handleYearSelect = (year) => {
|
||||
selectedYear.value = year;
|
||||
isYearOpen.value = false;
|
||||
notifyParent();
|
||||
};
|
||||
|
||||
// 处理月份选择
|
||||
const handleMonthSelect = (month) => {
|
||||
selectedMonth.value = month;
|
||||
isMonthOpen.value = false;
|
||||
notifyParent();
|
||||
};
|
||||
|
||||
// 点击外部关闭下拉框
|
||||
const handleClickOutside = (event) => {
|
||||
if (!event.target.closest('.year-month-picker')) {
|
||||
isYearOpen.value = false;
|
||||
isMonthOpen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 挂载时添加事件监听
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
// 验证初始值
|
||||
if (!years.value.includes(selectedYear.value)) {
|
||||
selectedYear.value = Math.max(props.startYear, Math.min(selectedYear.value, props.endYear));
|
||||
}
|
||||
selectedMonth.value = Math.max(1, Math.min(selectedMonth.value, 12));
|
||||
notifyParent(); // 确保初始值正确通知
|
||||
});
|
||||
|
||||
// 卸载时移除事件监听,防止内存泄漏
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
$vm_base: 1920;
|
||||
$vh_base: 1080;
|
||||
|
||||
// 计算vw
|
||||
@function vw($px) {
|
||||
@return calc(($px / $vm_base) * 100vw);
|
||||
}
|
||||
|
||||
// 计算vh
|
||||
@function vh($px) {
|
||||
@return calc(($px / $vh_base) * 100vh);
|
||||
}
|
||||
|
||||
.year-month-picker {
|
||||
display: inline-flex;
|
||||
border-radius: vw(8);
|
||||
font-size: vw(14);
|
||||
background-color: transparent;
|
||||
box-shadow: 0 vh(2) vh(8) rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
border: vw(1) solid #c0c4cc;
|
||||
}
|
||||
|
||||
.year-month-picker:hover {
|
||||
border-color: #909399;
|
||||
box-shadow: 0 vh(4) vh(12) rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.picker-group {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.picker-input {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
gap: vw(8);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: vh(8) vw(16);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-right: vw(1) solid #e9e9e9;
|
||||
}
|
||||
|
||||
/* 移除最后一个输入框的右边框 */
|
||||
.picker-group:last-child .picker-input {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.picker-input .value {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.picker-input.open .arrow {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: vw(6) vw(5) 0 vw(5);
|
||||
border-color: #909399 transparent transparent transparent;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
/* 美化后的下拉选项窗口 */
|
||||
.options {
|
||||
position: absolute;
|
||||
top: 110%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-height: vh(200); /* 限制最大高度并可滚动 */
|
||||
overflow-y: auto;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
border-radius: vw(4);
|
||||
box-shadow: 0 vh(4) vh(12) rgba(0, 0, 0, 0.15); /* 更明显的阴影 */
|
||||
list-style: none;
|
||||
z-index: 10;
|
||||
padding: vh(4) 0;
|
||||
margin: 0;
|
||||
border: vw(1) solid #ebeef5;
|
||||
}
|
||||
|
||||
.options li {
|
||||
padding: vh(10) vw(16);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
white-space: nowrap;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.options li:hover {
|
||||
background-color: #f5f7fa;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.options li.selected {
|
||||
// background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 美化滚动条 (WebKit浏览器) */
|
||||
.options::-webkit-scrollbar {
|
||||
width: vw(6);
|
||||
}
|
||||
|
||||
.options::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: vw(10);
|
||||
}
|
||||
|
||||
.options::-webkit-scrollbar-thumb {
|
||||
background: #c9c9c9;
|
||||
border-radius: vw(10);
|
||||
}
|
||||
|
||||
.options::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user