| | |
| | | <template> |
| | | <div class="calenBox"> |
| | | <el-calendar ref="calendar" v-model="leftValue"> |
| | | <el-calendar ref="calendar" v-model="currentMonth" :class="{ |
| | | 'five-rows': weeksNeeded === 5, |
| | | 'six-rows': weeksNeeded === 6 |
| | | }"> |
| | | <template #header="{ date }"> |
| | | <div> |
| | | <img :src="caleft" alt="" @click="selectDate('prev-month')" /> |
| | | |
| | | <el-date-picker |
| | | v-model="currentMonth" |
| | | type="month" |
| | | :clearable="false" |
| | | :prefix-icon="''" |
| | | @change="handleMonthChange" |
| | | :format="'YYYY年M月'" |
| | | /> |
| | | <img :src="caright" alt="" @click="selectDate('next-month')" /> |
| | | </div> |
| | | </template> |
| | | <template #date-cell="{ data }"> |
| | | <div :class="data.isSelected ? 'is-selected' : ''"> |
| | | <div class="date-number">{{ data.day.slice(8, 10) }}</div> |
| | |
| | | :class="event.type" |
| | | @click="jumpcalendar(event, data.day)" |
| | | > |
| | | <div class="imgBox"><img :src="getEventIcon(event.type)" alt="" /> {{ event.name }}</div> |
| | | <div class="imgBox"> |
| | | <img :src="getEventIcon(event.type)" alt="" /> {{ event.name }} |
| | | </div> |
| | | <span>{{ event.value }}</span> |
| | | </div> |
| | | </div> |
| | |
| | | |
| | | <script setup> |
| | | import dayjs from 'dayjs'; |
| | | import { jobEventBar, getCalen } from '@/api/home/index'; |
| | | import { ref, watch, onMounted, nextTick } from 'vue'; |
| | | import { getCalen } from '@/api/home/index'; |
| | | import { useRouter } from 'vue-router'; |
| | | import ev1 from '@/assets/images/workbench/ev1.svg'; |
| | | import ev2 from '@/assets/images/workbench/ev2.svg'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import caleft from '@/assets/images/workbench/caleft.png'; |
| | | import caright from '@/assets/images/workbench/caright.png'; |
| | | |
| | | const router = useRouter(); |
| | | const calendar = ref(); |
| | | const events = ref({}); |
| | | const currentMonth = ref(new Date()); // 控制当前显示的月份 |
| | | const selectedDate = ref(null); // 控制选中的日期 |
| | | const params = ref({ |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | |
| | | const eventIcons = ref({ |
| | | 'work-order': ev1, |
| | | task: ev2, |
| | | }); |
| | | |
| | | const monthRange = getCurrentMonthRange(); |
| | | params.value = monthRange; |
| | | // 检查日期是否是当前月份 |
| | | const isCurrentMonth = date => { |
| | | return dayjs(date).isSame(dayjs(), 'month'); |
| | | }; |
| | | // 处理月份选择器变化 |
| | | const handleMonthChange = val => { |
| | | const selectedMonth = new Date(val); |
| | | currentMonth.value = selectedMonth; |
| | | |
| | | // 如果是当前月份,则选中当天;否则不选中 |
| | | if (isCurrentMonth(selectedMonth)) { |
| | | selectedDate.value = new Date(); |
| | | } else { |
| | | selectedDate.value = null; |
| | | } |
| | | |
| | | // 强制更新日历视图 |
| | | nextTick(() => { |
| | | updateCalendarSelection(); |
| | | }); |
| | | }; |
| | | |
| | | // 获取日历数据 |
| | | const getJobEventBar = () => { |
| | | getCalen(params.value).then(res => { |
| | | if (res.data.code !== 0) return; |
| | | const a = res.data.data; |
| | | const filteredData = {}; |
| | | for (let date in a) { |
| | | filteredData[date] = a[date].filter(item => item.type !== 'work-order'); |
| | | } |
| | | events.value = filteredData; |
| | | }); |
| | | }; |
| | | |
| | | // 获取事件图标 |
| | | const getEventIcon = type => { |
| | | return eventIcons.value[type] || eventIcons.value.default; |
| | | }; |
| | | |
| | | // 获取当前月份范围 |
| | | function getCurrentMonthRange() { |
| | | return { |
| | | start_date: dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | end_date: dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | }; |
| | | } |
| | | const leftValue = ref(new Date()); |
| | | |
| | | // 月份切换处理 |
| | | const selectDate = async val => { |
| | | if (!calendar.value) return; |
| | | |
| | | if (val === 'prev-month') { |
| | | currentMonth.value = dayjs(currentMonth.value).subtract(1, 'month').toDate(); |
| | | } else if (val === 'next-month') { |
| | | currentMonth.value = dayjs(currentMonth.value).add(1, 'month').toDate(); |
| | | } |
| | | |
| | | // 如果是当前月份,则选中当天;否则不选中 |
| | | if (isCurrentMonth(currentMonth.value)) { |
| | | selectedDate.value = new Date(); |
| | | } else { |
| | | selectedDate.value = null; |
| | | } |
| | | |
| | | await nextTick(); |
| | | updateCalendarSelection(); |
| | | }; |
| | | const updateCalendarSelection = () => { |
| | | // 清除所有选中状态 |
| | | const selectedCells = document.querySelectorAll('.el-calendar-table td.is-selected'); |
| | | selectedCells.forEach(cell => { |
| | | cell.classList.remove('is-selected'); |
| | | }); |
| | | |
| | | // 如果是当前月份,设置当前日为选中状态 |
| | | if (isCurrentMonth(currentMonth.value) && selectedDate.value) { |
| | | const currentDay = dayjs(selectedDate.value).date(); |
| | | const calendarCells = document.querySelectorAll('.el-calendar-table td'); |
| | | |
| | | calendarCells.forEach(cell => { |
| | | const day = parseInt(cell.querySelector('.date-number').textContent); |
| | | if (day === currentDay) { |
| | | cell.classList.add('is-selected'); |
| | | } |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 监听当前月份变化 |
| | | watch( |
| | | () => leftValue.value, |
| | | (newV, oldV) => { |
| | | if (newV && oldV) { |
| | | const newDate = dayjs(newV); |
| | | params.value = { |
| | | start_date: newDate.startOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | end_date: newDate.endOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | }; |
| | | getJobEventBar(); |
| | | () => currentMonth.value, |
| | | newMonth => { |
| | | const newDate = dayjs(newMonth); |
| | | params.value = { |
| | | start_date: newDate.startOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | end_date: newDate.endOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | }; |
| | | // 如果是当前月份,则选中当天;否则清除选中 |
| | | if (isCurrentMonth(newMonth)) { |
| | | selectedDate.value = new Date(); |
| | | } else { |
| | | selectedDate.value = null; |
| | | } |
| | | nextTick(() => { |
| | | updateCalendarSelection(); |
| | | }); |
| | | |
| | | getJobEventBar(); |
| | | }, |
| | | { deep: true, immediate: true } |
| | | ); |
| | |
| | | // 获取对应日期的事件 |
| | | const getEvents = dateString => { |
| | | return events.value[dateString] || []; |
| | | }; |
| | | const monthRange = getCurrentMonthRange(); |
| | | params.value = monthRange; |
| | | |
| | | const getJobEventBar = () => { |
| | | getCalen(params.value).then(res => { |
| | | if (res.data.code !== 0) return; |
| | | const a = res.data.data; |
| | | const filteredData = {}; |
| | | for (let date in a) { |
| | | filteredData[date] = a[date].filter(item => item.type !== 'work-order'); |
| | | } |
| | | events.value = filteredData; |
| | | // events.value = res.data.data |
| | | }); |
| | | }; |
| | | const jumpcalendar = (event, day) => { |
| | | if (event.name === '工单') { |
| | |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const weeksNeeded = computed(() => { |
| | | const date = dayjs(currentMonth.value); |
| | | const firstDay = date.startOf('month').day(); // 0-6 (周日到周六) |
| | | const days = date.daysInMonth(); |
| | | |
| | | // 强制 2025 年 6 月显示六行 |
| | | if (date.year() === 2025 && date.month() === 5) { |
| | | return 6; |
| | | } |
| | | |
| | | // 其他月份按原逻辑计算 |
| | | return Math.ceil((firstDay + days) / 7); |
| | | }) |
| | | const isSixRows = computed(() => weeksNeeded.value > 5); |
| | | onMounted(() => { |
| | | // 初始化时如果是当前月份,选中当天;否则不选中 |
| | | if (isCurrentMonth(currentMonth.value)) { |
| | | selectedDate.value = new Date(); |
| | | } else { |
| | | selectedDate.value = null; |
| | | } |
| | | |
| | | nextTick(() => { |
| | | updateCalendarSelection(); |
| | | }); |
| | | |
| | | getJobEventBar(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .calenBox { |
| | | // height: 630px; |
| | | height: pxToVh(660); |
| | | height: pxToVh(670); |
| | | // 隐藏按钮组中间按钮 |
| | | .el-button-group > .el-button:not(:first-child):not(:last-child) { |
| | | display: none; |
| | | } |
| | | .el-calendar__body { |
| | | padding-top: 0 !important; |
| | | } |
| | | .date-number { |
| | | font-size: 13px !important;} |
| | | font-size: 13px !important; |
| | | } |
| | | .el-calendar__header { |
| | | justify-content: center; |
| | | margin-top: 10px; |
| | | padding-bottom: 0; |
| | | border-bottom: transparent; |
| | | div { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | img { |
| | | width: 28px; |
| | | height: 28px; |
| | | cursor: pointer; |
| | | } |
| | | span { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | margin: 0 18px; |
| | | } |
| | | } |
| | | } |
| | | .el-calendar-table td { |
| | | border: none; |
| | | } |
| | | .el-calendar-table tr td { |
| | | border: none; |
| | | } |
| | | .el-calendar-table .el-calendar-day { |
| | | box-sizing: border-box; |
| | | padding: 0.8rem; |
| | | background: #f5f7fa; |
| | | border-radius: 6px 6px 6px 6px; |
| | | border: 1px solid #efefef; |
| | | margin-right: 5px; |
| | | margin-bottom: 8px; |
| | | } |
| | | .el-input__prefix-inner { |
| | | display: none !important; |
| | | } |
| | | .el-input__suffix { |
| | | width: 0 !important; |
| | | } |
| | | .el-input__wrapper { |
| | | align-items: center !important; |
| | | cursor: text; |
| | | box-shadow: none !important; |
| | | display: inline-flex !important; |
| | | flex-grow: 1; |
| | | justify-content: center; |
| | | } |
| | | .el-input__inner { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | .el-date-editor.el-input, |
| | | .el-date-editor.el-input__wrapper { |
| | | height: var(--el-input-height, var(--el-component-size)); |
| | | |
| | | width: 170px !important; |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | <style lang="scss" scoped> |
| | | .calenBox { |
| | | margin-top: 10px; |
| | | // height: 630px; |
| | | height: pxToVh(622); |
| | | height: pxToVh(632); |
| | | border-radius: 10px; |
| | | overflow: hidden; |
| | | background-color: #fff; |
| | | |
| | | :deep(.el-date-picker .el-input__prefix-inner) { |
| | | display: none !important; |
| | | } |
| | | ::v-deep(.el-calendar) { |
| | | // height: 80%; // 日历填充容器 |
| | | // 标题样式 |
| | | .el-calendar__title { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | |
| | | // 日历主体 |
| | | // &__body { |
| | | // height: 98%; // 关键:继承父高度 |
| | | |
| | | // .el-calendar-table { |
| | | // height: 90% !important; // 百分比生效 |
| | | // } |
| | | // .el-calendar-table .el-calendar-day { |
| | | // height: pxToVh(88) !important; |
| | | // } |
| | | .el-calendar-table .el-calendar-day { |
| | | |
| | | height: pxToVh(91) !important; |
| | | } |
| | | |
| | | &.five-rows .el-calendar-table .el-calendar-day { |
| | | height: pxToVh(88) !important; |
| | | } |
| | | |
| | | &.six-rows .el-calendar-table .el-calendar-day { |
| | | height: pxToVh(78) !important; |
| | | } |
| | | // 选中日期样式 |
| | | .el-calendar-table td.is-selected { |
| | | background-color: #f0f7ff; |
| | | border: 2px solid #409eff; |
| | | border-radius: 4px; |
| | | |
| | | .date-number { |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | .el-calendar-day { |
| | | outline: 2px solid #409eff; |
| | | outline-offset: -2px; |
| | | border: none !important; |
| | | .date-number { |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | .el-calendar-table td.is-selected { |
| | | background-color: transparent; |
| | | } |
| | | |
| | | } |
| | | |
| | | .event-item { |
| | | font-size: 12px; |
| | | // padding: 2px; |
| | | text-align: center; |
| | | border-radius: 3px; |
| | | white-space: nowrap; |
| | | position: relative; |
| | | z-index: 2; |
| | | |
| | | &.work-order { |
| | | font-weight: 400; |
| | |
| | | |
| | | span { |
| | | font-weight: 600; |
| | | font-size: 18px; |
| | | font-size: 17px; |
| | | color: #029d36; |
| | | margin-left: 2px; |
| | | } |
| | | } |
| | | .imgBox { |
| | | font-size: 12px;} |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | } |
| | | </style> |