refactor(mes): 重构排班日历视图,提取公共日历格子组件
- 新增 CalendarDateCell.vue:抽取三个视图中重复的农历/节气/节假日/班次渲染逻辑,统一维护 - CalendarTypeView.vue → TypeView.vue,PersonView.vue → UserView.vue:按视图含义重命名,去掉多余前缀 - UserView.vue:用户选择从 el-input-number 改为 el-select 下拉,调用 getSimpleUserList 获取用户列表 - TeamView.vue / TypeView.vue / UserView.vue:统一使用 CalendarDateCell 组件;补充变量行尾注释、函数 JSDoc 及关键逻辑行内注释 - index.vue:更新组件引用pull/871/MERGE
parent
64b3ce64c3
commit
93a76b19db
|
|
@ -0,0 +1,105 @@
|
|||
<!-- 排班日历 - 日历格子(公共组件) -->
|
||||
<template>
|
||||
<div class="h-full p-4px">
|
||||
<!-- 顶部:日期数字 + 上班/休息标签 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-16px font-500" :class="{ 'text-#f56c6c': isWeekend }">
|
||||
{{ dayNumber }}
|
||||
</span>
|
||||
<el-tag v-if="isHoliday" size="small" effect="dark" type="success">休</el-tag>
|
||||
<el-tag v-else size="small" effect="dark"> 班 </el-tag>
|
||||
</div>
|
||||
<!-- 农历 / 节气 / 节日显示 -->
|
||||
<div
|
||||
class="text-12px text-#909399 mt-2px"
|
||||
:class="{ 'text-#67c23a': hasFestivalDay }"
|
||||
>
|
||||
{{ lunarDisplay }}
|
||||
</div>
|
||||
<!-- 班次信息:节假日不显示排班 -->
|
||||
<template v-if="!isHoliday">
|
||||
<!-- TODO @AI:最好有个地方,说明下这个逻辑; -->
|
||||
<div v-for="item in teamShifts" :key="item.sort" class="mt-2px">
|
||||
<!-- sort=1 白班:绿色 -->
|
||||
<el-button v-if="item.sort === 1" type="success" size="small" class="!w-full !text-12px">
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
<!-- sort=2 中班:三班倒时用橙色,两班倒时用灰色 -->
|
||||
<el-button
|
||||
v-else-if="item.sort === 2"
|
||||
:type="shiftType === MesCalShiftTypeEnum.THREE ? 'warning' : 'info'"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
<!-- sort=3 夜班:灰色 -->
|
||||
<el-button v-else-if="item.sort === 3" type="info" size="small" class="!w-full !text-12px">
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SolarDay } from 'tyme4ts'
|
||||
import { MesCalShiftTypeEnum } from '@/views/mes/utils/constants'
|
||||
import type { CalCalendarDayVO } from '@/api/mes/cal/calendar'
|
||||
|
||||
const props = defineProps<{
|
||||
day: string // 日期,格式 yyyy-MM-dd
|
||||
holidaySet: Set<string> // 节假日集合,key: yyyy-MM-dd
|
||||
calendarDayMap: Map<string, CalCalendarDayVO> // 排班数据,key: yyyy-MM-dd
|
||||
}>()
|
||||
|
||||
const dayNumber = computed(() => props.day.split('-')[2]) // 取日期中的"日"部分展示
|
||||
const isHoliday = computed(() => props.holidaySet.has(props.day)) // 是否节假日
|
||||
const isWeekend = computed(() => {
|
||||
const date = new Date(props.day)
|
||||
const weekDay = date.getDay()
|
||||
return weekDay === 0 || weekDay === 6 // 0=周日,6=周六
|
||||
})
|
||||
|
||||
const calDay = computed(() => props.calendarDayMap.get(props.day)) // 当天排班数据
|
||||
const teamShifts = computed(() => calDay.value?.teamShifts || []) // 当天班组排班列表
|
||||
const shiftType = computed(() => calDay.value?.shiftType) // 当天轮班方式(用于区分配色)
|
||||
|
||||
/** 解析当天的农历、节气、节日信息 */
|
||||
const lunarInfo = computed(() => {
|
||||
const parts = props.day.split('-')
|
||||
const year = parseInt(parts[0])
|
||||
const month = parseInt(parts[1])
|
||||
const date = parseInt(parts[2])
|
||||
try {
|
||||
const solarDay = SolarDay.fromYmd(year, month, date)
|
||||
const lunarDay = solarDay.getLunarDay()
|
||||
const solarFestival = solarDay.getFestival() // 公历节日,如 元旦
|
||||
const lunarFestival = lunarDay.getFestival() // 农历节日,如 春节
|
||||
const termDay = solarDay.getTermDay()
|
||||
const termName = termDay.getDayIndex() === 0 ? termDay.getSolarTerm().getName() : null // 节气,仅节气当天取值
|
||||
const lunarMonthName = lunarDay.getLunarMonth().getName()
|
||||
const lunarDayName = lunarDay.getName()
|
||||
return {
|
||||
solarFestival: solarFestival ? solarFestival.getName() : null,
|
||||
lunarFestival: lunarFestival ? lunarFestival.getName() : null,
|
||||
termName,
|
||||
lunarText: lunarMonthName + lunarDayName // 兜底:显示农历月日,如 正月初一
|
||||
}
|
||||
} catch {
|
||||
return { solarFestival: null, lunarFestival: null, termName: null, lunarText: '' }
|
||||
}
|
||||
})
|
||||
|
||||
/** 优先级:公历节日 > 农历节日 > 节气 > 农历月日 */
|
||||
const lunarDisplay = computed(() => {
|
||||
const info = lunarInfo.value
|
||||
return info.solarFestival || info.lunarFestival || info.termName || info.lunarText
|
||||
})
|
||||
|
||||
/** 当天是否有节日或节气(用于高亮显示农历文字) */
|
||||
const hasFestivalDay = computed(() => {
|
||||
const info = lunarInfo.value
|
||||
return !!(info.solarFestival || info.lunarFestival || info.termName)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
<!-- 排班日历 - 按分类视图 -->
|
||||
<!-- todo @ai:是不是去掉开头的 calendar,因为大家都是呀 -->
|
||||
<template>
|
||||
<div class="flex">
|
||||
<!-- 左侧:班组类型选择 -->
|
||||
<div class="w-150px shrink-0 mr-12px border border-solid border-#dcdfe6 rounded-4px overflow-hidden">
|
||||
<div
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.MES_CAL_CALENDAR_TYPE)"
|
||||
:key="dict.value"
|
||||
class="px-16px py-10px cursor-pointer text-14px text-#606266 border-b border-b-solid border-b-#ebeef5 last:border-b-0 hover:bg-#f5f7fa transition-colors"
|
||||
:class="selectedType === dict.value ? 'bg-#ecf5ff text-#409eff font-500' : ''"
|
||||
@click="selectedType = dict.value; onTypeSelected()"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:日历 -->
|
||||
<div class="flex-1">
|
||||
<el-calendar v-model="currentDate" v-loading="loading">
|
||||
<template #date-cell="{ data }">
|
||||
<div class="h-full p-4px">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-16px font-500" :class="{ 'text-#f56c6c': isWeekend(data.day) }">
|
||||
{{ data.day.split('-')[2] }}
|
||||
</span>
|
||||
<el-tag v-if="holidaySet.has(data.day)" size="small" effect="dark" type="success">
|
||||
休
|
||||
</el-tag>
|
||||
<el-tag v-else size="small" effect="dark"> 班 </el-tag>
|
||||
</div>
|
||||
<div
|
||||
class="text-12px text-#909399 mt-2px"
|
||||
:class="{ 'text-#67c23a': hasFestival(data.day) }"
|
||||
>
|
||||
{{ getLunarDisplay(data.day) }}
|
||||
</div>
|
||||
<!-- 班次信息 -->
|
||||
<template v-if="!holidaySet.has(data.day)">
|
||||
<div
|
||||
v-for="item in getTeamShiftsForDay(data.day)"
|
||||
:key="item.sort"
|
||||
class="mt-2px"
|
||||
>
|
||||
<el-button
|
||||
v-if="item.sort === 1"
|
||||
type="success"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="item.sort === 2"
|
||||
:type="getShiftTypeForDay(data.day) === MesCalShiftTypeEnum.THREE ? 'warning' : 'info'"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="item.sort === 3"
|
||||
type="info"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-calendar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CalCalendarApi, CalCalendarDayVO } from '@/api/mes/cal/calendar'
|
||||
import { CalHolidayApi, CalHolidayVO } from '@/api/mes/cal/holiday'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { SolarDay } from 'tyme4ts'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { HolidayType, MesCalShiftTypeEnum } from '@/views/mes/utils/constants'
|
||||
|
||||
const loading = ref(false)
|
||||
const currentDate = ref(new Date())
|
||||
const selectedType = ref<number>()
|
||||
const calendarDayMap = ref<Map<string, CalCalendarDayVO>>(new Map())
|
||||
const holidaySet = ref(new Set<string>())
|
||||
|
||||
/** 获取假期列表 */
|
||||
const getHolidayList = async () => {
|
||||
holidaySet.value.clear()
|
||||
const list = await CalHolidayApi.getHolidayList()
|
||||
if (list) {
|
||||
list.forEach((item: CalHolidayVO) => {
|
||||
const day = item.day ? formatDate(item.day as any, 'YYYY-MM-DD') : ''
|
||||
if (day && item.type === HolidayType.HOLIDAY) {
|
||||
holidaySet.value.add(day)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询排班日历 */
|
||||
const fetchCalendar = async () => {
|
||||
if (!selectedType.value) {
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
// 计算当前月份的起止日期
|
||||
const date = currentDate.value
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth()
|
||||
const startDay = new Date(year, month, 1)
|
||||
const endDay = new Date(year, month + 1, 0, 23, 59, 59)
|
||||
const list = await CalCalendarApi.getCalendarList({
|
||||
queryType: 'TYPE',
|
||||
calendarType: selectedType.value,
|
||||
startDay: formatDate(startDay, 'YYYY-MM-DD HH:mm:ss'),
|
||||
endDay: formatDate(endDay, 'YYYY-MM-DD HH:mm:ss')
|
||||
})
|
||||
calendarDayMap.value.clear()
|
||||
if (list) {
|
||||
list.forEach((item: CalCalendarDayVO) => {
|
||||
calendarDayMap.value.set(item.day, item)
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择分类 */
|
||||
const onTypeSelected = () => {
|
||||
fetchCalendar()
|
||||
}
|
||||
|
||||
/** 获取指定日期的班次列表 */
|
||||
const getTeamShiftsForDay = (day: string) => {
|
||||
const calDay = calendarDayMap.value.get(day)
|
||||
return calDay?.teamShifts || []
|
||||
}
|
||||
|
||||
/** 获取指定日期的轮班方式 */
|
||||
const getShiftTypeForDay = (day: string) => {
|
||||
const calDay = calendarDayMap.value.get(day)
|
||||
return calDay?.shiftType
|
||||
}
|
||||
|
||||
/** 判断是否周末 */
|
||||
const isWeekend = (day: string): boolean => {
|
||||
const date = new Date(day)
|
||||
const weekDay = date.getDay()
|
||||
return weekDay === 0 || weekDay === 6
|
||||
}
|
||||
|
||||
/** 获取农历显示信息 */
|
||||
const getLunarInfo = (day: string) => {
|
||||
const parts = day.split('-')
|
||||
const year = parseInt(parts[0])
|
||||
const month = parseInt(parts[1])
|
||||
const date = parseInt(parts[2])
|
||||
try {
|
||||
const solarDay = SolarDay.fromYmd(year, month, date)
|
||||
const lunarDay = solarDay.getLunarDay()
|
||||
const solarFestival = solarDay.getFestival()
|
||||
const lunarFestival = lunarDay.getFestival()
|
||||
const termDay = solarDay.getTermDay()
|
||||
const termName = termDay.getDayIndex() === 0 ? termDay.getSolarTerm().getName() : null
|
||||
const lunarMonthName = lunarDay.getLunarMonth().getName()
|
||||
const lunarDayName = lunarDay.getName()
|
||||
return {
|
||||
solarFestival: solarFestival ? solarFestival.getName() : null,
|
||||
lunarFestival: lunarFestival ? lunarFestival.getName() : null,
|
||||
termName,
|
||||
lunarText: lunarMonthName + lunarDayName
|
||||
}
|
||||
} catch {
|
||||
return { solarFestival: null, lunarFestival: null, termName: null, lunarText: '' }
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取农历显示文本 */
|
||||
const getLunarDisplay = (day: string): string => {
|
||||
const info = getLunarInfo(day)
|
||||
return info.solarFestival || info.lunarFestival || info.termName || info.lunarText
|
||||
}
|
||||
|
||||
/** 判断是否有节日 */
|
||||
const hasFestival = (day: string): boolean => {
|
||||
const info = getLunarInfo(day)
|
||||
return !!(info.solarFestival || info.lunarFestival || info.termName)
|
||||
}
|
||||
|
||||
/** 监听月份变化 */
|
||||
watch(currentDate, () => {
|
||||
if (selectedType.value) {
|
||||
fetchCalendar()
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getHolidayList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
<!-- 排班日历 - 按个人视图 -->
|
||||
<!-- TODO @AI: user > person 文件名 -->
|
||||
<template>
|
||||
<div>
|
||||
<!-- 顶部:用户选择 -->
|
||||
<el-form :inline="true" label-width="80px" class="mb-10px">
|
||||
<el-form-item label="用户编号">
|
||||
<!-- TODO @AI:下拉选择; -->
|
||||
<el-input-number
|
||||
v-model="userId"
|
||||
placeholder="请输入用户编号"
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
class="!w-200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onUserQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 查询
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 日历 -->
|
||||
|
||||
<el-calendar v-model="currentDate" v-loading="loading">
|
||||
<template #date-cell="{ data }">
|
||||
<div class="h-full p-4px">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-16px font-500" :class="{ 'text-#f56c6c': isWeekend(data.day) }">
|
||||
{{ data.day.split('-')[2] }}
|
||||
</span>
|
||||
<el-tag v-if="holidaySet.has(data.day)" size="small" effect="dark" type="success">
|
||||
休
|
||||
</el-tag>
|
||||
<el-tag v-else size="small" effect="dark"> 班 </el-tag>
|
||||
</div>
|
||||
<div
|
||||
class="text-12px text-#909399 mt-2px"
|
||||
:class="{ 'text-#67c23a': hasFestival(data.day) }"
|
||||
>
|
||||
{{ getLunarDisplay(data.day) }}
|
||||
</div>
|
||||
<!-- 班次信息 -->
|
||||
<template v-if="!holidaySet.has(data.day)">
|
||||
<div
|
||||
v-for="item in getTeamShiftsForDay(data.day)"
|
||||
:key="item.sort"
|
||||
class="mt-2px"
|
||||
>
|
||||
<el-button
|
||||
v-if="item.sort === 1"
|
||||
type="success"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="item.sort === 2"
|
||||
:type="getShiftTypeForDay(data.day) === MesCalShiftTypeEnum.THREE ? 'warning' : 'info'"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="item.sort === 3"
|
||||
type="info"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-calendar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CalCalendarApi, CalCalendarDayVO } from '@/api/mes/cal/calendar'
|
||||
import { CalHolidayApi, CalHolidayVO } from '@/api/mes/cal/holiday'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { SolarDay } from 'tyme4ts'
|
||||
import { HolidayType, MesCalShiftTypeEnum } from '@/views/mes/utils/constants'
|
||||
|
||||
const loading = ref(false)
|
||||
const currentDate = ref(new Date())
|
||||
const userId = ref<number>()
|
||||
const calendarDayMap = ref<Map<string, CalCalendarDayVO>>(new Map())
|
||||
const holidaySet = ref(new Set<string>())
|
||||
|
||||
/** 获取假期列表 */
|
||||
const getHolidayList = async () => {
|
||||
holidaySet.value.clear()
|
||||
const list = await CalHolidayApi.getHolidayList()
|
||||
if (list) {
|
||||
list.forEach((item: CalHolidayVO) => {
|
||||
const day = item.day ? formatDate(item.day as any, 'YYYY-MM-DD') : ''
|
||||
if (day && item.type === HolidayType.HOLIDAY) {
|
||||
holidaySet.value.add(day)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询排班日历 */
|
||||
const fetchCalendar = async () => {
|
||||
if (!userId.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
const date = currentDate.value
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth()
|
||||
const startDay = new Date(year, month, 1)
|
||||
const endDay = new Date(year, month + 1, 0, 23, 59, 59)
|
||||
const list = await CalCalendarApi.getCalendarList({
|
||||
queryType: 'USER',
|
||||
userId: userId.value,
|
||||
startDay: formatDate(startDay, 'YYYY-MM-DD HH:mm:ss'),
|
||||
endDay: formatDate(endDay, 'YYYY-MM-DD HH:mm:ss')
|
||||
})
|
||||
calendarDayMap.value.clear()
|
||||
if (list) {
|
||||
list.forEach((item: CalCalendarDayVO) => {
|
||||
calendarDayMap.value.set(item.day, item)
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询按钮 */
|
||||
const onUserQuery = () => {
|
||||
fetchCalendar()
|
||||
}
|
||||
|
||||
/** 获取指定日期的班次列表 */
|
||||
const getTeamShiftsForDay = (day: string) => {
|
||||
const calDay = calendarDayMap.value.get(day)
|
||||
return calDay?.teamShifts || []
|
||||
}
|
||||
|
||||
/** 获取指定日期的轮班方式 */
|
||||
const getShiftTypeForDay = (day: string) => {
|
||||
const calDay = calendarDayMap.value.get(day)
|
||||
return calDay?.shiftType
|
||||
}
|
||||
|
||||
/** 判断是否周末 */
|
||||
const isWeekend = (day: string): boolean => {
|
||||
const date = new Date(day)
|
||||
const weekDay = date.getDay()
|
||||
return weekDay === 0 || weekDay === 6
|
||||
}
|
||||
|
||||
/** 获取农历显示信息 */
|
||||
const getLunarInfo = (day: string) => {
|
||||
const parts = day.split('-')
|
||||
const year = parseInt(parts[0])
|
||||
const month = parseInt(parts[1])
|
||||
const date = parseInt(parts[2])
|
||||
try {
|
||||
const solarDay = SolarDay.fromYmd(year, month, date)
|
||||
const lunarDay = solarDay.getLunarDay()
|
||||
const solarFestival = solarDay.getFestival()
|
||||
const lunarFestival = lunarDay.getFestival()
|
||||
const termDay = solarDay.getTermDay()
|
||||
const termName = termDay.getDayIndex() === 0 ? termDay.getSolarTerm().getName() : null
|
||||
const lunarMonthName = lunarDay.getLunarMonth().getName()
|
||||
const lunarDayName = lunarDay.getName()
|
||||
return {
|
||||
solarFestival: solarFestival ? solarFestival.getName() : null,
|
||||
lunarFestival: lunarFestival ? lunarFestival.getName() : null,
|
||||
termName,
|
||||
lunarText: lunarMonthName + lunarDayName
|
||||
}
|
||||
} catch {
|
||||
return { solarFestival: null, lunarFestival: null, termName: null, lunarText: '' }
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取农历显示文本 */
|
||||
const getLunarDisplay = (day: string): string => {
|
||||
const info = getLunarInfo(day)
|
||||
return info.solarFestival || info.lunarFestival || info.termName || info.lunarText
|
||||
}
|
||||
|
||||
/** 判断是否有节日 */
|
||||
const hasFestival = (day: string): boolean => {
|
||||
const info = getLunarInfo(day)
|
||||
return !!(info.solarFestival || info.lunarFestival || info.termName)
|
||||
}
|
||||
|
||||
// 监听月份变化
|
||||
watch(currentDate, () => {
|
||||
if (userId.value) {
|
||||
fetchCalendar()
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getHolidayList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -2,7 +2,11 @@
|
|||
<template>
|
||||
<div class="flex">
|
||||
<!-- 左侧:班组列表选择 -->
|
||||
<div class="w-150px shrink-0 mr-12px border border-solid border-#dcdfe6 rounded-4px overflow-hidden">
|
||||
<!-- TODO @AI:默认选中首个 -->
|
||||
<div
|
||||
class="w-150px shrink-0 mr-12px border border-solid border-#dcdfe6 rounded-4px overflow-hidden"
|
||||
>
|
||||
<!-- TODO @AI:可以把 @click 在封装下么?更统一一些; -->
|
||||
<div
|
||||
v-for="team in teamList"
|
||||
:key="team.id"
|
||||
|
|
@ -18,56 +22,11 @@
|
|||
<div class="flex-1">
|
||||
<el-calendar v-model="currentDate" v-loading="loading">
|
||||
<template #date-cell="{ data }">
|
||||
<div class="h-full p-4px">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-16px font-500" :class="{ 'text-#f56c6c': isWeekend(data.day) }">
|
||||
{{ data.day.split('-')[2] }}
|
||||
</span>
|
||||
<el-tag v-if="holidaySet.has(data.day)" size="small" effect="dark" type="success">
|
||||
休
|
||||
</el-tag>
|
||||
<el-tag v-else size="small" effect="dark"> 班 </el-tag>
|
||||
</div>
|
||||
<div
|
||||
class="text-12px text-#909399 mt-2px"
|
||||
:class="{ 'text-#67c23a': hasFestival(data.day) }"
|
||||
>
|
||||
{{ getLunarDisplay(data.day) }}
|
||||
</div>
|
||||
<!-- 班次信息 -->
|
||||
<template v-if="!holidaySet.has(data.day)">
|
||||
<div
|
||||
v-for="item in getTeamShiftsForDay(data.day)"
|
||||
:key="item.sort"
|
||||
class="mt-2px"
|
||||
>
|
||||
<el-button
|
||||
v-if="item.sort === 1"
|
||||
type="success"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="item.sort === 2"
|
||||
:type="getShiftTypeForDay(data.day) === MesCalShiftTypeEnum.THREE ? 'warning' : 'info'"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="item.sort === 3"
|
||||
type="info"
|
||||
size="small"
|
||||
class="!w-full !text-12px"
|
||||
>
|
||||
{{ item.teamName }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<CalendarDateCell
|
||||
:day="data.day"
|
||||
:holiday-set="holidaySet"
|
||||
:calendar-day-map="calendarDayMap"
|
||||
/>
|
||||
</template>
|
||||
</el-calendar>
|
||||
</div>
|
||||
|
|
@ -79,40 +38,42 @@ import { CalCalendarApi, CalCalendarDayVO } from '@/api/mes/cal/calendar'
|
|||
import { CalTeamApi, CalTeamVO } from '@/api/mes/cal/team'
|
||||
import { CalHolidayApi, CalHolidayVO } from '@/api/mes/cal/holiday'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { SolarDay } from 'tyme4ts'
|
||||
import { HolidayType, MesCalShiftTypeEnum } from '@/views/mes/utils/constants'
|
||||
import { HolidayType } from '@/views/mes/utils/constants'
|
||||
import CalendarDateCell from './CalendarDateCell.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const currentDate = ref(new Date())
|
||||
const selectedTeamId = ref<number>()
|
||||
const teamList = ref<CalTeamVO[]>([])
|
||||
const calendarDayMap = ref<Map<string, CalCalendarDayVO>>(new Map())
|
||||
const holidaySet = ref(new Set<string>())
|
||||
const currentDate = ref(new Date()) // 日历当前显示月份
|
||||
const selectedTeamId = ref<number>() // 当前选中的班组编号
|
||||
const teamList = ref<CalTeamVO[]>([]) // 所有班组列表
|
||||
const calendarDayMap = ref<Map<string, CalCalendarDayVO>>(new Map()) // key: yyyy-MM-dd
|
||||
const holidaySet = ref(new Set<string>()) // 节假日日期集合,key: yyyy-MM-dd
|
||||
|
||||
/** 获取班组列表 */
|
||||
const getTeamList = async () => {
|
||||
teamList.value = await CalTeamApi.getTeamList()
|
||||
}
|
||||
|
||||
/** 获取假期列表 */
|
||||
/** 获取节假日列表,构建节假日日期集合 */
|
||||
const getHolidayList = async () => {
|
||||
holidaySet.value.clear()
|
||||
const list = await CalHolidayApi.getHolidayList()
|
||||
if (list) {
|
||||
list.forEach((item: CalHolidayVO) => {
|
||||
const day = item.day ? formatDate(item.day as any, 'YYYY-MM-DD') : ''
|
||||
if (day && item.type === HolidayType.HOLIDAY) {
|
||||
holidaySet.value.add(day)
|
||||
}
|
||||
})
|
||||
if (!list) {
|
||||
return
|
||||
}
|
||||
list.forEach((item: CalHolidayVO) => {
|
||||
const day = item.day ? formatDate(item.day as any, 'YYYY-MM-DD') : ''
|
||||
if (day && item.type === HolidayType.HOLIDAY) {
|
||||
holidaySet.value.add(day)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询排班日历 */
|
||||
/** 查询当前月份的排班日历,按选中班组过滤 */
|
||||
const fetchCalendar = async () => {
|
||||
if (!selectedTeamId.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
// 计算当前月份的起止时间
|
||||
const date = currentDate.value
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth()
|
||||
|
|
@ -124,80 +85,25 @@ const fetchCalendar = async () => {
|
|||
startDay: formatDate(startDay, 'YYYY-MM-DD HH:mm:ss'),
|
||||
endDay: formatDate(endDay, 'YYYY-MM-DD HH:mm:ss')
|
||||
})
|
||||
// 转为 Map 方便按日期快速查找
|
||||
calendarDayMap.value.clear()
|
||||
if (list) {
|
||||
list.forEach((item: CalCalendarDayVO) => {
|
||||
calendarDayMap.value.set(item.day, item)
|
||||
})
|
||||
if (!list) {
|
||||
return
|
||||
}
|
||||
list.forEach((item: CalCalendarDayVO) => {
|
||||
calendarDayMap.value.set(item.day, item)
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择班组 */
|
||||
/** 选择班组后刷新日历 */
|
||||
const onTeamSelected = () => {
|
||||
fetchCalendar()
|
||||
}
|
||||
|
||||
/** 获取指定日期的班次列表 */
|
||||
const getTeamShiftsForDay = (day: string) => {
|
||||
const calDay = calendarDayMap.value.get(day)
|
||||
return calDay?.teamShifts || []
|
||||
}
|
||||
|
||||
/** 获取指定日期的轮班方式 */
|
||||
const getShiftTypeForDay = (day: string) => {
|
||||
const calDay = calendarDayMap.value.get(day)
|
||||
return calDay?.shiftType
|
||||
}
|
||||
|
||||
/** 判断是否周末 */
|
||||
const isWeekend = (day: string): boolean => {
|
||||
const date = new Date(day)
|
||||
const weekDay = date.getDay()
|
||||
return weekDay === 0 || weekDay === 6
|
||||
}
|
||||
|
||||
/** 获取农历显示信息 */
|
||||
const getLunarInfo = (day: string) => {
|
||||
const parts = day.split('-')
|
||||
const year = parseInt(parts[0])
|
||||
const month = parseInt(parts[1])
|
||||
const date = parseInt(parts[2])
|
||||
try {
|
||||
const solarDay = SolarDay.fromYmd(year, month, date)
|
||||
const lunarDay = solarDay.getLunarDay()
|
||||
const solarFestival = solarDay.getFestival()
|
||||
const lunarFestival = lunarDay.getFestival()
|
||||
const termDay = solarDay.getTermDay()
|
||||
const termName = termDay.getDayIndex() === 0 ? termDay.getSolarTerm().getName() : null
|
||||
const lunarMonthName = lunarDay.getLunarMonth().getName()
|
||||
const lunarDayName = lunarDay.getName()
|
||||
return {
|
||||
solarFestival: solarFestival ? solarFestival.getName() : null,
|
||||
lunarFestival: lunarFestival ? lunarFestival.getName() : null,
|
||||
termName,
|
||||
lunarText: lunarMonthName + lunarDayName
|
||||
}
|
||||
} catch {
|
||||
return { solarFestival: null, lunarFestival: null, termName: null, lunarText: '' }
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取农历显示文本 */
|
||||
const getLunarDisplay = (day: string): string => {
|
||||
const info = getLunarInfo(day)
|
||||
return info.solarFestival || info.lunarFestival || info.termName || info.lunarText
|
||||
}
|
||||
|
||||
/** 判断是否有节日 */
|
||||
const hasFestival = (day: string): boolean => {
|
||||
const info = getLunarInfo(day)
|
||||
return !!(info.solarFestival || info.lunarFestival || info.termName)
|
||||
}
|
||||
|
||||
// 监听月份变化
|
||||
/** 监听月份切换,重新加载当月排班 */
|
||||
watch(currentDate, () => {
|
||||
if (selectedTeamId.value) {
|
||||
fetchCalendar()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
<!-- 排班日历 - 按分类视图 -->
|
||||
<template>
|
||||
<div class="flex">
|
||||
<!-- 左侧:班组类型选择 -->
|
||||
<!-- TODO @AI:默认选中首个 -->
|
||||
<div
|
||||
class="w-150px shrink-0 mr-12px border border-solid border-#dcdfe6 rounded-4px overflow-hidden"
|
||||
>
|
||||
<!-- TODO @AI:可以把 @click 在封装下么?更统一一些; -->
|
||||
<div
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.MES_CAL_CALENDAR_TYPE)"
|
||||
:key="dict.value"
|
||||
class="px-16px py-10px cursor-pointer text-14px text-#606266 border-b border-b-solid border-b-#ebeef5 last:border-b-0 hover:bg-#f5f7fa transition-colors"
|
||||
:class="selectedType === dict.value ? 'bg-#ecf5ff text-#409eff font-500' : ''"
|
||||
@click="
|
||||
selectedType = dict.value
|
||||
onTypeSelected()
|
||||
"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:日历 -->
|
||||
<div class="flex-1">
|
||||
<el-calendar v-model="currentDate" v-loading="loading">
|
||||
<template #date-cell="{ data }">
|
||||
<CalendarDateCell
|
||||
:day="data.day"
|
||||
:holiday-set="holidaySet"
|
||||
:calendar-day-map="calendarDayMap"
|
||||
/>
|
||||
</template>
|
||||
</el-calendar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CalCalendarApi, CalCalendarDayVO } from '@/api/mes/cal/calendar'
|
||||
import { CalHolidayApi, CalHolidayVO } from '@/api/mes/cal/holiday'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { HolidayType } from '@/views/mes/utils/constants'
|
||||
import CalendarDateCell from './CalendarDateCell.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const currentDate = ref(new Date()) // 日历当前显示月份
|
||||
const selectedType = ref<number>() // 当前选中的班组类型(枚举 MES_CAL_CALENDAR_TYPE)
|
||||
const calendarDayMap = ref<Map<string, CalCalendarDayVO>>(new Map()) // key: yyyy-MM-dd
|
||||
const holidaySet = ref(new Set<string>()) // 节假日日期集合,key: yyyy-MM-dd
|
||||
|
||||
/** 获取节假日列表,构建节假日日期集合 */
|
||||
const getHolidayList = async () => {
|
||||
holidaySet.value.clear()
|
||||
const list = await CalHolidayApi.getHolidayList()
|
||||
if (!list) {
|
||||
return
|
||||
}
|
||||
list.forEach((item: CalHolidayVO) => {
|
||||
const day = item.day ? formatDate(item.day as any, 'YYYY-MM-DD') : ''
|
||||
if (day && item.type === HolidayType.HOLIDAY) {
|
||||
holidaySet.value.add(day)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询当前月份的排班日历,按选中分类过滤 */
|
||||
const fetchCalendar = async () => {
|
||||
if (!selectedType.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
// 计算当前月份的起止时间
|
||||
const date = currentDate.value
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth()
|
||||
const startDay = new Date(year, month, 1)
|
||||
const endDay = new Date(year, month + 1, 0, 23, 59, 59)
|
||||
const list = await CalCalendarApi.getCalendarList({
|
||||
queryType: 'TYPE',
|
||||
calendarType: selectedType.value,
|
||||
startDay: formatDate(startDay, 'YYYY-MM-DD HH:mm:ss'),
|
||||
endDay: formatDate(endDay, 'YYYY-MM-DD HH:mm:ss')
|
||||
})
|
||||
// 转为 Map 方便按日期快速查找
|
||||
calendarDayMap.value.clear()
|
||||
if (!list) {
|
||||
return
|
||||
}
|
||||
list.forEach((item: CalCalendarDayVO) => {
|
||||
calendarDayMap.value.set(item.day, item)
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择分类后刷新日历 */
|
||||
const onTypeSelected = () => {
|
||||
fetchCalendar()
|
||||
}
|
||||
|
||||
/** 监听月份切换,重新加载当月排班 */
|
||||
watch(currentDate, () => {
|
||||
if (selectedType.value) {
|
||||
fetchCalendar()
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getHolidayList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<!-- 排班日历 - 按个人视图 -->
|
||||
<template>
|
||||
<div>
|
||||
<!-- 顶部:人员选择 -->
|
||||
<el-form :inline="true" label-width="80px" class="mb-10px">
|
||||
<el-form-item label="人员">
|
||||
<el-select
|
||||
v-model="userId"
|
||||
filterable
|
||||
placeholder="请选择人员"
|
||||
class="!w-200px"
|
||||
@change="onUserQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.id"
|
||||
:label="user.nickname"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onUserQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 查询
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 日历 -->
|
||||
<el-calendar v-model="currentDate" v-loading="loading">
|
||||
<template #date-cell="{ data }">
|
||||
<CalendarDateCell
|
||||
:day="data.day"
|
||||
:holiday-set="holidaySet"
|
||||
:calendar-day-map="calendarDayMap"
|
||||
/>
|
||||
</template>
|
||||
</el-calendar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CalCalendarApi, CalCalendarDayVO } from '@/api/mes/cal/calendar'
|
||||
import { CalHolidayApi, CalHolidayVO } from '@/api/mes/cal/holiday'
|
||||
import { getSimpleUserList, UserVO } from '@/api/system/user'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { HolidayType } from '@/views/mes/utils/constants'
|
||||
import CalendarDateCell from './CalendarDateCell.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const currentDate = ref(new Date()) // 日历当前显示月份
|
||||
const userId = ref<number>() // 当前选中的用户编号
|
||||
const userList = ref<UserVO[]>([]) // 所有用户列表
|
||||
const calendarDayMap = ref<Map<string, CalCalendarDayVO>>(new Map()) // key: yyyy-MM-dd
|
||||
const holidaySet = ref(new Set<string>()) // 节假日日期集合,key: yyyy-MM-dd
|
||||
|
||||
/** 获取节假日列表,构建节假日日期集合 */
|
||||
const getHolidayList = async () => {
|
||||
holidaySet.value.clear()
|
||||
const list = await CalHolidayApi.getHolidayList()
|
||||
if (!list) {
|
||||
return
|
||||
}
|
||||
list.forEach((item: CalHolidayVO) => {
|
||||
const day = item.day ? formatDate(item.day as any, 'YYYY-MM-DD') : ''
|
||||
if (day && item.type === HolidayType.HOLIDAY) {
|
||||
holidaySet.value.add(day)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询当前月份的排班日历,按选中人员过滤 */
|
||||
const fetchCalendar = async () => {
|
||||
if (!userId.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
// 计算当前月份的起止时间
|
||||
const date = currentDate.value
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth()
|
||||
const startDay = new Date(year, month, 1)
|
||||
const endDay = new Date(year, month + 1, 0, 23, 59, 59)
|
||||
const list = await CalCalendarApi.getCalendarList({
|
||||
queryType: 'USER',
|
||||
userId: userId.value,
|
||||
startDay: formatDate(startDay, 'YYYY-MM-DD HH:mm:ss'),
|
||||
endDay: formatDate(endDay, 'YYYY-MM-DD HH:mm:ss')
|
||||
})
|
||||
// 转为 Map 方便按日期快速查找
|
||||
calendarDayMap.value.clear()
|
||||
if (!list) {
|
||||
return
|
||||
}
|
||||
list.forEach((item: CalCalendarDayVO) => {
|
||||
calendarDayMap.value.set(item.day, item)
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询按钮 / 下拉选人后刷新日历 */
|
||||
const onUserQuery = () => {
|
||||
fetchCalendar()
|
||||
}
|
||||
|
||||
/** 监听月份切换,重新加载当月排班 */
|
||||
watch(currentDate, () => {
|
||||
if (userId.value) {
|
||||
fetchCalendar()
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
userList.value = await getSimpleUserList()
|
||||
await getHolidayList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -2,24 +2,23 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-tabs v-model="activeTab" type="border-card">
|
||||
<!-- TODO @AI:下面三个组件,都有日历,是不是可以复用下; -->
|
||||
<el-tab-pane label="按分类" name="type">
|
||||
<CalendarTypeView />
|
||||
<TypeView />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="按班组" name="team">
|
||||
<TeamView />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="按个人" name="user">
|
||||
<PersonView />
|
||||
<UserView />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CalendarTypeView from './CalendarTypeView.vue'
|
||||
import TypeView from './TypeView.vue'
|
||||
import TeamView from './TeamView.vue'
|
||||
import PersonView from './PersonView.vue'
|
||||
import UserView from './UserView.vue'
|
||||
|
||||
defineOptions({ name: 'MesCalCalendar' })
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue