迁移排班日历功能
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled

This commit is contained in:
sheng
2026-06-22 23:36:08 +08:00
parent e8e79fcb3f
commit 8ef94ecf2e
6 changed files with 167 additions and 5 deletions

View File

@@ -0,0 +1,132 @@
<template>
<d2-container>
<el-calendar ref="calendar" v-model="calendarDate" first-day-of-week="7">
<template slot="dateCell" slot-scope="{ data }">
<div :class="data.isSelected ? 'is-selected' : ''" class="calendar-cell">
<div class="date-number">{{ data.day.split('-')[2] }}</div>
<div class="shift-box">
<template v-for="(plan, index) in productionShiftData">
<div v-if="plan[data.day]" :key="index" class="shift-item">
<el-tag v-if="plan[data.day].is_rest_day === true" type="info" size="small">{{ index }}-{{ $t(key('rest')) }}</el-tag>
<el-popover v-else placement="top-start" width="360" trigger="hover">
<div class="popover-title">{{ index }}</div>
<div v-for="item in plan[data.day].shifts" :key="item.name + item.start_time" class="popover-shift">
<el-tag size="small" type="warning">{{ item.name }}</el-tag>
<el-tag v-if="item.cross_day" size="small" class="cross-day">{{ $t(key('cross_day')) }}</el-tag>
<span>{{ item.start_time }}-{{ item.finish_time }} {{ item.team }}</span>
</div>
<el-tag slot="reference" type="warning" size="small">{{ index }}</el-tag>
</el-popover>
</div>
</template>
</div>
</div>
</template>
</el-calendar>
</d2-container>
</template>
<script>
import { i18nMixin } from '@/composables/useI18n'
import { getShiftCalendarByDateRange } from '@/api/production-master-data/shift-management'
function pad (value) {
return String(value).padStart(2, '0')
}
function formatDate (date) {
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
}
function addDays (date, days) {
const next = new Date(date)
next.setDate(next.getDate() + days)
return next
}
function getCalendarRange (date) {
const first = new Date(date.getFullYear(), date.getMonth(), 1)
const start = addDays(first, -first.getDay())
const last = new Date(date.getFullYear(), date.getMonth() + 1, 0)
const end = addDays(last, 6 - last.getDay())
return { start_time: formatDate(start), finish_time: formatDate(end) }
}
export default {
name: 'production-master-data-scheduling-calendar',
mixins: [i18nMixin('page.production_master_data.team_model.scheduling_calendar')],
data () {
return {
calendarDate: new Date(),
productionShiftData: [],
loading: false
}
},
watch: {
calendarDate () {
this.fetchCalendarData()
}
},
mounted () {
this.fetchCalendarData()
},
methods: {
async fetchCalendarData () {
this.loading = true
try {
const range = getCalendarRange(this.calendarDate || new Date())
const res = await getShiftCalendarByDateRange(range)
this.productionShiftData = (res && res.data) || res || []
} finally {
this.loading = false
}
}
}
}
</script>
<style scoped>
.calendar-cell {
height: 100%;
}
.date-number {
line-height: 18px;
}
.is-selected .date-number {
color: #409EFF;
font-weight: 600;
}
.shift-box {
height: 90px;
overflow-y: auto;
}
.shift-item {
margin-top: 4px;
}
.popover-title {
margin-bottom: 8px;
font-weight: 600;
}
.popover-shift {
margin-bottom: 6px;
}
.cross-day {
margin-left: 4px;
background: #D4EEA7;
color: #606266;
}
.shift-box::-webkit-scrollbar {
width: 3px;
}
.shift-box::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.shift-box::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 10px;
}
/deep/ .el-calendar-day {
height: 120px;
}
</style>