Merge branch 'lx' of http://xny.yj-3d.com:3000/taoge_xiaodi/maintenance_system into tcy
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
import { AxiosPromise } from 'axios';
|
import { AxiosPromise } from 'axios';
|
||||||
|
import { SchedulingVO } from './types';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,11 +17,63 @@ export function getPaibanRenYuanList(deptId:string | number): AxiosPromise<any>
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询运维-人员排班列表
|
* 查询运维-人员排班列表
|
||||||
* @param deptId
|
|
||||||
*/
|
*/
|
||||||
export function getPaibanRiLiList(deptId:string | number): AxiosPromise<any> {
|
export function getPaibanRiLiList(query?: SchedulingVO): AxiosPromise<SchedulingVO[]> {
|
||||||
return request({
|
return request({
|
||||||
url: `/ops/personnel/scheduling/getRiLiList/`+deptId,
|
url: `/ops/personnel/scheduling/getRiLiList`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运维-人员排班-查询排班列表
|
||||||
|
*/
|
||||||
|
export function getPaibanListPage(query?: SchedulingVO): AxiosPromise<SchedulingVO[]> {
|
||||||
|
return request({
|
||||||
|
url: `/ops/personnel/scheduling/list`,
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 运维-人员排班-安排排班
|
||||||
|
*/
|
||||||
|
export function savePaiban(data: any): AxiosPromise<any> {
|
||||||
|
return request({
|
||||||
|
url: `/ops/personnel/scheduling/all`,
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运维-人员排班-修改排班
|
||||||
|
*/
|
||||||
|
export function updatePaiban(data:any): AxiosPromise<any> {
|
||||||
|
return request({
|
||||||
|
url: `/ops/personnel/scheduling`,
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运维-人员排班-批量修改排班
|
||||||
|
*/
|
||||||
|
export function updateAllPaiban(): AxiosPromise<any> {
|
||||||
|
return request({
|
||||||
|
url: `/ops/personnel/scheduling/all`,
|
||||||
|
method: 'put',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运维-人员排班-删除排班
|
||||||
|
*/
|
||||||
|
export function deletePaiban(ids: string): AxiosPromise<any> {
|
||||||
|
return request({
|
||||||
|
url: `/ops/personnel/scheduling/${ids}`,
|
||||||
|
method: 'delete',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
export interface SchedulingVO {
|
||||||
|
/**
|
||||||
|
* 开始时间
|
||||||
|
*/
|
||||||
|
schedulingStartDate: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束时间
|
||||||
|
*/
|
||||||
|
schedulingEndDate: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID
|
||||||
|
*/
|
||||||
|
projectId?: string | number;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// export interface SchedulingQuery extends PageQuery {
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 开始时间
|
||||||
|
// */
|
||||||
|
// schedulingStartDate: string;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 结束时间
|
||||||
|
// */
|
||||||
|
// schedulingEndDate: string;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 部门ID
|
||||||
|
// */
|
||||||
|
// projectId?: string | number;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
70
src/utils/getDate.ts
Normal file
70
src/utils/getDate.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// 获取指定月份的日期信息
|
||||||
|
export interface DateInfo {
|
||||||
|
date: number;
|
||||||
|
weekDay: string;
|
||||||
|
fullDate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前月份的日期信息
|
||||||
|
* @returns 包含当月所有日期信息的数组
|
||||||
|
*/
|
||||||
|
export const getCurrentMonthDates = (): DateInfo[] => {
|
||||||
|
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: DateInfo[] = [];
|
||||||
|
|
||||||
|
// 生成当月所有日期信息
|
||||||
|
for (let i = 1; i <= daysInMonth; i++) {
|
||||||
|
const date = new Date(year, month, i);
|
||||||
|
const weekDayIndex = date.getDay(); // 0-6,0表示星期日
|
||||||
|
dates.push({
|
||||||
|
date: i,
|
||||||
|
weekDay: weekdays[weekDayIndex],
|
||||||
|
fullDate: `${year}-${String(month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return dates;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定月份的日期信息
|
||||||
|
* @param year 年份
|
||||||
|
* @param month 月份(0-11)
|
||||||
|
* @returns 包含指定月份所有日期信息的数组
|
||||||
|
*/
|
||||||
|
export const getMonthDates = (year: number, month: number): DateInfo[] => {
|
||||||
|
// 获取当月第一天
|
||||||
|
const firstDay = new Date(year, month, 1);
|
||||||
|
// 获取当月最后一天
|
||||||
|
const lastDay = new Date(year, month + 1, 0);
|
||||||
|
// 当月总天数
|
||||||
|
const daysInMonth = lastDay.getDate();
|
||||||
|
|
||||||
|
const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
|
||||||
|
const dates: DateInfo[] = [];
|
||||||
|
|
||||||
|
// 生成当月所有日期信息
|
||||||
|
for (let i = 1; i <= daysInMonth; i++) {
|
||||||
|
const date = new Date(year, month, i);
|
||||||
|
const weekDayIndex = date.getDay(); // 0-6,0表示星期日
|
||||||
|
dates.push({
|
||||||
|
date: i,
|
||||||
|
weekDay: weekdays[weekDayIndex],
|
||||||
|
fullDate: `${year}-${String(month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return dates;
|
||||||
|
};
|
||||||
@ -1,88 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 人员排班弹窗 -->
|
<!-- 人员排班弹窗 -->
|
||||||
<el-dialog
|
<el-dialog :model-value="manageAttendDialogVisible" @update:model-value="handleDialogVisibleChange" title="管理考勤"
|
||||||
:model-value="manageAttendDialogVisible"
|
width="500">
|
||||||
@update:model-value="handleDialogVisibleChange"
|
<!-- 添加表单引用和校验规则 -->
|
||||||
title="管理考勤"
|
<el-form ref="attendFormRef" :model="attendForm" :rules="formRules" label-width="100px">
|
||||||
width="500"
|
<el-form-item label="选择日期" prop="schedulingDate">
|
||||||
>
|
<el-date-picker v-model="attendForm.schedulingDate" type="date" placeholder="选择日期" style="width: 100%;"
|
||||||
<el-form :model="attendForm" label-width="100px">
|
:disabled-date="(time) => time.getTime() < Date.now() - 8.64e7" :date-format="'yyyy-MM-dd'"
|
||||||
<el-form-item label="选择日期">
|
value-format="YYYY-MM-DD" />
|
||||||
<el-date-picker
|
|
||||||
v-model="attendForm.date"
|
|
||||||
type="date"
|
|
||||||
placeholder="选择日期"
|
|
||||||
style="width: 100%;"
|
|
||||||
:disabled-date="(time) => time.getTime() < Date.now() - 8.64e7"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 排班类型为空提示 -->
|
||||||
|
<div v-if="shiftTypes.length === 0" class="empty-tip">
|
||||||
|
<el-alert title="暂无排班类型,请先添加排班类型" type="warning" show-icon :closable="false" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 排班人员为空提示 -->
|
||||||
|
<div v-if="props.personnelList.length === 0" class="empty-tip">
|
||||||
|
<el-alert title="暂无排班人员,请先添加排班人员" type="warning" show-icon :closable="false" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 动态排班表单 -->
|
<!-- 动态排班表单 -->
|
||||||
<div v-for="(item, index) in attendForm.shifts" :key="index" class="dynamic-shift-item">
|
<!-- 动态排班表单 -->
|
||||||
|
<div v-for="(item, index) in attendForm.userTypeBos" :key="index" class="dynamic-shift-item">
|
||||||
<el-form-item :label="index === 0 ? '排班设置' : ''" :required="index === 0">
|
<el-form-item :label="index === 0 ? '排班设置' : ''" :required="index === 0">
|
||||||
<div class="shift-form-row">
|
<div class="shift-form-row">
|
||||||
<!-- 排班类型选择 -->
|
<!-- 排班类型选择 -->
|
||||||
<el-select
|
<el-select v-model="item.schedulingType" placeholder="请选择排班类型" style="width: 40%; margin-right: 10px;"
|
||||||
v-model="item.type"
|
filterable :validate-event="false">
|
||||||
placeholder="请选择排班类型"
|
<!-- 使用完整的shiftTypes列表以确保已选项目也能正确显示label -->
|
||||||
style="width: 40%; margin-right: 10px;"
|
<el-option v-for="option in shiftTypes" :key="option.value" :label="option.label" :value="option.value"
|
||||||
>
|
:disabled="attendForm.userTypeBos.some((bosItem) => bosItem.schedulingType === option.value && bosItem !== item)"></el-option>
|
||||||
<el-option
|
|
||||||
v-for="option in availableShiftOptions"
|
|
||||||
:key="option.value"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|
||||||
<!-- 人员选择 -->
|
<!-- 人员选择 -->
|
||||||
<el-select
|
<el-select v-model="item.opsUserId" placeholder="请选择人员" style="width: 50%; margin-right: 10px;" multiple
|
||||||
v-model="item.personnel"
|
filterable :validate-event="false">
|
||||||
placeholder="请选择人员"
|
<el-option v-for="person in props.personnelList" :key="person.value" :label="person.label"
|
||||||
style="width: 50%; margin-right: 10px;"
|
:value="person.value"></el-option>
|
||||||
multiple
|
|
||||||
filterable
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="person in props.personnelList"
|
|
||||||
:key="person.value"
|
|
||||||
:label="person.label"
|
|
||||||
:value="person.value"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|
||||||
<!-- 删除按钮 (仅在不是第一个项目时显示) -->
|
<!-- 删除按钮 (仅在不是第一个项目时显示) -->
|
||||||
<el-button
|
<el-button v-if="index > 0" type="danger" icon="CircleCloseFilled" circle
|
||||||
v-if="index > 0"
|
@click="removeShiftItem(index)"></el-button>
|
||||||
type="danger"
|
|
||||||
icon="CircleCloseFilled"
|
|
||||||
circle
|
|
||||||
@click="removeShiftItem(index)"
|
|
||||||
></el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 添加排班类型按钮 -->
|
<!-- 添加排班类型按钮 -->
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button
|
<el-button type="primary" icon="CirclePlusFilled" @click="addShiftItem"
|
||||||
type="primary"
|
:disabled="attendForm.userTypeBos.length >= shiftTypes.length">
|
||||||
icon="CirclePlusFilled"
|
|
||||||
@click="addShiftItem"
|
|
||||||
:disabled="attendForm.shifts.length >= shiftTypes.length"
|
|
||||||
>
|
|
||||||
添加排班类型
|
添加排班类型
|
||||||
</el-button>
|
</el-button>
|
||||||
<div v-if="attendForm.shifts.length > 0" class="form-tip">
|
<div v-if="attendForm.userTypeBos.length > 0" class="form-tip">
|
||||||
提示:已添加 {{ attendForm.shifts.length }}/{{ shiftTypes.length }} 种排班类型
|
提示:已添加 {{ attendForm.userTypeBos.length }}/{{ shiftTypes.length }} 种排班类型
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="handleCancel">取消</el-button>
|
<el-button @click="handleCancel">取消</el-button>
|
||||||
<el-button type="primary" @click="handleConfirm">
|
<el-button type="primary" @click="handleConfirm"
|
||||||
|
:disabled="shiftTypes.length === 0 || props.personnelList.length === 0">
|
||||||
确认
|
确认
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@ -102,13 +83,12 @@ const props = defineProps({
|
|||||||
// 排班人员列表数据
|
// 排班人员列表数据
|
||||||
personnelList: {
|
personnelList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [
|
default: () => []
|
||||||
{ label: '张三', value: '1' },
|
},
|
||||||
{ label: '李四', value: '2' },
|
// 排班类型列表
|
||||||
{ label: '王五', value: '3' },
|
typeList: {
|
||||||
{ label: '赵六', value: '4' },
|
type: Array,
|
||||||
{ label: '钱七', value: '5' }
|
default: () => []
|
||||||
]
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -118,67 +98,66 @@ const emit = defineEmits<{
|
|||||||
'confirm': [formData: any];
|
'confirm': [formData: any];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// 排班类型列表
|
// 排班类型列表(从传入的typeList派生)
|
||||||
const shiftTypes = [
|
const shiftTypes = ref((props.typeList || []).map(item => ({
|
||||||
{ label: '早班', value: '早班' },
|
label: (item as { schedulingName: string }).schedulingName,
|
||||||
{ label: '中班', value: '中班' },
|
value: (item as { id: any }).id
|
||||||
{ label: '晚班', value: '晚班' },
|
})));
|
||||||
{ label: '休息', value: '休息' }
|
|
||||||
];
|
// 导入表单相关类型和消息组件
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
// 表单引用
|
||||||
|
const attendFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
// 考勤表单数据
|
// 考勤表单数据
|
||||||
const attendForm = ref({
|
const attendForm = ref({
|
||||||
date: '',
|
schedulingDate: '',
|
||||||
shifts: [
|
userTypeBos: [
|
||||||
{ type: '', personnel: [] }
|
{ schedulingType: '', opsUserId: [] }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取可用的排班类型选项(排除已添加的类型)
|
// 表单校验规则
|
||||||
const availableShiftOptions = ref(shiftTypes);
|
const formRules = ref<FormRules>({
|
||||||
|
schedulingDate: [
|
||||||
|
{ required: true, message: '请选择日期', trigger: 'change' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
// 监听排班类型变化,更新可用选项
|
// 监听typeList变化,更新shiftTypes
|
||||||
const setupWatchers = () => {
|
watch(() => props.typeList, (newTypeList) => {
|
||||||
attendForm.value.shifts.forEach((item) => {
|
shiftTypes.value = (newTypeList || []).map(item => ({
|
||||||
watch(() => item.type, (newType) => {
|
label: (item as { schedulingName: string }).schedulingName,
|
||||||
updateAvailableShiftOptions();
|
value: (item as { id: any }).id
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 初始化时设置监听器
|
|
||||||
setupWatchers();
|
|
||||||
|
|
||||||
// 更新可用的排班类型选项
|
// 当没有排班类型时,清空排班项;有排班类型但没有排班项时,添加一个空排班项
|
||||||
const updateAvailableShiftOptions = () => {
|
if (shiftTypes.value.length === 0) {
|
||||||
const usedTypes = attendForm.value.shifts
|
attendForm.value.userTypeBos = [];
|
||||||
.map(item => item.type)
|
} else if (attendForm.value.userTypeBos.length === 0) {
|
||||||
.filter(type => type !== '');
|
attendForm.value.userTypeBos = [{ schedulingType: '', opsUserId: [] }];
|
||||||
|
}
|
||||||
availableShiftOptions.value = shiftTypes.filter(type =>
|
}, { deep: true });
|
||||||
!usedTypes.includes(type.value)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 添加新的排班类型项
|
// 添加新的排班类型项
|
||||||
const addShiftItem = () => {
|
const addShiftItem = () => {
|
||||||
// 检查是否还有可用的排班类型
|
// 检查是否还有可用的排班类型
|
||||||
if (attendForm.value.shifts.length < shiftTypes.length) {
|
if (attendForm.value.userTypeBos.length < shiftTypes.value.length) {
|
||||||
attendForm.value.shifts.push({ type: '', personnel: [] });
|
attendForm.value.userTypeBos.push({ schedulingType: '', opsUserId: [] });
|
||||||
|
|
||||||
// 为新添加的项添加监听器
|
// 现在不再需要为新添加的项添加监听器
|
||||||
const newItem = attendForm.value.shifts[attendForm.value.shifts.length - 1];
|
|
||||||
watch(() => newItem.type, (newType) => {
|
|
||||||
updateAvailableShiftOptions();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除排班类型项
|
// 删除排班类型项
|
||||||
const removeShiftItem = (index: number) => {
|
const removeShiftItem = (index: number) => {
|
||||||
if (attendForm.value.shifts.length > 1) {
|
if (attendForm.value.userTypeBos.length > 1) {
|
||||||
attendForm.value.shifts.splice(index, 1);
|
attendForm.value.userTypeBos.splice(index, 1);
|
||||||
updateAvailableShiftOptions();
|
// 不再需要更新可用选项
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -197,24 +176,52 @@ const handleDialogVisibleChange = (newVisible: boolean) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 处理确认
|
// 处理确认
|
||||||
const handleConfirm = () => {
|
const handleConfirm = async () => {
|
||||||
// 提交表单数据给父组件
|
if (!attendFormRef.value) return;
|
||||||
emit('confirm', attendForm.value);
|
|
||||||
emit('update:manageAttendDialogVisible', false);
|
try {
|
||||||
resetForm();
|
// 验证日期
|
||||||
|
await attendFormRef.value.validateField('schedulingDate');
|
||||||
|
|
||||||
|
// 验证每个排班项
|
||||||
|
let isValid = true;
|
||||||
|
const validationPromises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
attendForm.value.userTypeBos.forEach((item, index) => {
|
||||||
|
// 验证排班类型
|
||||||
|
if (!item.schedulingType) {
|
||||||
|
isValid = false;
|
||||||
|
ElMessage.error(`第${index + 1}行排班类型不能为空`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证人员选择
|
||||||
|
if (!item.opsUserId || item.opsUserId.length === 0) {
|
||||||
|
isValid = false;
|
||||||
|
ElMessage.error(`第${index + 1}行请至少选择一名人员`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
// 提交表单数据给父组件
|
||||||
|
emit('confirm', attendForm.value);
|
||||||
|
emit('update:manageAttendDialogVisible', false);
|
||||||
|
resetForm();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 日期验证失败
|
||||||
|
console.error('表单验证失败', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置表单
|
// 重置表单
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
attendForm.value = {
|
attendForm.value = {
|
||||||
date: '',
|
schedulingDate: '',
|
||||||
shifts: [
|
// 只有当有排班类型时才初始化一个空的排班项
|
||||||
{ type: '', personnel: [] }
|
userTypeBos: shiftTypes.value.length > 0 ? [{ schedulingType: '', opsUserId: [] }] : []
|
||||||
]
|
|
||||||
};
|
};
|
||||||
updateAvailableShiftOptions();
|
|
||||||
// 重新设置监听器
|
|
||||||
setupWatchers();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听弹窗显示状态变化,在显示时重置表单
|
// 监听弹窗显示状态变化,在显示时重置表单
|
||||||
@ -248,19 +255,33 @@ watch(() => props.manageAttendDialogVisible, (newVisible) => {
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 空数据提示样式 */
|
||||||
|
.empty-tip {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip .el-alert {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确认按钮禁用状态提示 */
|
||||||
|
.dialog-footer .el-button.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
/* 响应式调整 */
|
/* 响应式调整 */
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.shift-form-row {
|
.shift-form-row {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shift-form-row .el-select {
|
.shift-form-row .el-select {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
margin-right: 0 !important;
|
margin-right: 0 !important;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shift-form-row .el-button {
|
.shift-form-row .el-button {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
|
|||||||
@ -6,10 +6,11 @@
|
|||||||
max-height="600"
|
max-height="600"
|
||||||
stripe
|
stripe
|
||||||
border
|
border
|
||||||
|
v-loading="loading"
|
||||||
>
|
>
|
||||||
<!-- 固定列 -->
|
<!-- 固定列 -->
|
||||||
<el-table-column fixed prop="name" label="姓名" width="120" align="center" />
|
<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" />
|
<el-table-column fixed="left" prop="weeklyHours" label="周总计/小时" width="120" align="center" />
|
||||||
|
|
||||||
<!-- 日期列 - 纵向显示号数和星期几 -->
|
<!-- 日期列 - 纵向显示号数和星期几 -->
|
||||||
@ -29,17 +30,17 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div
|
<div
|
||||||
class="schedule-cell"
|
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)"
|
@click="handleCellClick(scope.row, {...dateInfo, index}, scope)"
|
||||||
>
|
>
|
||||||
{{ scope.row[`day${index + 1}`] }}
|
{{ formatShiftText(scope.row[`day${index + 1}`]) }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页组件 -->
|
<!-- 分页组件 -->
|
||||||
<div class="pagination-container">
|
<!-- <div class="pagination-container">
|
||||||
<el-pagination
|
<el-pagination
|
||||||
v-model:current-page="currentPage"
|
v-model:current-page="currentPage"
|
||||||
v-model:page-size="pageSize"
|
v-model:page-size="pageSize"
|
||||||
@ -49,137 +50,228 @@
|
|||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 emit = defineEmits<{
|
||||||
const employees = [
|
'edit-schedule': [rowData: TableRowData, columnData: DateInfo & { index: number }, cellEvent: any];
|
||||||
{ name: '张三', position: '水泥工', weeklyHours: 142 },
|
'page-change': [currentPage: number, pageSize: number];
|
||||||
{ 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 shifts = ['早班', '中班', '晚班', '休息'];
|
const shiftConfig = {
|
||||||
|
'早班': { color: '#67c23a', className: 'morning-shift' },
|
||||||
// 排班类型与颜色的映射关系
|
'中班': { color: '#e6a23c', className: 'afternoon-shift' },
|
||||||
const shiftColorMap = {
|
'晚班': { color: '#409eff', className: 'evening-shift' },
|
||||||
'早班': '#67c23a', // 绿色
|
'休息': { color: '#909399', className: 'rest-day' },
|
||||||
'中班': '#e6a23c', // 橙色
|
|
||||||
'晚班': '#409eff', // 蓝色
|
|
||||||
'休息': '#909399' // 灰色
|
|
||||||
};
|
|
||||||
|
|
||||||
// 根据排班类型获取对应的颜色
|
|
||||||
const getShiftColor = (shiftType: string) => {
|
|
||||||
return shiftColorMap[shiftType as keyof typeof shiftColorMap] || '#333';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取当前月的日期信息
|
// 获取当前月的日期信息
|
||||||
const currentMonthDates = ref<any[]>([]);
|
const currentMonthDates = ref<(DateInfo & { year: number; month: number })[]>([]);
|
||||||
|
|
||||||
// 计算当前月份并生成日期信息
|
|
||||||
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-6,0表示星期日
|
|
||||||
dates.push({
|
|
||||||
date: i,
|
|
||||||
weekDay: weekdays[weekDayIndex],
|
|
||||||
fullDate: `${year}-${String(month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return dates;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 分页相关状态
|
// 分页相关状态
|
||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const pageSize = ref(10);
|
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 startIndex = (currentPage.value - 1) * pageSize.value;
|
||||||
const endIndex = startIndex + pageSize.value;
|
const endIndex = startIndex + pageSize.value;
|
||||||
|
|
||||||
return Array.from({ length: total.value }, (_, index) => {
|
// 确保 props.scheduleList 存在
|
||||||
// 循环使用员工数据
|
const scheduleList = props.scheduleList || [];
|
||||||
const employee = employees[index % employees.length];
|
|
||||||
|
// 如果没有数据且loading为false,返回空数组显示空状态
|
||||||
// 为每行生成不同的排班组合
|
if (scheduleList.length === 0 && !props.loading) {
|
||||||
const rowData = {
|
return [];
|
||||||
name: employee.name,
|
}
|
||||||
position: employee.position,
|
|
||||||
weeklyHours: employee.weeklyHours
|
// 处理排班数据
|
||||||
|
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) => {
|
currentMonthDates.value.forEach((dateInfo, dayIndex) => {
|
||||||
// 使用不同的种子生成略有变化的排班模式
|
// 格式化日期为 YYYY-MM-DD
|
||||||
const seed = (index * 3 + dayIndex + 1) % shifts.length;
|
const dateKey = `${dateInfo.year}-${String(dateInfo.month).padStart(2, '0')}-${String(dateInfo.date).padStart(2, '0')}`;
|
||||||
rowData[`day${dayIndex + 1}`] = shifts[seed];
|
|
||||||
|
// 从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;
|
return rowData;
|
||||||
}).slice(startIndex, endIndex);
|
}).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) => {
|
const handleSizeChange = (size: number) => {
|
||||||
pageSize.value = size;
|
pageSize.value = size;
|
||||||
currentPage.value = 1; // 重置为第一页
|
currentPage.value = 1; // 重置为第一页
|
||||||
|
emit('page-change', currentPage.value, pageSize.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 当前页码变化处理
|
// 当前页码变化处理
|
||||||
const handleCurrentChange = (current: number) => {
|
const handleCurrentChange = (current: number) => {
|
||||||
currentPage.value = current;
|
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);
|
emit('edit-schedule', rowData, columnData, cellEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 组件挂载时初始化
|
||||||
|
onMounted(() => {
|
||||||
|
updateDates();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听目标月份变化,更新日期列表
|
||||||
|
watch(() => props.targetMonth, () => {
|
||||||
|
updateDates();
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
|
// 监听排班数据变化,重置页码
|
||||||
|
watch(() => props.scheduleList, () => {
|
||||||
|
currentPage.value = 1;
|
||||||
|
}, { deep: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.schedule-table-container {
|
.schedule-table-container {
|
||||||
overflow-x: auto;
|
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;
|
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 {
|
.date-number {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -248,20 +328,64 @@ const handleCellClick = (rowData: any, columnData: any, cellEvent: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.week-day {
|
.week-day {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分页容器样式 */
|
/* 排班单元格样式 */
|
||||||
.pagination-container {
|
.schedule-cell {
|
||||||
margin-top: 16px;
|
padding: 8px 0;
|
||||||
display: flex;
|
cursor: pointer;
|
||||||
justify-content: flex-end;
|
transition: all 0.2s ease;
|
||||||
align-items: center;
|
text-align: center;
|
||||||
}
|
border-radius: 4px;
|
||||||
|
}
|
||||||
/* 分页组件样式优化 */
|
|
||||||
:deep(.el-pagination) {
|
.schedule-cell:hover {
|
||||||
font-size: 14px;
|
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>
|
</style>
|
||||||
|
|||||||
@ -52,7 +52,8 @@
|
|||||||
管理考勤
|
管理考勤
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<renyuanpaiban @edit-schedule="handleEditSchedule"></renyuanpaiban>
|
<renyuanpaiban @edit-schedule="handleEditSchedule" :schedule-list="scheduleList">
|
||||||
|
</renyuanpaiban>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -69,12 +70,9 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<!-- 人员排班弹窗组件 -->
|
<!-- 人员排班弹窗组件 -->
|
||||||
<renyuanguanliDialog
|
<renyuanguanliDialog v-model:manageAttendDialogVisible="manageAttendDialogVisible"
|
||||||
v-model:manageAttendDialogVisible="manageAttendDialogVisible"
|
@confirm="handleAttendConfirm" :personnel-list="paibanRenYuanList" :type-list="scheduleTypes" />
|
||||||
@confirm="handleAttendConfirm"
|
|
||||||
:personnel-list="paibanRenYuanList"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 编辑排班弹窗 -->
|
<!-- 编辑排班弹窗 -->
|
||||||
<el-dialog v-model="editScheduleDialogVisible" title="修改排班" width="400">
|
<el-dialog v-model="editScheduleDialogVisible" title="修改排班" width="400">
|
||||||
<el-form :model="editScheduleForm" label-width="100px">
|
<el-form :model="editScheduleForm" label-width="100px">
|
||||||
@ -89,11 +87,12 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="修改为">
|
<el-form-item label="修改为">
|
||||||
<el-select v-model="editScheduleForm.newShift" placeholder="请选择排班类型" style="width: 100%;">
|
<el-select v-model="editScheduleForm.newShift" placeholder="请选择排班类型" style="width: 100%;">
|
||||||
<el-option v-for="option in shiftOptions" :key="option.value" :label="option.label" :value="option.value"></el-option>
|
<el-option v-for="option in editscheduleTypes" :key="option.id" :label="option.schedulingName"
|
||||||
|
:value="option.id"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="editScheduleDialogVisible = false">取消</el-button>
|
<el-button @click="editScheduleDialogVisible = false">取消</el-button>
|
||||||
@ -114,102 +113,266 @@ import calendar from '@/views/integratedManage/attendManage/components/rightBox/
|
|||||||
import totalView from '@/views/integratedManage/attendManage/components/totalView.vue'
|
import totalView from '@/views/integratedManage/attendManage/components/totalView.vue'
|
||||||
import renyuanpaiban from '@/views/integratedManage/attendManage/components/renyuanpaiban.vue'
|
import renyuanpaiban from '@/views/integratedManage/attendManage/components/renyuanpaiban.vue'
|
||||||
import renyuanguanliDialog from '@/views/integratedManage/attendManage/components/renyuanguanliDialog.vue'
|
import renyuanguanliDialog from '@/views/integratedManage/attendManage/components/renyuanguanliDialog.vue'
|
||||||
import { getPaibanRenYuanList } from '@/api/renyuan/paiban';
|
|
||||||
import { ref, onMounted } from 'vue';
|
|
||||||
|
import { getPaibanRenYuanList, getPaibanRiLiList, savePaiban, updatePaiban, deletePaiban } from '@/api/renyuan/paiban';
|
||||||
|
import { SchedulingVO } from '@/api/renyuan/paiban/types';
|
||||||
|
import { listSchedulingDate } from '@/api/renyuan/schedulingDate';
|
||||||
|
import { ref, onMounted, watch, onUnmounted } from 'vue';
|
||||||
|
import { getCurrentMonthDates } from '@/utils/getDate';
|
||||||
|
const currentMonthDates = getCurrentMonthDates();
|
||||||
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||||
// 导入用户store
|
// 导入用户store
|
||||||
import { useUserStore } from '@/store/modules/user';
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
// 初始化用户store
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
// 排班人员列表
|
// 排班人员列表
|
||||||
const paibanRenYuanList = ref([]);
|
const paibanRenYuanList = ref([]);
|
||||||
|
|
||||||
// 获取排班人员列表
|
// 排班人员数据
|
||||||
const fetchPaibanRenYuanList = async (deptId?: string) => {
|
const scheduleList = ref<SchedulingVO[]>([]);
|
||||||
try {
|
|
||||||
// 如果没有提供deptId,默认使用当前登录用户的部门ID
|
// 排班类型
|
||||||
const targetDeptId = deptId || userStore.deptId;
|
const scheduleTypes = ref([]);
|
||||||
if (!targetDeptId) {
|
// 修改弹出框的类型下拉
|
||||||
console.warn('未提供部门ID,无法获取排班人员列表');
|
const editscheduleTypes = ref([]);
|
||||||
return;
|
// 编辑排班弹窗
|
||||||
}
|
const editScheduleDialogVisible = ref(false);
|
||||||
const response = await getPaibanRenYuanList(targetDeptId);
|
|
||||||
// 格式化人员列表数据,确保每个人员对象包含label和value属性
|
|
||||||
paibanRenYuanList.value = response.data?.map((user: any) => ({
|
|
||||||
label: user.nickName || user.userName || '未知用户',
|
|
||||||
value: user.userId || user.id || ''
|
|
||||||
})) || [];
|
|
||||||
|
|
||||||
console.log('获取排班人员列表成功:', paibanRenYuanList.value);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取排班人员列表失败:', error);
|
|
||||||
// 错误情况下提供模拟数据,确保功能可用
|
|
||||||
paibanRenYuanList.value = [
|
|
||||||
{ label: '张三', value: '1' },
|
|
||||||
{ label: '李四', value: '2' },
|
|
||||||
{ label: '王五', value: '3' },
|
|
||||||
{ label: '赵六', value: '4' },
|
|
||||||
{ label: '钱七', value: '5' }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 人员排班弹窗
|
// 人员排班弹窗
|
||||||
const manageAttendDialogVisible = ref(false);
|
const manageAttendDialogVisible = ref(false);
|
||||||
|
|
||||||
// 处理考勤管理确认
|
// 获取排班人员列表
|
||||||
const handleAttendConfirm = (formData: any) => {
|
const fetchPaibanRenYuanList = async (deptId?: string) => {
|
||||||
console.log('考勤表单数据:', formData);
|
try {
|
||||||
// 这里可以添加表单提交逻辑
|
// 如果没有提供deptId,默认使用当前登录用户的部门ID
|
||||||
|
const targetDeptId = deptId || userStore.deptId;
|
||||||
|
if (!targetDeptId) {
|
||||||
|
console.warn('未提供部门ID,无法获取排班人员列表');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const response = await getPaibanRenYuanList(targetDeptId);
|
||||||
|
// console.log('获取排班人员:', response);
|
||||||
|
paibanRenYuanList.value = response.data?.map((user: any) => ({
|
||||||
|
label: user.nickName,
|
||||||
|
value: user.userId,
|
||||||
|
deptId: user.deptId,
|
||||||
|
|
||||||
|
})) || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取排班人员列表失败:', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 编辑排班弹窗
|
// 获取排班数据
|
||||||
const editScheduleDialogVisible = ref(false);
|
const getscheduleData = async (query?: SchedulingVO) => {
|
||||||
|
try {
|
||||||
|
if (userStore.selectedProject && userStore.selectedProject.id) {
|
||||||
|
const res = await getPaibanRiLiList(query);
|
||||||
|
if (res.code === 200) {
|
||||||
|
scheduleList.value = res.data || [];
|
||||||
|
} else {
|
||||||
|
proxy?.$modal.msgError(res.msg || '获取排班数据失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
proxy?.$modal.msgError('获取排班数据失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取排班类型
|
||||||
|
const getTypeList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await listSchedulingDate({ projectId: userStore.selectedProject?.id, pageNum: 1, pageSize: 10 });
|
||||||
|
if (res.code === 200) {
|
||||||
|
scheduleTypes.value = res.rows || [];
|
||||||
|
// 在scheduleTypes基础上新增休息字段
|
||||||
|
editscheduleTypes.value = [
|
||||||
|
...(res.rows || []),
|
||||||
|
{ id: 'rest', schedulingName: '休息' }
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
proxy?.$modal.msgError(res.msg || '获取排班类型失败');
|
||||||
|
// 如果获取失败,至少保留休息选项
|
||||||
|
editscheduleTypes.value = [{ id: 'rest', schedulingName: '休息' }];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取排班类型出错:', error);
|
||||||
|
proxy?.$modal.msgError('获取排班类型失败');
|
||||||
|
// 异常情况下也保留休息选项
|
||||||
|
editscheduleTypes.value = [{ id: 'rest', schedulingTypeName: '休息' }];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安排人员排班
|
||||||
|
const arrangePaiban = async (formData: any) => {
|
||||||
|
try {
|
||||||
|
// 添加projectId到表单数据中
|
||||||
|
const dataWithProjectId = {
|
||||||
|
...formData,
|
||||||
|
projectId: userStore.selectedProject?.id
|
||||||
|
};
|
||||||
|
const res = await savePaiban(dataWithProjectId);
|
||||||
|
if (res.code === 200) {
|
||||||
|
proxy?.$modal.msgSuccess('排班成功');
|
||||||
|
// 刷新排班数据
|
||||||
|
refreshScheduleData(userStore.selectedProject.id);
|
||||||
|
// 关闭弹窗
|
||||||
|
manageAttendDialogVisible.value = false;
|
||||||
|
} else {
|
||||||
|
proxy?.$modal.msgError(res.msg || '排班失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
proxy?.$modal.msgError('排班失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 修改排班
|
||||||
|
const updateSchedule = async (formData: any) => {
|
||||||
|
try {
|
||||||
|
const res = await updatePaiban(formData);
|
||||||
|
if (res.code === 200) {
|
||||||
|
proxy?.$modal.msgSuccess('修改排班成功');
|
||||||
|
// 刷新排班数据
|
||||||
|
refreshScheduleData(userStore.selectedProject.id);
|
||||||
|
// 关闭弹窗
|
||||||
|
editScheduleDialogVisible.value = false;
|
||||||
|
} else {
|
||||||
|
proxy?.$modal.msgError(res.msg || '修改排班失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
proxy?.$modal.msgError('修改排班失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除排班
|
||||||
|
const deleteSchedule = async (ID: any) => {
|
||||||
|
try {
|
||||||
|
const res = await deletePaiban(ID);
|
||||||
|
if (res.code === 200) {
|
||||||
|
proxy?.$modal.msgSuccess('删除排班成功');
|
||||||
|
// 刷新排班数据
|
||||||
|
refreshScheduleData(userStore.selectedProject.id);
|
||||||
|
// 关闭弹窗
|
||||||
|
editScheduleDialogVisible.value = false;
|
||||||
|
} else {
|
||||||
|
proxy?.$modal.msgError(res.msg || '删除排班失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
proxy?.$modal.msgError('删除排班失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理考勤管理确认
|
||||||
|
const handleAttendConfirm = (formData: any) => {
|
||||||
|
// console.log('考勤表单数据:', formData);
|
||||||
|
// 这里可以添加表单提交逻辑
|
||||||
|
arrangePaiban(formData);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 编辑排班表单数据
|
// 编辑排班表单数据
|
||||||
const editScheduleForm = ref({
|
const editScheduleForm = ref({
|
||||||
name: '',
|
opsUserId: '', // 新增opsUserId字段
|
||||||
date: '',
|
name: '',
|
||||||
currentShift: '',
|
date: '',
|
||||||
newShift: ''
|
currentShift: '',
|
||||||
|
newShift: '',
|
||||||
|
id: '' // 新增scheduledId字段,用于存储排班的id
|
||||||
});
|
});
|
||||||
|
|
||||||
// 排班类型选项
|
|
||||||
const shiftOptions = [
|
// 格式化排班文本,用于显示当前排班
|
||||||
{ label: '早班', value: '早班' },
|
const formatShiftDisplay = (shiftData: any): string => {
|
||||||
{ label: '中班', value: '中班' },
|
if (!shiftData) return '休息';
|
||||||
{ label: '晚班', value: '晚班' },
|
|
||||||
{ label: '休息', value: '休息' }
|
// 如果是字符串,直接返回
|
||||||
];
|
if (typeof shiftData === 'string') {
|
||||||
|
return shiftData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是数组,返回第一个排班类型的名称
|
||||||
|
if (Array.isArray(shiftData) && shiftData.length > 0) {
|
||||||
|
if (typeof shiftData[0] === 'object' && shiftData[0].schedulingTypeName) {
|
||||||
|
return shiftData[0].schedulingTypeName;
|
||||||
|
}
|
||||||
|
return shiftData[0].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是对象,返回排班类型名称
|
||||||
|
if (typeof shiftData === 'object' && shiftData.schedulingTypeName) {
|
||||||
|
return shiftData.schedulingTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是对象但没有schedulingTypeName属性,尝试返回其字符串表示
|
||||||
|
if (typeof shiftData === 'object') {
|
||||||
|
return JSON.stringify(shiftData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '休息';
|
||||||
|
};
|
||||||
|
|
||||||
// 处理编辑排班
|
// 处理编辑排班
|
||||||
const handleEditSchedule = (rowData: any, columnData: any) => {
|
const handleEditSchedule = (rowData: any, columnData: any) => {
|
||||||
// 设置表单数据
|
|
||||||
editScheduleForm.value = {
|
// 设置表单数据
|
||||||
name: rowData.name,
|
editScheduleForm.value = {
|
||||||
date: `${columnData.fullDate}`,
|
opsUserId: rowData.opsUserId || '', // 从opsUserId字段获取用户ID
|
||||||
currentShift: '',
|
name: rowData.name,
|
||||||
newShift: ''
|
date: `${columnData.fullDate}`,
|
||||||
};
|
currentShift: '',
|
||||||
|
newShift: '',
|
||||||
// 查找当前排班
|
id: ''
|
||||||
Object.keys(rowData).forEach(key => {
|
};
|
||||||
if (key.startsWith('day')) {
|
|
||||||
const dayIndex = parseInt(key.replace('day', '')) - 1;
|
// 查找当前排班
|
||||||
if (dayIndex === columnData.index) {
|
Object.keys(rowData).forEach(key => {
|
||||||
editScheduleForm.value.currentShift = rowData[key];
|
if (key.startsWith('day')) {
|
||||||
editScheduleForm.value.newShift = rowData[key];
|
const dayIndex = parseInt(key.replace('day', '')) - 1;
|
||||||
}
|
if (dayIndex === columnData.index) {
|
||||||
}
|
const formattedShift = formatShiftDisplay(rowData[key]);
|
||||||
});
|
editScheduleForm.value.currentShift = formattedShift;
|
||||||
|
editScheduleForm.value.newShift = formattedShift;
|
||||||
// 显示弹窗
|
|
||||||
editScheduleDialogVisible.value = true;
|
// 如果不是休息状态,则获取id并赋值到表单中
|
||||||
|
if (formattedShift !== '休息' && rowData[key]) {
|
||||||
|
// 处理rowData[key]为数组的情况
|
||||||
|
if (Array.isArray(rowData[key]) && rowData[key].length > 0 && rowData[key][0].id) {
|
||||||
|
editScheduleForm.value.id = rowData[key][0].id;
|
||||||
|
}
|
||||||
|
// 同时处理rowData[key]为对象的情况作为兼容
|
||||||
|
else if (typeof rowData[key] === 'object' && rowData[key].id) {
|
||||||
|
editScheduleForm.value.id = rowData[key].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 显示弹窗
|
||||||
|
editScheduleDialogVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 确认修改排班
|
// 确认修改排班
|
||||||
const handleConfirmEditSchedule = () => {
|
const handleConfirmEditSchedule = () => {
|
||||||
console.log('修改排班数据:', editScheduleForm.value);
|
// 按照要求的格式准备数据
|
||||||
// 这里可以添加修改排班的逻辑
|
const submitData = {
|
||||||
editScheduleDialogVisible.value = false;
|
projectId: userStore.selectedProject?.id,
|
||||||
|
opsUserId: editScheduleForm.value.opsUserId,
|
||||||
|
schedulingType: editScheduleForm.value.newShift,
|
||||||
|
schedulingDate: editScheduleForm.value.date,
|
||||||
|
id: editScheduleForm.value.id
|
||||||
|
};
|
||||||
|
console.log('提交的排班数据格式:', submitData);
|
||||||
|
if (submitData.schedulingType == 'rest') {
|
||||||
|
deleteSchedule(submitData.id);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
updateSchedule(submitData);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 出勤数据 - 用于attendTrend组件
|
// 出勤数据 - 用于attendTrend组件
|
||||||
@ -366,11 +529,42 @@ const calendarData = ref({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// 初始化用户store
|
// 封装刷新排班数据的方法
|
||||||
const userStore = useUserStore();
|
const refreshScheduleData = (projectId: string) => {
|
||||||
|
// 获取排班数据
|
||||||
|
getscheduleData({
|
||||||
|
projectId: projectId,
|
||||||
|
schedulingStartDate: currentMonthDates[0].fullDate,
|
||||||
|
schedulingEndDate: currentMonthDates[currentMonthDates.length - 1].fullDate,
|
||||||
|
});
|
||||||
|
// 获取排班类型
|
||||||
|
getTypeList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听projectId变化
|
||||||
|
const projectIdWatcher = watch(
|
||||||
|
() => userStore.selectedProject?.id,
|
||||||
|
(newProjectId, oldProjectId) => {
|
||||||
|
if (newProjectId && newProjectId !== oldProjectId) {
|
||||||
|
refreshScheduleData(newProjectId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: false, deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchPaibanRenYuanList(String(userStore.deptId));
|
// 刷新排班数据
|
||||||
|
refreshScheduleData(userStore.selectedProject.id);
|
||||||
|
|
||||||
|
// 获取可以排班的人员列表
|
||||||
|
fetchPaibanRenYuanList(String(userStore.deptId));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件卸载时移除监听器
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (projectIdWatcher) {
|
||||||
|
projectIdWatcher();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user