diff --git a/klp-ui/src/api/wms/attendanceSchedule.js b/klp-ui/src/api/wms/attendanceSchedule.js index 6e07237f..29093434 100644 --- a/klp-ui/src/api/wms/attendanceSchedule.js +++ b/klp-ui/src/api/wms/attendanceSchedule.js @@ -69,3 +69,12 @@ export function batchUpdateSchedule(data) { data: data }) } + +// 批量修改排班班次(按主键) +export function batchUpdateShiftByIds(data) { + return request({ + url: '/wms/attendanceSchedule/batchUpdateShift', + method: 'put', + data: data + }) +} diff --git a/klp-ui/src/views/wms/hrm/attendance/drag.vue b/klp-ui/src/views/wms/hrm/attendance/drag.vue index 71ee3af0..7a4a9014 100644 --- a/klp-ui/src/views/wms/hrm/attendance/drag.vue +++ b/klp-ui/src/views/wms/hrm/attendance/drag.vue @@ -1,21 +1,46 @@ @@ -273,7 +342,7 @@ import TimeRangePicker from '@/views/wms/report/components/timeRangePicker.vue' import EmployeeSelector from '@/components/EmployeeSelector/index.vue' import AttendanceTemplateManager from '@/components/AttendanceTemplateManager/index.vue' -import { listAttendanceSchedule, generateenerateSchedule, updateAttendanceSchedule, addAttendanceSchedule, delAttendanceSchedule, batchUpdateSchedule } from '@/api/wms/attendanceSchedule' +import { listAttendanceSchedule, generateenerateSchedule, updateAttendanceSchedule, addAttendanceSchedule, delAttendanceSchedule, batchUpdateSchedule, batchUpdateShiftByIds } from '@/api/wms/attendanceSchedule' import { listShift } from '@/api/wms/attendanceShift' import { listAttendanceShiftRule } from '@/api/wms/attendanceShiftRule' import { listEmployeeInfo } from '@/api/wms/employeeInfo' @@ -292,6 +361,10 @@ export default { scheduleData: [], shiftList: [], shiftRuleList: [], + queryParams: { + employeeDept: '', + employeeName: '' + }, dialogVisible: false, batchDialogVisible: false, batchForm: { @@ -319,6 +392,10 @@ export default { }, currentEditRow: null, currentEditDate: '', + batchCellSelectMode: false, + batchCellSelection: {}, + batchCellDialogVisible: false, + batchCellEditShiftId: '', rules: { dateRange: [ { required: true, message: '请选择时间段', trigger: 'change' } @@ -395,6 +472,44 @@ export default { key: emp.id, label: emp.name })) + }, + deptList() { + const depts = new Set() + this.employeeList.forEach(emp => { + if (emp.dept) depts.add(emp.dept) + }) + return Array.from(depts).sort() + }, + employeeNameList() { + return this.employeeList.map(emp => ({ + value: emp.name, + dept: emp.dept || '' + })) + }, + batchSelectedCount() { + return Object.keys(this.batchCellSelection).length + }, + batchEditTableData() { + const result = [] + Object.keys(this.batchCellSelection).forEach(key => { + const cell = this.batchCellSelection[key] + if (cell) { + result.push({ + key, + employeeName: cell.employeeName, + workDate: cell.workDate, + scheduleId: cell.scheduleId, + oldShiftName: cell.shiftName, + oldShiftType: cell.shiftType + }) + } + }) + return result + }, + batchCellEditShiftShiftType() { + if (!this.batchCellEditShiftId) return '' + const shift = this.shiftList.find(s => s.shiftId === this.batchCellEditShiftId) + return shift ? shift.shiftType : '' } }, created() { @@ -472,7 +587,10 @@ export default { // 获取排班列表 getScheduleList() { this.loading = true - listAttendanceSchedule(this.dateRangeParams).then(response => { + const params = { ...this.dateRangeParams } + if (this.queryParams.employeeDept) params.employeeDept = this.queryParams.employeeDept + if (this.queryParams.employeeName) params.employeeName = this.queryParams.employeeName + listAttendanceSchedule(params).then(response => { this.scheduleData = this.transformScheduleData(response.rows || []) this.loading = false }).catch(() => { @@ -544,6 +662,96 @@ export default { return shiftType === '夜班' ? 'night-shift' : 'day-shift' }, + // ========== 批量单元格编辑 ========== + + cellKey(row, date) { + return `${row.employeeId}_${date}` + }, + + cellClass(row, date) { + const hasData = !!row[date] + const selected = this.batchCellSelectMode && this.isCellSelected(row, date) + return { + 'has-data': hasData, + 'empty-cell': !hasData, + 'batch-mode': this.batchCellSelectMode, + 'selected-cell': selected + } + }, + + isCellSelected(row, date) { + return !!this.batchCellSelection[this.cellKey(row, date)] + }, + + handleCellClick(row, date) { + if (!this.batchCellSelectMode) return + if (!row[date]) return + const key = this.cellKey(row, date) + if (this.batchCellSelection[key]) { + this.$delete(this.batchCellSelection, key) + } else { + this.$set(this.batchCellSelection, key, { + scheduleId: row[date].scheduleId, + employeeName: row.employeeName, + workDate: date, + shiftName: row[date].shiftName, + shiftType: row[date].shiftType + }) + } + }, + + enterBatchCellMode() { + this.batchCellSelectMode = true + this.batchCellSelection = {} + }, + + cancelBatchCellSelection() { + this.batchCellSelectMode = false + this.batchCellSelection = {} + }, + + confirmBatchCellSelection() { + if (this.batchSelectedCount === 0) { + this.$message.warning('请至少选择一个排班单元格') + return + } + this.batchCellSelectMode = false + this.batchCellEditShiftId = '' + this.batchCellDialogVisible = true + }, + + getNewShiftName() { + if (!this.batchCellEditShiftId) return '' + const shift = this.shiftList.find(s => s.shiftId === this.batchCellEditShiftId) + return shift ? shift.shiftName : '' + }, + + submitBatchCellEdit() { + if (!this.batchCellEditShiftId) { + this.$message.warning('请选择新班次') + return + } + const shiftId = this.batchCellEditShiftId + const list = this.batchEditTableData.map(item => ({ + scheduleId: item.scheduleId, + shiftId: shiftId + })) + + this.buttonLoading = true + batchUpdateShiftByIds(list).then(() => { + this.$message.success(`已批量修改 ${list.length} 条排班`) + this.buttonLoading = false + this.batchCellDialogVisible = false + this.batchCellEditShiftId = '' + this.batchCellSelection = {} + this.getScheduleList() + }).catch(() => { + this.$message.error('批量修改失败,正在重新获取数据') + this.buttonLoading = false + this.getScheduleList() + }) + }, + // 双击单元格处理 handleCellDoubleClick(row, date) { this.currentEditRow = row @@ -1014,13 +1222,29 @@ export default { }) this.employeeList = filteredList.map(item => ({ id: item.infoId, - name: item.name + name: item.name, + dept: item.dept || '' })) }).catch(() => { this.employeeList = [] }) }, + querySearchAsync(queryString, cb) { + if (!queryString) { + cb([]) + return + } + const results = this.employeeNameList.filter(item => { + return item.value.toLowerCase().includes(queryString.toLowerCase()) + }) + cb(results) + }, + + handleFilterChange() { + this.getScheduleList() + }, + // 加载模板列表 loadTemplates() { try { @@ -1114,12 +1338,30 @@ export default { padding: 20px; } -.date-range-section { +.tool-bar { + display: flex; + align-items: center; + justify-content: space-between; margin-bottom: 20px; + padding: 12px 16px; + background: #fafafa; + border-radius: 4px; + flex-wrap: wrap; + gap: 10px; } -.operation-bar { - margin-bottom: 20px; +.tool-bar-left { + display: flex; + align-items: center; + gap: 12px; + flex-wrap: wrap; +} + +.tool-bar-right { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; } .schedule-table-wrapper { @@ -1152,6 +1394,72 @@ export default { color: #c0c4cc; } +/* 批量选择模式 */ +.batch-select-bar { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; + padding: 10px 16px; + background: #ecf5ff; + border: 1px solid #b3d8ff; + border-radius: 4px; +} + +.batch-select-info { + font-size: 14px; + color: #409eff; +} + +.batch-select-actions { + display: flex; + gap: 8px; +} + +.schedule-cell.batch-mode { + position: relative; + border: 1px dashed #c0c4cc; + cursor: pointer; +} + +.schedule-cell.batch-mode.empty-cell { + cursor: not-allowed; + opacity: 0.5; +} + +.schedule-cell.selected-cell { + background-color: #ecf5ff !important; + border-color: #409eff; + border-style: solid; +} + +.cell-checkbox { + position: absolute; + top: 2px; + right: 2px; + display: flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + border-radius: 50%; +} + +.schedule-cell.selected-cell .cell-checkbox { + background-color: #409eff; +} + +/* 批量单元格编辑弹窗 */ +.batch-cell-summary { + margin-bottom: 16px; + font-size: 14px; + color: #606266; +} + +.batch-cell-form { + margin-bottom: 16px; +} + .shift-info { text-align: center; }