diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfCpReserveController.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfCpReserveController.java index 4a1cce0c..1f24dd21 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfCpReserveController.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfCpReserveController.java @@ -44,11 +44,25 @@ public class AppEnfCpReserveController extends EfcodeCpReserveController { } /** - * 同意预约 + * 同意预约(系统匹配专用分支) + * + * @param id 预约记录ID + * @param opSetDate 第一个操作用户选择的日期(opOrder==0 时必传) + * @param opSetTime 第一个操作用户选择的时段(opOrder==0 时必传) + */ + @PutMapping("/approveBySystem/{id}") + public R approveBySystem(@PathVariable String id, + @RequestParam(required = false) String opSetDate, + @RequestParam(required = false) Long opSetTime) { + return cpReserveService.approveReserveBySystem(id, opSetDate, opSetTime); + } + + /** + * 同意预约(手动匹配专用分支) */ - @PutMapping("/approve/{id}") - public R approve(@PathVariable String id) { - return cpReserveService.approveReserve(id); + @PutMapping("/approveByManual/{id}") + public R approveByManual(@PathVariable String id) { + return cpReserveService.approveReserveByManual(id); } /** diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpReserveService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpReserveService.java index bd9f80b2..756c5fb5 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpReserveService.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpReserveService.java @@ -89,9 +89,19 @@ public interface IEfcodeCpReserveService { * 同意预约 * * @param reserveId 预约记录ID + * @param opSetDate 第一个操作用户选择的日期(opOrder==0 时必传,格式 yyyy-MM-dd) + * @param opSetTime 第一个操作用户选择的时段(opOrder==0 时必传) * @return 操作结果 */ - R approveReserve(String reserveId); + R approveReserveBySystem(String reserveId, String opSetDate, Long opSetTime); + + /** + * 同意预约(手动匹配专用分支) + * + * @param reserveId 预约记录ID + * @return 操作结果 + */ + R approveReserveByManual(String reserveId); /** * 拒绝预约 diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpSettingService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpSettingService.java index 10b798cf..cad55578 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpSettingService.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeCpSettingService.java @@ -41,6 +41,11 @@ public interface IEfcodeCpSettingService { */ String SETTING_ID_MAX_INNER_UNIT_AC_PERSON_NUM = "CP_MAX_INNER_UNIT_AC_PERSON_NUM"; + /** + * 设置项主键:系统匹配预约周数范围<= + */ + String SETTING_ID_SYSTEM_MATCH_WEEKS = "CP_SETTING_SYSTEM_MATCH_WEEKS"; + /** * 默认值:下阶段计划提交最晚时间 */ @@ -66,6 +71,11 @@ public interface IEfcodeCpSettingService { */ int DEFAULT_MAX_INNER_UNIT_AC_PERSON_NUM = 3; + /** + * 默认值:系统匹配预约周数范围 + */ + int DEFAULT_SYSTEM_MATCH_WEEKS = 2; + /** * 查询行政执法检查计划-设置 * @@ -109,6 +119,13 @@ public interface IEfcodeCpSettingService { */ Integer getMaxInnerUnitAcPersonNum(); + /** + * 获取系统匹配预约周数范围 + * + * @return 数值,未配置或无法解析时返回默认值 {@link #DEFAULT_SYSTEM_MATCH_WEEKS} + */ + Integer getSystemMatchWeeks(); + /** * 分页查询行政执法检查计划-设置列表 * diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java index c836146e..dd6c8ff4 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java @@ -38,9 +38,8 @@ import java.util.stream.Collectors; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.satoken.utils.LoginHelper; import com.zdxt.enforcementcode.domain.EfcodeCpReserve; -import com.zdxt.enforcementcode.domain.EfcodeCpSetting; import com.zdxt.enforcementcode.mapper.EfcodeCpReserveMapper; -import com.zdxt.enforcementcode.mapper.EfcodeCpSettingMapper; +import com.zdxt.enforcementcode.service.IEfcodeCpSettingService; import com.zdxt.enforcementcode.service.IEfcodeCpTaskService; import com.zdxt.enforcementcode.service.IEnterpriseInformationService; import com.zdxt.enforcementcode.service.ISsztJbxx110497NewService; @@ -61,7 +60,7 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { private final EfcodeCpPlanMapper baseMapper; private final EfcodeCpPlanUsersMapper efcodeCpPlanUsersMapper; private final EfcodeCpReserveMapper efcodeCpReserveMapper; - private final EfcodeCpSettingMapper efcodeCpSettingMapper; + private final IEfcodeCpSettingService cpSettingService; private final IEfcodeCpTaskService cpTaskService; private final IEnterpriseInformationService enterpriseInformationService; private final ISsztJbxx110497NewService ssztJbxx110497NewService; @@ -374,11 +373,7 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { } // 条件2:执行时间距今天 <= CP_SETTING_MANUAL_MATCH_DAYS,直接生成任务 if (entity.getPlanDate() != null) { - String manualMatchDaysStr = getSettingValue("CP_SETTING_MANUAL_MATCH_DAYS"); - int manualMatchDays = 3; // 默认值 - if (StringUtils.isNotBlank(manualMatchDaysStr)) { - try { manualMatchDays = Integer.parseInt(manualMatchDaysStr.trim()); } catch (NumberFormatException ignored) {} - } + int manualMatchDays = cpSettingService.getManualMatchDays(); LocalDate planLocalDate = entity.getPlanDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); long daysUntilPlan = ChronoUnit.DAYS.between(LocalDate.now(), planLocalDate); if (daysUntilPlan <= manualMatchDays) { @@ -590,23 +585,16 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { // 1. 日期校验:如果不是强制匹配,则判断当前天数是否等于 CP_SETTING_AUTO_MATCH_START if (!forceMatch) { - String autoMatchStartDay = getSettingValue("CP_SETTING_AUTO_MATCH_START"); + int autoMatchStart = cpSettingService.getAutoMatchStart(); int today = LocalDate.now().getDayOfMonth(); - if (StringUtils.isNotBlank(autoMatchStartDay)) { - int settingDay = Integer.parseInt(autoMatchStartDay.trim()); - if (today != settingDay) { - log.info("[CpPlan] 当前日期{}号,不等于设定的自动匹配日期{}号,跳过执行", today, settingDay); - return resultMap; - } + if (today != autoMatchStart) { + log.info("[CpPlan] 当前日期{}号,不等于设定的自动匹配日期{}号,跳过执行", today, autoMatchStart); + return resultMap; } } // 2. 获取最大联合单位数量 - int maxUnionUnit = 3; // 默认值 - String maxUnionStr = getSettingValue("CP_MAX_UNION_UNIT"); - if (StringUtils.isNotBlank(maxUnionStr)) { - maxUnionUnit = Integer.parseInt(maxUnionStr.trim()); - } + int maxUnionUnit = cpSettingService.getMaxUnionUnit(); // 3. 查询待匹配的计划:plan_date > 当前时间 且 system_reserved=0, reserve_status=1, is_public=1, is_union=1 Date now = new Date(); @@ -761,23 +749,20 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { // 1. 日期校验:如果不是强制匹配,则判断当前天数是否等于 CP_SETTING_AUTO_MATCH_START if (!forceMatch) { - String autoMatchStartDay = getSettingValue("CP_SETTING_AUTO_MATCH_START"); + int autoMatchStart = cpSettingService.getAutoMatchStart(); int today = LocalDate.now().getDayOfMonth(); - if (StringUtils.isNotBlank(autoMatchStartDay)) { - int settingDay = Integer.parseInt(autoMatchStartDay.trim()); - if (today != settingDay) { - log.info("[CpPlan-V2] 当前日期{}号,不等于设定的自动匹配日期{}号,跳过执行", today, settingDay); - return resultMap; - } + if (today != autoMatchStart) { + log.info("[CpPlan-V2] 当前日期{}号,不等于设定的自动匹配日期{}号,跳过执行", today, autoMatchStart); + return resultMap; } } // 2. 获取最大联合单位数量 - int maxUnionUnit = 3; // 默认值 - String maxUnionStr = getSettingValue("CP_MAX_UNION_UNIT"); - if (StringUtils.isNotBlank(maxUnionStr)) { - maxUnionUnit = Integer.parseInt(maxUnionStr.trim()); - } + int maxUnionUnit = cpSettingService.getMaxUnionUnit(); + + // 2.1 获取系统匹配预约周数范围 + int systemMatchWeeks = cpSettingService.getSystemMatchWeeks(); + log.info("[CpPlan-V2] 系统匹配周数范围={}", systemMatchWeeks); // 3. 查询待匹配的计划:plan_date > 当前时间 且 system_reserved=0, manual_reserved=0, reserve_status=1, is_public=1, is_union=1 Date now = new Date(); @@ -808,10 +793,10 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { continue; } - // 5. 计算计划所在的自然周(周一~周日) + // 5. 计算计划所在的时间范围:以计划所在自然周的周一为起始,向后扩展 systemMatchWeeks 周 LocalDate planLocalDate = plan.getPlanDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate weekStartDate = planLocalDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); - LocalDate weekEndDate = planLocalDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); + LocalDate weekEndDate = weekStartDate.plusWeeks(systemMatchWeeks).minusDays(1); // 扩展N周后的周日 // 按"企业+周起始日"去重,同企业同周只处理一次 String processKey = enterpriseNumber + "_" + weekStartDate; @@ -938,15 +923,4 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { log.info("[CpPlan-V2] 计划自动匹配预约执行完毕,共匹配{}组联合预约", matchedCount); return resultMap; } - - /** - * 获取 efcode_cp_setting 配置值 - * - * @param settingId 设置ID - * @return 设置值 - */ - private String getSettingValue(String settingId) { - EfcodeCpSetting setting = efcodeCpSettingMapper.selectById(settingId); - return setting != null ? setting.getSettingValue() : null; - } } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java index eb74e59f..5eaef7ac 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java @@ -382,9 +382,181 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService { /** * 同意预约 + *

+ * opOrder==0 时为第一个操作的用户: + * - 必须传入 opSetDate、opSetTime(用户选择的时间) + * - 重新设置同组记录的 opOrder 为 1,2,3,4,5... + * - 更新当前记录及关联记录的 op_set_date、op_set_time + * - 生成 warringMessage,设置到关联记录(warring_is_pass=0) + * - 当前记录 warring_is_pass=1 + *

+ * opOrder>0 时为后续操作的用户: + * - 设置当前记录 warring_is_pass=1 + * - 备份关联计划的 plan_date/plan_time 到 plan_source_date/plan_source_time + * - 将预约的 op_set_date/op_set_time 更新回关联计划的 plan_date/plan_time + * - 同步更新 efcode_cp_plan 的 reserve_status + */ + @Transactional(rollbackFor = Exception.class) + @Override + public R approveReserveBySystem(String reserveId, String opSetDate, Long opSetTime) { + EfcodeCpReserve reserve = baseMapper.selectById(reserveId); + if (reserve == null) { + return R.fail("预约记录不存在"); + } + if (!Long.valueOf(3L).equals(reserve.getOpStatus())) { + return R.fail("该预约记录已被处理"); + } + + // 查询关联计划 + EfcodeCpPlan myPlan = cpPlanMapper.selectById(reserve.getPlanId()); + if (myPlan == null) { + return R.fail("关联计划不存在"); + } + + // 查询同组其他预约记录 + List groupReserves = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(EfcodeCpReserve::getPlanGroupId, reserve.getPlanGroupId()) + .ne(EfcodeCpReserve::getId, reserveId) + ); + + if (reserve.getOpOrder() != null && reserve.getOpOrder() == 0L) { + // ===== opOrder==0:第一个操作的用户 ===== + if (StringUtils.isBlank(opSetDate) || opSetTime == null) { + return R.fail("请选择执行时间"); + } + + // 解析日期 + Date selectedDate; + try { + selectedDate = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(opSetDate); + } catch (Exception e) { + return R.fail("日期格式不正确"); + } + + // 1. 更新当前记录:同意 + 设置时间 + warring_is_pass=1 + reserve.setOpStatus(1L); + reserve.setOpTime(new Date()); + reserve.setOpSetDate(selectedDate); + reserve.setOpSetTime(opSetTime); + reserve.setWarringIsPass(1); + reserve.setOpOrder(1L); // 第一个操作用户排序号为1 + baseMapper.updateById(reserve); + + // 2. 生成 warringMessage + String timeDesc = opSetDate + " " + getPlanTimeText(opSetTime); + String warringMsg = "第一个操作的" + myPlan.getBureauDeptName() + "执法部门用户选择了" + timeDesc + "时间,请确认时间安排是否合适"; + + // 3. 更新关联记录:设置 op_set_date/op_set_time、warringMessage、warring_is_pass=0、重新分配 opOrder + int order = 2; + for (EfcodeCpReserve r : groupReserves) { + r.setOpSetDate(selectedDate); + r.setOpSetTime(opSetTime); + r.setWarringMessage(warringMsg); + r.setWarringIsPass(0); + r.setOpOrder((long) order++); + baseMapper.updateById(r); + } + + // 4. 备份当前计划的 plan_date/plan_time 到 plan_source_date/plan_source_time,并更新为选定时间 + EfcodeCpPlan planUpdate = new EfcodeCpPlan(); + planUpdate.setId(myPlan.getId()); + if (myPlan.getPlanSourceDate() == null) { + // 只有第一次才备份,避免多次覆盖 + planUpdate.setPlanSourceDate(myPlan.getPlanDate()); + planUpdate.setPlanSourceTime(myPlan.getPlanTime()); + } + planUpdate.setPlanDate(selectedDate); + planUpdate.setPlanTime(opSetTime); + planUpdate.setReserveStatus(3L); // 预约完成 + cpPlanMapper.updateById(planUpdate); + + } else { + // ===== opOrder>0:后续操作的用户 ===== + + // 1. 设置当前记录:同意 + warring_is_pass=1 + reserve.setOpStatus(1L); + reserve.setOpTime(new Date()); + reserve.setWarringIsPass(1); + baseMapper.updateById(reserve); + + // 2. 备份关联计划的 plan_date/plan_time 到 plan_source_date/plan_source_time + EfcodeCpPlan planUpdate = new EfcodeCpPlan(); + planUpdate.setId(myPlan.getId()); + if (myPlan.getPlanSourceDate() == null) { + // 只有第一次才备份,避免多次覆盖 + planUpdate.setPlanSourceDate(myPlan.getPlanDate()); + planUpdate.setPlanSourceTime(myPlan.getPlanTime()); + } + + // 3. 将预约的 op_set_date/op_set_time 更新回关联计划的 plan_date/plan_time + planUpdate.setPlanDate(reserve.getOpSetDate()); + planUpdate.setPlanTime(reserve.getOpSetTime()); + planUpdate.setReserveStatus(3L); // 预约完成 + cpPlanMapper.updateById(planUpdate); + } + + // 后置处理:检查是否所有同组记录都已同意,若是则生成任务 + afterApproveCheck(reserve); + return R.ok(); + } + + /** + * 同意操作后置检查:检查同组是否全部同意,若是则生成任务 + */ + private void afterApproveCheck(EfcodeCpReserve reserve) { + if (StringUtils.isBlank(reserve.getPlanGroupId())) { + return; + } + // 查询同组全部记录 + List allGroupReserves = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(EfcodeCpReserve::getPlanGroupId, reserve.getPlanGroupId()) + ); + // 检查是否全部同意 + boolean allApproved = allGroupReserves.stream() + .allMatch(r -> Long.valueOf(1L).equals(r.getOpStatus())); + if (!allApproved) { + log.info("[预约后置] planGroupId={} 尚有未同意的记录,等待其他用户操作", reserve.getPlanGroupId()); + return; + } + // 全部同意:生成1条任务,所有同组计划共享该 task_id + String sharedTaskId = null; + for (EfcodeCpReserve r : allGroupReserves) { + EfcodeCpPlan plan = cpPlanMapper.selectById(r.getPlanId()); + if (plan == null || StringUtils.isNotBlank(plan.getTaskId())) continue; + if (sharedTaskId == null) { + sharedTaskId = cpTaskService.forceGenerateTask(plan); + } else { + EfcodeCpPlan planUpdate = new EfcodeCpPlan(); + planUpdate.setId(plan.getId()); + planUpdate.setTaskId(sharedTaskId); + planUpdate.setReserveStatus(3L); + cpPlanMapper.updateById(planUpdate); + } + } + log.info("[预约后置] planGroupId={} 全部同意,生成任务taskId={},关联{}条计划", + reserve.getPlanGroupId(), sharedTaskId, allGroupReserves.size()); + } + + /** + * 时段文本 + */ + private String getPlanTimeText(Long time) { + if (time == null) return ""; + switch (time.intValue()) { + case 1: return "上午"; + case 2: return "下午"; + case 3: return "其他"; + default: return ""; + } + } + + /** + * 同意预约(手动匹配专用分支) */ @Override - public R approveReserve(String reserveId) { + public R approveReserveByManual(String reserveId) { EfcodeCpReserve reserve = baseMapper.selectById(reserveId); if (reserve == null) { return R.fail("预约记录不存在"); @@ -436,42 +608,6 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService { cpPlanMapper.updateById(planUpdate); log.info("[预约后置] 计划[{}] reserve_status 更新为 {}", reserve.getPlanId(), newPlanStatus); } - - // 同意操作后:检查同一 planGroupId 已同意数量是否达到最大联合单位数 - if ("approve".equals(action) && StringUtils.isNotBlank(reserve.getPlanGroupId())) { - int maxUnionUnit = cpSettingService.getMaxUnionUnit(); - long approvedCount = baseMapper.selectCount( - Wrappers.lambdaQuery() - .eq(EfcodeCpReserve::getPlanGroupId, reserve.getPlanGroupId()) - .eq(EfcodeCpReserve::getOpStatus, 1L) // 1=已同意 - ); - log.info("[预约后置] planGroupId={}, 已同意数量={}, 最大联合数={}", reserve.getPlanGroupId(), approvedCount, maxUnionUnit); - if (approvedCount >= maxUnionUnit) { - // 达标:只生成1条任务,所有同组计划共享该task_id - List groupReserves = baseMapper.selectList( - Wrappers.lambdaQuery() - .eq(EfcodeCpReserve::getPlanGroupId, reserve.getPlanGroupId()) - .eq(EfcodeCpReserve::getOpStatus, 1L) - ); - String sharedTaskId = null; - for (EfcodeCpReserve r : groupReserves) { - EfcodeCpPlan plan = cpPlanMapper.selectById(r.getPlanId()); - if (plan == null || StringUtils.isNotBlank(plan.getTaskId())) continue; - if (sharedTaskId == null) { - // 用第一条计划生成唯一一条任务 - sharedTaskId = cpTaskService.forceGenerateTask(plan); - } else { - // 其余计划直接回写同一个task_id - EfcodeCpPlan planUpdate = new EfcodeCpPlan(); - planUpdate.setId(plan.getId()); - planUpdate.setTaskId(sharedTaskId); - planUpdate.setReserveStatus(3L); - cpPlanMapper.updateById(planUpdate); - } - } - log.info("[预约后置] planGroupId={} 联合数量达标,生成1条任务taskId={},关联{}条计划", reserve.getPlanGroupId(), sharedTaskId, groupReserves.size()); - } - } } /** diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpSettingServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpSettingServiceImpl.java index c00765a7..9b3c61d4 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpSettingServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpSettingServiceImpl.java @@ -84,6 +84,14 @@ public class EfcodeCpSettingServiceImpl implements IEfcodeCpSettingService { return getIntSetting(SETTING_ID_MAX_INNER_UNIT_AC_PERSON_NUM, DEFAULT_MAX_INNER_UNIT_AC_PERSON_NUM); } + /** + * 获取系统匹配预约周数范围 + */ + @Override + public Integer getSystemMatchWeeks() { + return getIntSetting(SETTING_ID_SYSTEM_MATCH_WEEKS, DEFAULT_SYSTEM_MATCH_WEEKS); + } + /** * 根据设置主键获取整型配置值,不存在或解析失败时返回默认值 * diff --git a/zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/reserve.js b/zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/reserve.js index 2b997385..58082388 100644 --- a/zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/reserve.js +++ b/zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/reserve.js @@ -7,8 +7,15 @@ const reserveBaseUrl = "/app-enf/cpReserve"; // 预约列表(分页,支持 resFrom 过滤:0=手动预约,1=系统匹配) export const getReserveList = (params, success, error) => _get(reserveBaseUrl + "/list", params, success, error); -// 预约处理:同意 -export const approveReserve = (id, success, error) => _put(reserveBaseUrl + "/approve/" + id, {}, success, error); +// 预约处理:同意(系统匹配专用,支持传选择的时间参数) +export const approveReserve = (id, params, success, error) => { + let url = reserveBaseUrl + "/approveBySystem/" + id; + if (params) { + const qs = Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&'); + if (qs) url += '?' + qs; + } + return _put(url, {}, success, error); +}; // 预约处理:拒绝 export const rejectReserve = (id, success, error) => _put(reserveBaseUrl + "/reject/" + id, {}, success, error); diff --git a/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue b/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue index ecb7d66f..9882936b 100644 --- a/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue +++ b/zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue @@ -43,7 +43,7 @@

计划时间:
-
{{ item.planDate || '-' }} {{ getPlanTimeText(item.planTime) }}
+
{{ formatDateShort(item.planDate) }} {{ getPlanTimeText(item.planTime) }}
@@ -70,7 +70,7 @@
计划时间: - {{ formatDate(member.planDate) }} {{ getPlanTimeText(member.planTime) }} + {{ formatDateShort(member.planDate) }} {{ getPlanTimeText(member.planTime) }}
@@ -103,6 +103,30 @@ + + + +
+
+
{{ opt.bureauDeptName }}
+
{{ opt.dateLabel }}
+
+
没有可选的时间
+ 确认选择 +
+
@@ -121,6 +145,12 @@ const pageNum = ref(1); const pageSize = 10; const filterStatus = ref(null); // null=全部, 1=已同意, 2=已拒绝, 3=待确认 +// 时间选择相关 +const showTimePicker = ref(false); +const timeOptions = ref([]); // [{ bureauDeptName, dateLabel, planDate, planTime }] +const selectedTimeIdx = ref(null); +const pendingApproveItem = ref(null); // 待同意的item缓存 + const filterOptions = [ { label: '全部', value: null }, { label: '待确认', value: 3 }, @@ -146,7 +176,7 @@ const getPlanTimeText = (time) => { return map[time] || ''; }; -// 格式化日期 +// 格式化日期(带时分) const formatDate = (dateStr) => { if (!dateStr) return '-'; const d = new Date(dateStr); @@ -159,6 +189,17 @@ const formatDate = (dateStr) => { return `${y}-${m}-${day} ${h}:${min}`; }; +// 格式化日期(仅日期) +const formatDateShort = (dateStr) => { + if (!dateStr) return '-'; + const d = new Date(dateStr); + if (isNaN(d.getTime())) return dateStr; + const y = d.getFullYear(); + const m = String(d.getMonth() + 1).padStart(2, '0'); + const day = String(d.getDate()).padStart(2, '0'); + return `${y}-${m}-${day}`; +}; + // 加载数据 const onLoad = () => { fetchList(); @@ -251,19 +292,70 @@ const onShowMessage = (item) => { // 同意 const onApprove = (item) => { - showConfirmDialog({ - title: '提示', - message: '确定同意该系统匹配吗?', - }).then(() => { - approveReserve(item.id, (res) => { - if (res.code === 200) { - showSuccessToast('已同意'); - afterOperate(item, 'approve'); - } else { - showDialog({ title: '提示', message: res.msg || '操作失败' }); - } + if (item.opOrder === 0 || item.opOrder === null) { + // opOrder==0:第一个操作的用户,弹出时间选择 + buildTimeOptions(item); + pendingApproveItem.value = item; + selectedTimeIdx.value = null; + showTimePicker.value = true; + } else { + // opOrder>0:后续用户,弹出 warringMessage 提示 + const msg = item.warringMessage || '确定同意该系统匹配吗?'; + showConfirmDialog({ + title: '时间确认', + message: msg, + confirmButtonText: '确认同意', + cancelButtonText: '取消', + }).then(() => { + doApprove(item.id, null); + }).catch(() => {}); + } +}; + +// 构建时间选项(从当前卡片+关联计划中提取可选的日期/时段) +const buildTimeOptions = (item) => { + const options = []; + // 当前卡片本身的计划时间 + if (item.planDate) { + options.push({ + bureauDeptName: item.bureauDeptName || '本单位', + dateLabel: formatDateShort(item.planDate) + ' ' + getPlanTimeText(item.planTime), + planDate: formatDateShort(item.planDate), + planTime: item.planTime, }); - }).catch(() => {}); + } + // 关联计划的时间 + for (const member of groupMembers.value) { + if (member.planDate) { + options.push({ + bureauDeptName: member.bureauDeptName || '-', + dateLabel: formatDateShort(member.planDate) + ' ' + getPlanTimeText(member.planTime), + planDate: formatDateShort(member.planDate), + planTime: member.planTime, + }); + } + } + timeOptions.value = options; +}; + +// 确认时间选择后提交同意 +const onConfirmTimeSelection = () => { + if (selectedTimeIdx.value === null || !pendingApproveItem.value) return; + const selected = timeOptions.value[selectedTimeIdx.value]; + showTimePicker.value = false; + doApprove(pendingApproveItem.value.id, { opSetDate: selected.planDate, opSetTime: selected.planTime }); +}; + +// 执行同意接口调用 +const doApprove = (id, params) => { + approveReserve(id, params, (res) => { + if (res.code === 200) { + showSuccessToast('已同意'); + afterOperate(); + } else { + showDialog({ title: '提示', message: res.msg || '操作失败' }); + } + }); }; // 拒绝 @@ -275,7 +367,7 @@ const onReject = (item) => { rejectReserve(item.id, (res) => { if (res.code === 200) { showSuccessToast('已拒绝'); - afterOperate(item, 'reject'); + afterOperate(); } else { showDialog({ title: '提示', message: res.msg || '操作失败' }); } @@ -284,9 +376,10 @@ const onReject = (item) => { }; // 操作后置处理 -const afterOperate = (item, action) => { - // TODO: 可在此处扩展后置逻辑,如通知、跳转等 +const afterOperate = () => { selectedId.value = null; + groupMembers.value = []; + pendingApproveItem.value = null; onRefresh(); }; @@ -532,4 +625,47 @@ const afterOperate = (item, action) => { color: rgba(255, 255, 255, 0.6); text-align: center; } + +.time-picker-content { + padding: 16px; +} + +.time-option { + display: flex; + justify-content: space-between; + align-items: center; + padding: 14px 16px; + margin-bottom: 10px; + border-radius: 10px; + background: #f5f7fa; + border: 2px solid transparent; + transition: all 0.2s; + + &.time-option-selected { + background: #e8f4ff; + border-color: #1f75c9; + } +} + +.time-option-unit { + font-size: 14px; + font-weight: 600; + color: #333; +} + +.time-option-date { + font-size: 14px; + color: #666; +} + +.time-option-empty { + text-align: center; + color: #999; + padding: 20px; + font-size: 14px; +} + +.time-confirm-btn { + margin-top: 16px; +}