feat(oa): 添加项目进度步骤批量延期功能
- 在服务层接口添加批量延期方法定义 - 实现批量延期业务逻辑,支持按天或小时延期 - 添加数据库批量延期SQL映射 - 控制器增加批量延期API端点 - 前端组件添加批量延期按钮和对话框 - 集成前端批量延期API调用逻辑 - 添加批量延期数据传输对象定义
This commit is contained in:
@@ -7,6 +7,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import com.ruoyi.common.core.AjaxResult;
|
import com.ruoyi.common.core.AjaxResult;
|
||||||
import com.ruoyi.oa.domain.bo.BatchBo;
|
import com.ruoyi.oa.domain.bo.BatchBo;
|
||||||
|
import com.ruoyi.oa.domain.bo.BatchDelayBo;
|
||||||
import com.ruoyi.oa.domain.dto.PersonalReportDTO;
|
import com.ruoyi.oa.domain.dto.PersonalReportDTO;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
@@ -117,6 +118,15 @@ public class OaProjectScheduleStepController extends BaseController {
|
|||||||
return toAjax(iOaProjectScheduleStepService.changeBatch(batchBo));
|
return toAjax(iOaProjectScheduleStepService.changeBatch(batchBo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量延期步骤结束时间
|
||||||
|
*/
|
||||||
|
@RepeatSubmit()
|
||||||
|
@PutMapping("/batch-delay")
|
||||||
|
public R<Void> batchDelay(@Validated(EditGroup.class) @RequestBody BatchDelayBo batchDelayBo) {
|
||||||
|
return toAjax(iOaProjectScheduleStepService.batchDelay(batchDelayBo));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除项目进度步骤跟踪
|
* 删除项目进度步骤跟踪
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.ruoyi.oa.domain.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BatchDelayBo {
|
||||||
|
|
||||||
|
private List<Long> trackIds;
|
||||||
|
|
||||||
|
private Integer delayValue;
|
||||||
|
|
||||||
|
private String delayUnit;
|
||||||
|
}
|
||||||
@@ -55,4 +55,12 @@ public interface OaProjectScheduleStepMapper extends BaseMapperPlus<OaProjectSch
|
|||||||
* 根据 schedule_id 批量更新节点负责人(仅更新 node_header 不为空的记录)
|
* 根据 schedule_id 批量更新节点负责人(仅更新 node_header 不为空的记录)
|
||||||
*/
|
*/
|
||||||
int updateNodeHeaderByScheduleId(@Param("scheduleId") Long scheduleId, @Param("nodeHeader") String nodeHeader);
|
int updateNodeHeaderByScheduleId(@Param("scheduleId") Long scheduleId, @Param("nodeHeader") String nodeHeader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量延期:根据 trackId 列表更新 plan_end 时间
|
||||||
|
* @param trackIds trackId 列表
|
||||||
|
* @param delayMinutes 延期分钟数
|
||||||
|
* @return 更新记录数
|
||||||
|
*/
|
||||||
|
int batchDelayPlanEnd(@Param("trackIds") List<Long> trackIds, @Param("delayMinutes") Long delayMinutes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,4 +82,11 @@ public interface IOaProjectScheduleStepService{
|
|||||||
PersonalReportDTO personalReport(Long poolId, String nickName);
|
PersonalReportDTO personalReport(Long poolId, String nickName);
|
||||||
|
|
||||||
TableDataInfo<OaProjectScheduleStepVo> queryPageListMyPage(OaProjectScheduleStepBo bo, PageQuery pageQuery);
|
TableDataInfo<OaProjectScheduleStepVo> queryPageListMyPage(OaProjectScheduleStepBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量延期步骤结束时间
|
||||||
|
* @param bo 批量延期参数
|
||||||
|
* @return 是否成功
|
||||||
|
*/
|
||||||
|
Boolean batchDelay(com.ruoyi.oa.domain.bo.BatchDelayBo bo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.ruoyi.oa.domain.*;
|
import com.ruoyi.oa.domain.*;
|
||||||
import com.ruoyi.oa.domain.bo.BatchBo;
|
import com.ruoyi.oa.domain.bo.BatchBo;
|
||||||
|
import com.ruoyi.oa.domain.bo.BatchDelayBo;
|
||||||
import com.ruoyi.oa.domain.dto.NodeDTO;
|
import com.ruoyi.oa.domain.dto.NodeDTO;
|
||||||
import com.ruoyi.oa.domain.dto.PersonalReportDTO;
|
import com.ruoyi.oa.domain.dto.PersonalReportDTO;
|
||||||
import com.ruoyi.oa.domain.vo.OaExpressVo;
|
import com.ruoyi.oa.domain.vo.OaExpressVo;
|
||||||
@@ -1035,5 +1036,40 @@ public class OaProjectScheduleStepServiceImpl implements IOaProjectScheduleStepS
|
|||||||
// return stats;
|
// return stats;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Boolean batchDelay(BatchDelayBo bo) {
|
||||||
|
if (bo.getTrackIds() == null || bo.getTrackIds().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延期前检查:查询所有待延期的步骤,判断是否已完成
|
||||||
|
List<OaProjectScheduleStep> steps = baseMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<OaProjectScheduleStep>()
|
||||||
|
.in(OaProjectScheduleStep::getTrackId, bo.getTrackIds())
|
||||||
|
);
|
||||||
|
|
||||||
|
// 检查是否有已完成的步骤(status=2)
|
||||||
|
String completedTrack = steps.stream()
|
||||||
|
.filter(step -> step.getStatus() != null && step.getStatus() == 2)
|
||||||
|
.map(OaProjectScheduleStep::getSecondLevelNode)
|
||||||
|
.collect(Collectors.joining(","));
|
||||||
|
|
||||||
|
if (!completedTrack.isEmpty()) {
|
||||||
|
throw new RuntimeException("以下步骤已完成,不允许延期:" + completedTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
Long delayMinutes;
|
||||||
|
if ("day".equals(bo.getDelayUnit())) {
|
||||||
|
delayMinutes = bo.getDelayValue() * 24L * 60L;
|
||||||
|
} else if ("hour".equals(bo.getDelayUnit())) {
|
||||||
|
delayMinutes = bo.getDelayValue() * 60L;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int updated = baseMapper.batchDelayPlanEnd(bo.getTrackIds(), delayMinutes);
|
||||||
|
return updated > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -306,8 +306,19 @@
|
|||||||
WHERE schedule_id = #{scheduleId}
|
WHERE schedule_id = #{scheduleId}
|
||||||
AND del_flag = '0'
|
AND del_flag = '0'
|
||||||
AND use_flag = '1'
|
AND use_flag = '1'
|
||||||
AND node_header IS NOT NULL
|
AND (node_header IS NULL OR node_header = '')
|
||||||
AND node_header != ''
|
</update>
|
||||||
|
|
||||||
|
<update id="batchDelayPlanEnd">
|
||||||
|
UPDATE oa_project_schedule_step
|
||||||
|
SET plan_end = DATE_ADD(plan_end, INTERVAL #{delayMinutes} MINUTE)
|
||||||
|
WHERE track_id IN
|
||||||
|
<foreach collection="trackIds" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
AND del_flag = '0'
|
||||||
|
AND use_flag = '1'
|
||||||
|
AND status IN (0, 1)
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -67,3 +67,12 @@ export function listMyPage (query) {
|
|||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量延期步骤结束时间
|
||||||
|
export function batchDelayStep (data) {
|
||||||
|
return request({
|
||||||
|
url: '/oa/projectScheduleStep/batch-delay',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,15 @@
|
|||||||
<el-col>
|
<el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="addInnerData">新增</el-button>
|
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="addInnerData">新增</el-button>
|
||||||
|
<el-button type="warning" plain icon="el-icon-time" size="mini" @click="handleBatchDelay" :disabled="selectedRows.length === 0">批量延期</el-button>
|
||||||
<slot name="extra-buttons"></slot>
|
<slot name="extra-buttons"></slot>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<vxe-table size="mini" height="500" ref="tableRef" border show-overflow :edit-config="editConfig" :data="innerData"
|
<vxe-table size="mini" height="500" ref="tableRef" border show-overflow :edit-config="editConfig" :data="innerData"
|
||||||
:row-config="{ 'isCurrent': true }" :column-config="{ 'isCurrent': true }" :sort-config="sortConfig">
|
:row-config="{ 'isCurrent': true, 'isHover': true }" :column-config="{ 'isCurrent': true }" :sort-config="sortConfig"
|
||||||
|
@checkbox-change="handleCheckboxChange" @checkbox-all="handleCheckboxAll">
|
||||||
|
<vxe-column type="checkbox" width="50"></vxe-column>
|
||||||
<vxe-column field="sortNum" title="顺序" :edit-render="{ name: 'input' }" width="70" sortable></vxe-column>
|
<vxe-column field="sortNum" title="顺序" :edit-render="{ name: 'input' }" width="70" sortable></vxe-column>
|
||||||
<vxe-column field="secondLevelNode" title="步骤名称" :edit-render="{ name: 'input' }"></vxe-column>
|
<vxe-column field="secondLevelNode" title="步骤名称" :edit-render="{ name: 'input' }"></vxe-column>
|
||||||
<vxe-column field="tabNode" title="进度类别" :edit-render="{ name: 'input' }"></vxe-column>
|
<vxe-column field="tabNode" title="进度类别" :edit-render="{ name: 'input' }"></vxe-column>
|
||||||
@@ -166,6 +169,25 @@
|
|||||||
<el-button type="primary" @click="submitApplyDelay">确认申请</el-button>
|
<el-button type="primary" @click="submitApplyDelay">确认申请</el-button>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 批量延期对话框 -->
|
||||||
|
<el-dialog :visible.sync="dialogBatchDelayVisible" title="批量延期" append-to-body width="400px">
|
||||||
|
<el-form :model="dialogBatchDelayForm" label-width="100px">
|
||||||
|
<el-form-item label="延期数值">
|
||||||
|
<el-input-number v-model="dialogBatchDelayForm.delayValue" :min="1" :max="365" style="width: 100%;"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="延期单位">
|
||||||
|
<el-radio-group v-model="dialogBatchDelayForm.delayUnit">
|
||||||
|
<el-radio label="day">天</el-radio>
|
||||||
|
<el-radio label="hour">小时</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button @click="dialogBatchDelayVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitBatchDelay">确认延期</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog :visible.sync="addDialogVisible" title="新增进度" append-to-body>
|
<el-dialog :visible.sync="addDialogVisible" title="新增进度" append-to-body>
|
||||||
<el-form :model="dialogAddForm" ref="formRef" label-width="120px">
|
<el-form :model="dialogAddForm" ref="formRef" label-width="120px">
|
||||||
<el-form-item label="进度类别" prop="tabNode">
|
<el-form-item label="进度类别" prop="tabNode">
|
||||||
@@ -229,7 +251,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { addFileOperationRecord } from '@/api/oa/fileOperationRecord';
|
import { addFileOperationRecord } from '@/api/oa/fileOperationRecord';
|
||||||
import { applyProjectScheduleDelay } from "@/api/oa/projectScheduleDelay";
|
import { applyProjectScheduleDelay } from "@/api/oa/projectScheduleDelay";
|
||||||
import { updateProjectScheduleStep } from "@/api/oa/projectScheduleStep";
|
import { updateProjectScheduleStep, batchDelayStep } from "@/api/oa/projectScheduleStep";
|
||||||
import { listSupplier } from "@/api/oa/supplier";
|
import { listSupplier } from "@/api/oa/supplier";
|
||||||
import { listUser } from "@/api/system/user";
|
import { listUser } from "@/api/system/user";
|
||||||
|
|
||||||
@@ -280,6 +302,13 @@ export default {
|
|||||||
delayTo: '',
|
delayTo: '',
|
||||||
applyReason: '',
|
applyReason: '',
|
||||||
},
|
},
|
||||||
|
// 批量延期对话框控制
|
||||||
|
dialogBatchDelayVisible: false,
|
||||||
|
dialogBatchDelayForm: {
|
||||||
|
delayValue: 1,
|
||||||
|
delayUnit: 'day',
|
||||||
|
},
|
||||||
|
selectedRows: [],
|
||||||
dialogMoreVisible: false,
|
dialogMoreVisible: false,
|
||||||
// 新增对话框控制
|
// 新增对话框控制
|
||||||
addDialogVisible: false,
|
addDialogVisible: false,
|
||||||
@@ -699,6 +728,44 @@ export default {
|
|||||||
handleDelete (row) {
|
handleDelete (row) {
|
||||||
this.$emit('delete', row.trackId);
|
this.$emit('delete', row.trackId);
|
||||||
},
|
},
|
||||||
|
handleCheckboxChange ({ records }) {
|
||||||
|
this.selectedRows = records;
|
||||||
|
},
|
||||||
|
handleCheckboxAll ({ records }) {
|
||||||
|
this.selectedRows = records;
|
||||||
|
},
|
||||||
|
handleBatchDelay () {
|
||||||
|
if (this.selectedRows.length === 0) {
|
||||||
|
this.$modal.msgWarning("请先选择要延期的步骤");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.dialogBatchDelayForm = {
|
||||||
|
delayValue: 1,
|
||||||
|
delayUnit: 'day',
|
||||||
|
};
|
||||||
|
this.dialogBatchDelayVisible = true;
|
||||||
|
},
|
||||||
|
submitBatchDelay () {
|
||||||
|
const trackIds = this.selectedRows.map(row => row.trackId).filter(id => id);
|
||||||
|
if (trackIds.length === 0) {
|
||||||
|
this.$modal.msgWarning("请选择有效的步骤");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.buttonLoading = true;
|
||||||
|
batchDelayStep({
|
||||||
|
trackIds: trackIds,
|
||||||
|
delayValue: this.dialogBatchDelayForm.delayValue,
|
||||||
|
delayUnit: this.dialogBatchDelayForm.delayUnit,
|
||||||
|
}).then(res => {
|
||||||
|
this.$modal.msgSuccess("批量延期成功");
|
||||||
|
this.dialogBatchDelayVisible = false;
|
||||||
|
this.$emit("refresh", this.innerData);
|
||||||
|
}).catch(() => {
|
||||||
|
this.$modal.msgError("批量延期失败");
|
||||||
|
}).finally(() => {
|
||||||
|
this.buttonLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user