You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

879 lines
25 KiB

<template>
<div class="app-container">
<el-container>
<el-aside width="300px">
<el-row>
<el-col :span="24">
<el-select
v-model="groupType"
placeholder="选择班组类别"
style="height: 40px"
@change="handleGroupTypeChange"
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.BASIC_TEAM_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
>
<span style="float: left">{{ dict.label }}</span>
<span style="float: right; color: var(--el-text-color-secondary); font-size: 13px">
{{ dict.value }}
</span>
</el-option>
</el-select>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<template #default>
<el-table
:data="tableData"
ref="refTableTeams"
highlight-current-row
@current-change="handleTableCurrentChange"
@row-dblclick="handleClick('')"
>
<el-table-column type="index" width="50" />
<el-table-column prop="code" label="班组编码" />
<el-table-column prop="name" label="班组名称" />
<el-table-column label="删除排班" >
<template #default="scope">
<el-button type="text" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
small
:page-size="queryPage.pageSize"
background
layout="prev, pager, next"
:total="queryPage.pageTotal"
class="mt-4"
@current-change="handleGroupPageChange"
/>
</template>
</el-col>
</el-row>
</el-aside>
<el-main>
<div class="calender-class">
<el-calendar ref="calendarMain">
<template #header="{ date }">
<span>排班日历</span>
<span>{{ date }}</span>
<el-button-group>
<el-button size="small" @click="selectDate('prev-month')"> 上一月 </el-button>
<el-button size="small" @click="selectDate('today')">今天</el-button>
<el-button size="small" @click="selectDate('next-month')"> 下一月 </el-button>
<el-button size="small" type="primary" @click="handleClick('batch')"
>批量排班</el-button
>
</el-button-group>
</template>
<template #date-cell="{ data }">
<template v-if="viewDate[data.day.replace(/-/g, '')]">
<div class="header-class">
<div class="day-class">
{{ data.day.split('-').slice(1).join('-') }}
</div>
<div class="handle-class">
<el-button
v-if="holiday[data.day.replace(/-/g, '')]"
size="mini"
circle
style="background-color: chocolate"
><Icon icon="ep:star" />{{holiday[data.day.replace(/-/g, '')].holidayName}}
</el-button>
</div>
<div class="handle-class">
<el-button
size="mini"
circle
@click="handleWorkInfo(viewDate[data.day.replace(/-/g, '')], data)"
><Icon icon="ep:edit" />
</el-button>
</div>
</div>
<div class="paiban-class">
<div
v-for="(dayValue, i) in viewDate[data.day.replace(/-/g, '')]"
:key="i"
:class="[
'draggable-div' + i,
'each-paiban-class',
setWorkClass(setSortValue(dayValue.shiftName))
]"
draggable="true"
@dragstart="handleDragStart(dayValue, data.day)"
@dragover.prevent="handleDragOver($event)"
@dragenter="handleDragEnter($event, dayValue)"
@dragend="handleDragEnd"
>
<i> <Icon :icon="setIconClass(dayValue.shiftName)" /></i>
<div class="paiban-name-class">{{ dayValue.teamName }}</div>
</div>
</div>
</template>
<template v-else>
<div class="header-class">
<div class="day-class">
{{ data.day.split('-').slice(1).join('-') }}
</div>
<div class="handle-class">
<el-button
v-if="holiday[data.day.replace(/-/g, '')]"
size="mini"
circle
style="background-color: chocolate"
><Icon icon="ep:star" />{{holiday[data.day.replace(/-/g, '')].holidayName}}
</el-button>
</div>
<div class="handle-class">
<el-button size="mini" circle @click="handleWorkInfo(null, data)"
><Icon icon="ep:edit" />
</el-button>
</div>
</div>
<div class="no-work-class">
<div class="icon-class"><Icon icon="ep:clock" /></div>
<div class="tips-class"> 暂无排班 </div>
</div>
</template>
</template>
</el-calendar>
</div>
</el-main>
</el-container>
<!-- 批量排班抽屉弹窗 -->
<div>
<el-drawer :title="batchTitle" v-model="batchAddDrawer" size="40%">
<div class="demo-drawer_content">
<el-form v-model="batchAddForm">
<el-form-item label="排班日期:" label-width="120px">
<el-date-picker
v-model="batchAddForm.batchDate"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
>
</el-date-picker>
</el-form-item>
<el-form-item label="规则类型:" label-width="120px">
<!-- <el-button type="primary" @click="addDomain" link> <Icon icon="ep:plus" /> </el-button> -->
<el-radio-group v-model="ruleType" @change="handleRuleType">
<el-radio label="一班倒">一班倒</el-radio>
<el-radio label="两班倒">两班倒</el-radio>
<el-radio label="三班倒">三班倒</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排除非法定休息日:" label-width="120px">
<el-switch v-model="batchAddForm.excludeRestDay" />
</el-form-item>
<el-table :data="shiftTableData" ref="shiftTable" style="width: 100%">
<el-table-column label="班次" prop="shiftName" />
<el-table-column label="上班时间" prop="startTime">
<template #default="scope">
<el-time-picker
format="HH:mm"
value-format="HH:mm"
v-model="scope.row.startTime"
/>
</template>
</el-table-column>
<el-table-column label="下班时间" prop="endTime">
<template #default="scope">
<el-time-picker
format="HH:mm"
value-format="HH:mm"
v-model="scope.row.endTime"
/> </template
></el-table-column>
</el-table>
</el-form>
</div>
<div class="demo-drawer__footer">
<el-button @click="handleBatchClose">取 消</el-button>
<el-button type="primary" @click="batchAddWork"> 保存 </el-button>
</div>
</el-drawer>
</div>
<!-- 单独排班 -->
<div>
<el-drawer :title="'【' + hanleDay.day + '】排班'" v-model="drawer" size="40%">
<div class="add-work-class">
<div class="demo-drawer__content">
<el-form v-model="addForm">
<el-form-item label="规则类型:" label-width="120px">
<!-- <el-button type="primary" @click="addDomain" link> <Icon icon="ep:plus" /> </el-button> -->
<el-radio-group v-model="ruleType" @change="handleRuleType">
<el-radio label="一班倒">一班倒</el-radio>
<el-radio label="两班倒">两班倒</el-radio>
<el-radio label="三班倒">三班倒</el-radio>
</el-radio-group>
</el-form-item>
<el-table :data="shiftTableData" ref="shiftTable" style="width: 100%">
<el-table-column label="班次" prop="shiftName" />
<el-table-column label="上班时间" prop="startTime">
<template #default="scope">
<el-time-picker
format="HH:mm"
value-format="HH:mm"
v-model="scope.row.startTime"
/>
</template>
</el-table-column>
<el-table-column label="下班时间" prop="endTime">
<template #default="scope">
<el-time-picker
format="HH:mm"
value-format="HH:mm"
v-model="scope.row.endTime"
/> </template
></el-table-column>
</el-table>
</el-form>
<div class="demo-drawer__footer">
<el-button @click="handleBatchClose">取 消</el-button>
<el-button type="primary" @click="addWork"> 保存 </el-button>
</div>
</div>
</div>
</el-drawer>
</div>
</div>
</template>
<script setup lang="ts">
import { useMessage } from '@/hooks/web/useMessage'
import dayjs from 'dayjs'
import { ref, watch, nextTick } from 'vue'
import type { CalendarDateType, CalendarInstance } from 'element-plus'
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
import * as workCalendarApi from '@/api/mes/workcalendar'
const message = useMessage() // 消息弹窗
const ruleType = ref('一班倒') //排班规则
const currentDate = ref(dayjs().format('YYYY-MM-DD'))
const batchTitle = ref('批量排班')
const queryPage = ref({ pageNo: 1, pageSize: 20, pageTotal: 20, teamGroup: '' })
const tableData = ref([]) //班组列表
const groupType = ref([]) //班组类型
const shiftTableData = ref([{ shiftName: '', startTime: '08:00', endTime: '16:00' }])
//节假日数据
const holiday = ref({
'20240501': {
holidayName: '',
holidayType: '法定',
date: '2024-05-01'
},
'20240502': {
holidayName: '',
holidayType: '法定',
date: '2024-05-02'
},
'20240503': {
holidayName: '',
holidayType: '法定',
date: '2024-05-03'
},
'20240504': {
holidayName: '',
holidayType: '法定',
date: '2024-05-04'
},
'20240505': {
holidayName: '',
holidayType: '法定',
date: '2024-05-05'
}
})
//排班数据
const viewDate = ref({})
//今日数据
const thisDay = ref()
//今日班次数据
const thisDayWork = ref()
const ending = ref()
const dragging = ref()
const batchAddDrawer = ref(false)
// 批量添加
const batchAddForm = ref({
batchDate: [],
excludeRestDay:true
})
// 单日添加
const addForm = ref({
shiftName: '',
teamName: 'chongya01',
ruleCode: '1',
sort: 1
})
const drawer = ref(false)
const innerDrawer = ref(false)
const hanleDay = ref('')
const workInfoList = ref()
// 时间范围
const dateRange = ref({
start: '2023-10-1',
end: '2023-10-20'
})
//班组选择
interface Team {
code: string
name: string
class: string
}
const currentRow = ref() //当前行的值
const handleTableCurrentChange = (val: Team) => {
currentRow.value = val
}
const calendarMain = ref<CalendarInstance>()
//console.log("🚀 ~ file: App.vue:617 ~ getViewData ~ calendarMain:",calendarMain)
///处理排班和tablerow切换
const handleClick = (type) => {
if (currentRow.value) {
if (type == 'single') {
drawer.value = true
} else if (type == 'batch') {
batchTitle.value = '批量排班[' + currentRow.value.name + ']'
batchAddDrawer.value = true
}
//console.log("🚀 ~ file: App.vue:617 ~ getViewData ~ currentDate.value:",currentDate.value)
getViewData(currentRow.value.code, currentDate.value.slice(0, 8) + '01', dayjs(currentDate.value).endOf('month').format('YYYY-MM-DD'))
} else {
message.error('请选择一个班组!')
return
}
}
const handleRuleType = (value: string) => {
if (value == '三班倒') {
shiftTableData.value = [
{
shiftName: '早',
startTime: shiftTime.value['早'].startTime,
endTime: shiftTime.value['早'].endTime
},
{
shiftName: '中',
startTime: shiftTime.value['中'].startTime,
endTime: shiftTime.value['中'].endTime
},
{
shiftName: '晚',
startTime: shiftTime.value['晚'].startTime,
endTime: shiftTime.value['晚'].endTime
}
]
} else if (value == '两班倒') {
shiftTableData.value = [
{
shiftName: '早',
startTime: shiftTime.value['早'].startTime,
endTime: shiftTime.value['早'].endTime
},
{
shiftName: '中',
startTime: shiftTime.value['中'].startTime,
endTime: shiftTime.value['中'].endTime
}
]
} else {
shiftTableData.value = [
{
shiftName: '早',
startTime: shiftTime.value['早'].startTime,
endTime: shiftTime.value['早'].endTime
}
]
}
}
watch(
() => addForm.value.shiftName,
(newVal) => {
switch (newVal) {
case '早':
addForm.value.sort = 1
break
case '中':
addForm.value.sort = 2
break
case '晚':
addForm.value.sort = 3
break
default:
break
}
}
)
onMounted(
async function () {
let res=await workCalendarApi.getHolidays(dayjs(currentDate.value).year())
holiday.value=res
//console.log('🚀 ~ file: App.vue:571 ~ onMounted ~ getHolidays:', res)
}
)
const handleDragStart = (item, data) => {
dragging.value = item
thisDay.value = data
let dkey = dayjs(thisDay.value.date).format('YYYYMMDD')
thisDayWork.value = viewDate.value[dkey]
}
const handleDragEnd = () => {
if (ending.value.id === dragging.value.id) {
return
}
let newItems = [...thisDayWork.value]
const src = newItems.indexOf(dragging.value)
const dst = newItems.indexOf(ending.value)
newItems.splice(src, 1, ...newItems.splice(dst, 1, newItems[src]))
nextTick(() => {
dragging.value = null
ending.value = null
})
}
const handleDragOver = (e) => {
// 首先把div变成可以放置的元素,即重写dragenter/dragover
e.dataTransfer.dropEffect = 'move' // e.dataTransfer.dropEffect="move";//在dragenter中针对放置目标来设置!
}
const handleDragEnter = (e, item) => {
e.dataTransfer.effectAllowed = 'move' // 为需要移动的元素设置dragstart事件
ending.value = item
}
// 获取时间范围中的所有日期
const enumerateDaysBetweenDates = (startDate, endDate) => {
let daysList = []
let SDate = dayjs(startDate)
let EDate = dayjs(endDate)
while (SDate.isBefore(EDate)) {
daysList.push(SDate.format('YYYY-MM-DD'))
SDate = SDate.add(1, 'day')
}
daysList.push(EDate.format('YYYY-MM-DD'))
return daysList
}
const setSortValue = (value) => {
let sort = 1
switch (value) {
case '早':
sort = 1
break
case '中':
sort = 2
break
case '晚':
sort = 3
break
default:
break
}
return sort
}
const setWorkClass = (value) => {
let classValue = 'no-work-class'
switch (value) {
case 1:
classValue = 'zao-work-class'
break
case 2:
classValue = 'wan-work-class'
break
case 3:
classValue = 'ye-work-class'
break
default:
break
}
return classValue
}
const setIconClass = (value) => {
let classValue = 'ep:sunrise'
switch (value) {
case '早':
classValue = 'ep:sunrise'
break
case '中':
classValue = 'ep:sunny'
break
case '晚':
classValue = 'ep:moon'
break
default:
break
}
return classValue
}
// 编辑单日排班
const handleWorkInfo = (info, data) => {
if (currentRow.value) {
hanleDay.value = data
drawer.value = true
if (info != undefined && info.length > 0) {
workInfoList.value = info
} else {
workInfoList.value = []
}
} else {
message.error('请选择一个班组!')
return
}
}
const handleClose = () => {
innerDrawer.value = false
}
// //班次时间
const shiftTime = ref({
: { startTime: '8:00', endTime: '16:00' },
: { startTime: '16:00', endTime: '24:00' },
: { startTime: '00:00', endTime: '8:00' }
})
// 添加单日排班
const addWork = () => {
let saveList = []
let shiftData = shiftTableData.value //班次
let keyDate = dayjs(hanleDay.value.day).format('YYYYMMDD')
shiftData.forEach((shift) => {
let info = {
keyDate: keyDate,
//班组编码
teamCode: currentRow.value.code,
//班组名称
teamName: currentRow.value.name,
//班组类别"
teamType: queryPage.value.teamGroup,
//班次名称"
shiftName: shift.shiftName,
//班次编码
shiftCode: shift.shiftName,
//上班时间
startTime: shift.startTime,
//下班时间"
endTime: shift.endTime,
//工作日期
//workDate:work,
workDateString: hanleDay.value.day,
//倒班规则
shiftRule: ruleType.value,
//倒班类型
shiftRate: '月',
sort: setSortValue(shift.shiftName)
}
saveList.push(info)
})
//console.log('🚀 ~ file: App.vue:571 ~ addWork ~ saveList:', saveList)
savePlan(saveList)
getViewData(currentRow.value.code, hanleDay.value.day, hanleDay.value.day)
drawer.value = false
}
// 清除单日排班数据
// const deleteRow = (row, tableData) => {
// let index = row.$index
// tableData.splice(index, 1)
// if (tableData.length > 0) {
// //tableData.$set(viewDate.value, hanleDay.value, tableData.value);
// } else {
// //tableData.$delete(viewDate.value, hanleDay.value);
// }
// }
// 批量添加排班数据
const batchAddWork = () => {
let dateList = batchAddForm.value.batchDate //时间范围
//let classList = batchAddForm.value.classData
let shiftData = shiftTableData.value //班次
//console.log('dateList', dateList)
//console.log('classList', classList)
let list = []
//生成时间段
if (dateList && dateList.length > 0) {
list = enumerateDaysBetweenDates(dateList[0], dateList[1])
}
//当前每天生成一个排班计划, 排班规则 班次计划
let saveList = []
let flag=false//过滤项目
list.forEach((item) => {
let keyDate = dayjs(item).format('YYYYMMDD')
if(batchAddForm.value.excludeRestDay){
if(dayjs(item).day()==0||dayjs(item).day()==6){
flag=true
}else{
flag=false
}
}else{
flag=false
}
if (!holiday.value[keyDate] && !flag) {
//let workList=[]
shiftData.forEach((shift) => {
let info = {
keyDate: keyDate,
//班组编码
teamCode: currentRow.value.code,
//班组名称
teamName: currentRow.value.name,
//班组类别"
teamType: queryPage.value.teamGroup,
//班次名称"
shiftName: shift.shiftName,
//班次编码
shiftCode: shift.shiftName,
//上班时间
startTime: shift.startTime,
//下班时间"
endTime: shift.endTime,
//工作日期SchedulingcalendarCreateReqVO(super=SchedulingcalendarBaseVO(keyDate=20240506, teamCode=T01, teamName=测试添加, teamType=1, shiftName=早, shiftCode=早, startTime=08:00, endTime=16:00, status=null, workDate=null, shiftRule=一班倒, shiftRate=月, workDateStr=null))
//workDate:work,
workDateString: item,
//倒班规则
shiftRule: ruleType.value,
//倒班类型
shiftRate: '月',
sort: setSortValue(shift.shiftName)
}
//workList.push(info)
saveList.push(info)
})
}
})
savePlan(saveList)
getViewData(currentRow.value.code, dateList[0], dateList[1])
batchAddDrawer.value = false
batchAddForm.value = {
batchDate: [],
excludeRestDay:true
}
}
const handleBatchClose = () => {
batchAddDrawer.value = false
}
const savePlan = async (workList) => {
await workCalendarApi.createBatch(workList)
//await workCalendarApi.createObj(workList);
}
const getViewData = async (teamCode: any, startDate: any, endDate: any) => {
let params = {
code: teamCode,
startTime: startDate,
endTime: endDate
}
let res = await workCalendarApi.getWorkPlan(params)
//if(res)
viewDate.value = res
//workInfoList.value =viewDate.value
//console.log('🚀 ~ file: App.vue:672 ~ getViewData ~ getViewData:', viewDate.value)
}
// 设置禁用值
const setDisabled = (date) => {
// console.log("🚀 ~ file: App.vue:537 ~ setDisabled ~ date:", date)
if (dayjs(date).isBefore(dateRange.value[0]) || dayjs(date).isAfter(dateRange.value[1])) {
return 'disabled-date-class'
}
}
const selectDate = (val: CalendarDateType) => {
if (!calendarMain.value) return
if (val === 'prev-month') {
currentDate.value = dayjs(currentDate.value).subtract(1, 'month').format('YYYY-MM-DD')
//console.log('🚀 ~ file: App.vue:644 ~ getViewData ~ datedd:', currentDate.value)
}
if (val === 'next-month') {
currentDate.value = dayjs(currentDate.value).add(1, 'month').format('YYYY-MM-DD')
//console.log('🚀 ~ file: App.vue:644 ~ getViewData ~ datedd:', currentDate.value)
}
if (val === 'today') {
currentDate.value = dayjs(new Date()).format('YYYY-MM-DD')
//console.log('🚀 ~ file: App.vue:644 ~ getViewData ~ datedd:', currentDate.value)
}
calendarMain.value.selectDate(val)
}
const handleGroupTypeChange = (val: any) => {
queryPage.value.teamGroup = val
getTeamList()
}
const handleGroupPageChange = (val: number) => {
;(queryPage.value.pageNo = val), getTeamList()
}
const getTeamList = async () => {
let query = {
pageNo: queryPage.value.pageNo,
pageSize: queryPage.value.pageSize,
teamGroup: queryPage.value.teamGroup
}
let res = await workCalendarApi.getTeamList(query)
queryPage.value.pageTotal = res.total
tableData.value = res.list
}
//删除班组排班
const handleDelete=(row:any)=>{
message.delConfirm('确认要删除['+row.name+']班组当月排班数据吗?', 'Warning').then(() => {
//console.log('🚀 ~ file: App.vue:720 ~ handleDelete ~ :', dayjs(currentDate.value).endOf('month').format('YYYY-MM-DD'))
deleteWorkPlan(row.code,currentDate.value.slice(0, 8) + '01', dayjs(currentDate.value).endOf('month').format('YYYY-MM-DD'))
})
// .catch(() => {
// })
}
const deleteWorkPlan=async (code:any,startTime:any,endTime:any)=>{
let data={
code:code,
startTime:startTime,
endTime: endTime
}
console.log('🚀 ~ file: App.vue:720 ~ deleteWorkPlan ~ :', data)
workCalendarApi.deleteWorkPlan(data)
getViewData(code, startTime,endTime)
}
</script>
<style>
#app-container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.el-table__fixed-right {
height: 100% !important;
}
.calender-class {
width: 100%;
height: 100%;
}
.is-selected {
color: #1989fa;
}
.el-calendar__body {
height: 85vh;
}
.el-calendar-table {
height: 100%;
}
.el-calendar-day {
height: 100% !important;
}
.day-content-class {
height: 100px;
display: flex;
flex-direction: column;
}
.header-class {
flex: 1;
display: flex;
height: 28px;
flex-direction: row;
justify-content: space-between;
}
.day-class {
flex: 4;
}
.handle-class {
flex: 1;
}
.holiday-class {
flex: 1;
flex-direction: column;
left: 1px;
}
.paiban-class {
flex: 4;
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-end;
}
.paiban-icon-class {
font-size: 22px;
margin: 8px 0 10px 0;
}
.paiban-name-class {
padding-top: 10px;
}
.each-paiban-class {
text-align: center;
max-width: 50px;
margin: 5px 5px 0 5px;
border-radius: 5px;
padding: 0 0 5px 0;
flex: 1;
}
.zao-work-class {
background-color: #d9ffd9;
font-size: small;
color: #11be11;
}
.wan-work-class {
background-color: #fff0bd;
font-size: small;
color: #fccb2c;
}
.ye-work-class {
background-color: #ddeffb;
font-size: small;
color: #2dabff;
}
.no-work-class {
text-align: center;
font-size: small;
color: #cacaca;
}
.icon-class {
font-size: 20px;
margin-bottom: 20px;
}
/* 侧边弹窗 */
.add-btn-class {
margin: 10px;
float: right;
}
.change-date-drawer-class .el-calendar__body {
height: 45%;
}
.change-date-drawer-class .day-content-class {
height: 30px;
}
.disabled-date-class {
color: #ccc;
pointer-events: none;
}
</style>