排班管理接口对接

This commit is contained in:
re-JZzzz
2025-09-23 20:15:50 +08:00
parent 07c5dcde11
commit 30f5941202
6 changed files with 828 additions and 327 deletions

View File

@ -6,10 +6,11 @@
max-height="600"
stripe
border
v-loading="loading"
>
<!-- 固定列 -->
<el-table-column fixed prop="name" label="姓名" width="120" align="center" />
<el-table-column fixed="left" prop="position" label="岗位" width="120" align="center" />
<el-table-column fixed="left" prop="postName" label="岗位" width="120" align="center" />
<el-table-column fixed="left" prop="weeklyHours" label="周总计/小时" width="120" align="center" />
<!-- 日期列 - 纵向显示号数和星期几 -->
@ -29,17 +30,17 @@
<template #default="scope">
<div
class="schedule-cell"
:style="{ color: getShiftColor(scope.row[`day${index + 1}`]) }"
:class="getShiftClass(scope.row[`day${index + 1}`])"
@click="handleCellClick(scope.row, {...dateInfo, index}, scope)"
>
{{ scope.row[`day${index + 1}`] }}
{{ formatShiftText(scope.row[`day${index + 1}`]) }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<div class="pagination-container">
<!-- <div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
@ -49,137 +50,228 @@
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div> -->
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted, watch } from 'vue';
import { getCurrentMonthDates, getMonthDates } from '@/utils/getDate';
import { ElMessage } from 'element-plus';
const emit = defineEmits<{
'edit-schedule': [rowData: any, columnData: any, cellEvent: any]
// 定义排班类型接口
interface UserTypePair {
schedulingDate: string;
schedulingType: string;
schedulingTypeName: string;
// 可能还有其他字段
[key: string]: any;
}
// 定义员工排班信息接口
interface ScheduleItem {
opsUserId: number;
opsUserName: string;
durationCount: number;
postName: string;
userTypePairs: UserTypePair[];
// 可能还有其他字段
[key: string]: any;
}
// 定义日期信息接口
interface DateInfo {
date: number;
weekDay: string;
fullDate: string;
year: number;
month: number;
}
// 定义表格行数据接口
interface TableRowData {
opsUserId: number;
name: string;
postName: string;
weeklyHours: number;
[key: string]: any; // 动态添加day1, day2等字段
}
// 定义props接收排班数据
const props = defineProps<{
scheduleList: ScheduleItem[];
loading?: boolean;
// 可选:指定要显示的月份
targetMonth?: { year: number; month: number };
}>();
// 员工列表
const employees = [
{ name: '张三', position: '水泥工', weeklyHours: 142 },
{ name: '李四', position: '电工', weeklyHours: 138 },
{ name: '王五', position: '木工', weeklyHours: 145 },
{ name: '赵六', position: '钢筋工', weeklyHours: 140 },
{ name: '钱七', position: '油漆工', weeklyHours: 135 },
{ name: '孙八', position: '瓦工', weeklyHours: 143 },
{ name: '周九', position: '钳工', weeklyHours: 137 },
{ name: '吴十', position: '管道工', weeklyHours: 139 },
{ name: '郑十一', position: '焊工', weeklyHours: 141 },
{ name: '王十二', position: '起重工', weeklyHours: 136 }
];
const emit = defineEmits<{
'edit-schedule': [rowData: TableRowData, columnData: DateInfo & { index: number }, cellEvent: any];
'page-change': [currentPage: number, pageSize: number];
}>();
// 排班类型
const shifts = ['早班', '中班', '晚班', '休息'];
// 排班类型与颜色的映射关系
const shiftColorMap = {
'早班': '#67c23a', // 绿色
'中班': '#e6a23c', // 橙色
'晚班': '#409eff', // 蓝色
'休息': '#909399' // 灰色
};
// 根据排班类型获取对应的颜色
const getShiftColor = (shiftType: string) => {
return shiftColorMap[shiftType as keyof typeof shiftColorMap] || '#333';
// 排班类型与样式的映射关系
const shiftConfig = {
'早班': { color: '#67c23a', className: 'morning-shift' },
'中班': { color: '#e6a23c', className: 'afternoon-shift' },
'晚班': { color: '#409eff', className: 'evening-shift' },
'休息': { color: '#909399', className: 'rest-day' },
};
// 获取当前月的日期信息
const currentMonthDates = ref<any[]>([]);
// 计算当前月份并生成日期信息
const getCurrentMonthDates = () => {
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth(); // 0-11
// 获取当月第一天
const firstDay = new Date(year, month, 1);
// 获取当月最后一天
const lastDay = new Date(year, month + 1, 0);
// 当月总天数
const daysInMonth = lastDay.getDate();
const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
const dates = [];
// 生成当月所有日期信息
for (let i = 1; i <= daysInMonth; i++) {
const date = new Date(year, month, i);
const weekDayIndex = date.getDay(); // 0-60表示星期日
dates.push({
date: i,
weekDay: weekdays[weekDayIndex],
fullDate: `${year}-${String(month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}`
});
}
return dates;
};
const currentMonthDates = ref<(DateInfo & { year: number; month: number })[]>([]);
// 分页相关状态
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(50); // 总数据条数,模拟数据
const total = computed(() => props.scheduleList ? props.scheduleList.length : 0);
// 格式化排班文本,支持多排班情况
const formatShiftText = (shiftData: any): string => {
if (!shiftData) return '休息';
// 如果是字符串,直接返回
if (typeof shiftData === 'string') {
return shiftData;
}
// 如果是数组,返回第一个排班类型
if (Array.isArray(shiftData)) {
return shiftData.length > 0 ? shiftData[0].schedulingTypeName || '休息' : '休息';
}
// 如果是对象,返回排班类型名称
if (typeof shiftData === 'object' && shiftData.schedulingTypeName) {
return shiftData.schedulingTypeName;
}
return '休息';
};
// 获取排班对应的样式类名
const getShiftClass = (shiftData: any): string => {
const shiftText = formatShiftText(shiftData);
return shiftConfig[shiftText as keyof typeof shiftConfig]?.className || 'unknown-shift';
};
// 生成排班数据
const scheduleData = computed(() => {
const scheduleData = computed((): TableRowData[] => {
const startIndex = (currentPage.value - 1) * pageSize.value;
const endIndex = startIndex + pageSize.value;
return Array.from({ length: total.value }, (_, index) => {
// 循环使用员工数据
const employee = employees[index % employees.length];
// 为每行生成不同的排班组合
const rowData = {
name: employee.name,
position: employee.position,
weeklyHours: employee.weeklyHours
// 确保 props.scheduleList 存在
const scheduleList = props.scheduleList || [];
// 如果没有数据且loading为false返回空数组显示空状态
if (scheduleList.length === 0 && !props.loading) {
return [];
}
// 处理排班数据
return scheduleList.map((item: ScheduleItem) => {
const rowData: TableRowData = {
opsUserId: item.opsUserId,
name: item.opsUserName || `未知员工${item.opsUserId}`,
postName: item.postName || '未知岗位',
weeklyHours: item.durationCount || 0
};
// 为当月每一天生成排班数据
currentMonthDates.value.forEach((_, dayIndex) => {
// 使用不同的种子生成略有变化的排班模式
const seed = (index * 3 + dayIndex + 1) % shifts.length;
rowData[`day${dayIndex + 1}`] = shifts[seed];
currentMonthDates.value.forEach((dateInfo, dayIndex) => {
// 格式化日期为 YYYY-MM-DD
const dateKey = `${dateInfo.year}-${String(dateInfo.month).padStart(2, '0')}-${String(dateInfo.date).padStart(2, '0')}`;
// 从userTypePairs中查找对应日期的所有排班信息
let daySchedule = null;
if (item.userTypePairs && Array.isArray(item.userTypePairs)) {
// 查找对应日期的所有排班信息
const dateSchedules = item.userTypePairs.filter(pair => pair.schedulingDate === dateKey);
// 如果有多个排班,也返回,方便后续扩展显示多个排班
daySchedule = dateSchedules.length > 0 ? dateSchedules : null;
}
// 如果找到排班信息,存储原始数据;如果没有,设置为'休息'
rowData[`day${dayIndex + 1}`] = daySchedule || '休息';
});
return rowData;
}).slice(startIndex, endIndex);
});
// 更新日期列表
const updateDates = () => {
if (props.targetMonth) {
// 使用指定的月份
const dates = getMonthDates(props.targetMonth.year, props.targetMonth.month - 1); // getMonthDates的month参数是0-11
currentMonthDates.value = dates.map(date => ({
...date,
year: props.targetMonth!.year,
month: props.targetMonth!.month
}));
} else {
// 使用当前月份
const today = new Date();
const dates = getCurrentMonthDates();
currentMonthDates.value = dates.map(date => ({
...date,
year: today.getFullYear(),
month: today.getMonth() + 1
}));
}
};
// 分页大小变化处理
const handleSizeChange = (size: number) => {
pageSize.value = size;
currentPage.value = 1; // 重置为第一页
emit('page-change', currentPage.value, pageSize.value);
};
// 当前页码变化处理
const handleCurrentChange = (current: number) => {
currentPage.value = current;
emit('page-change', currentPage.value, pageSize.value);
};
// 组件挂载时获取当前月数据
onMounted(() => {
currentMonthDates.value = getCurrentMonthDates();
});
// 处理单元格点击事件
const handleCellClick = (rowData: any, columnData: any, cellEvent: any) => {
const handleCellClick = (rowData: TableRowData, columnData: DateInfo & { index: number }, cellEvent: any) => {
// 获取当前单元格的排班数据
const cellData = rowData[`day${columnData.index + 1}`];
const shiftText = formatShiftText(cellData);
// 如果是休息状态,显示提示信息,不触发编辑事件
if (shiftText === '休息') {
ElMessage.warning('请前往管理考勤增加排班');
return;
}
// 非休息状态,触发编辑事件
emit('edit-schedule', rowData, columnData, cellEvent);
};
// 组件挂载时初始化
onMounted(() => {
updateDates();
});
// 监听目标月份变化,更新日期列表
watch(() => props.targetMonth, () => {
updateDates();
}, { deep: true });
// 监听排班数据变化,重置页码
watch(() => props.scheduleList, () => {
currentPage.value = 1;
}, { deep: true });
</script>
<style scoped>
.schedule-table-container {
overflow-x: auto;
padding: 16px;
background: #fff;
border-radius: 4px;
}
/* 优化滚动条样式 */
@ -228,18 +320,6 @@ const handleCellClick = (rowData: any, columnData: any, cellEvent: any) => {
padding: 8px 0;
}
/* 排班单元格样式 */
.schedule-cell {
padding: 8px 0;
cursor: pointer;
transition: all 0.2s ease;
}
.schedule-cell:hover {
background-color: #f5f7fa;
transform: scale(1.05);
}
.date-number {
font-size: 16px;
font-weight: 600;
@ -248,20 +328,64 @@ const handleCellClick = (rowData: any, columnData: any, cellEvent: any) => {
}
.week-day {
font-size: 12px;
color: #666;
}
/* 分页容器样式 */
.pagination-container {
margin-top: 16px;
display: flex;
justify-content: flex-end;
align-items: center;
}
/* 分页组件样式优化 */
:deep(.el-pagination) {
font-size: 14px;
}
font-size: 12px;
color: #666;
}
/* 排班单元格样式 */
.schedule-cell {
padding: 8px 0;
cursor: pointer;
transition: all 0.2s ease;
text-align: center;
border-radius: 4px;
}
.schedule-cell:hover {
background-color: #f5f7fa;
transform: scale(1.05);
}
/* 排班类型样式 */
.morning-shift {
color: #67c23a; /* 早班 - 绿色 */
font-weight: 500;
}
.afternoon-shift {
color: #e6a23c; /* 中班 - 橙色 */
font-weight: 500;
}
.evening-shift {
color: #409eff; /* 晚班 - 蓝色 */
font-weight: 500;
}
.rest-day {
color: #909399; /* 休息 - 灰色 */
}
.unknown-shift {
color: #333; /* 未知类型 - 默认黑色 */
}
/* 分页容器样式 */
.pagination-container {
margin-top: 16px;
display: flex;
justify-content: flex-end;
align-items: center;
}
/* 分页组件样式优化 */
:deep(.el-pagination) {
font-size: 14px;
}
/* 加载状态样式优化 */
:deep(.el-loading-mask) {
background-color: rgba(255, 255, 255, 0.8);
}
</style>