Browse Source

执法端-计划

dev-V1.0.1
庄锐波 5 months ago
parent
commit
7ef1273d3b
  1. 2
      script/sql/zfjd-refactoring-v1/013-计划任务表设计.sql
  2. 6
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EfcodeCpPlanVo.java
  3. 13
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EfcodeCpReserveVo.java
  4. 113
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java
  5. 76
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java
  6. 4
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml
  7. 16
      zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/task.js
  8. 4
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/plan/cp_plan_list.vue
  9. 169
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/plan/cp_plan_mine.vue
  10. 18
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue

2
script/sql/zfjd-refactoring-v1/013-计划任务表设计.sql

@ -24,7 +24,7 @@ CREATE TABLE `efcode_cp_plan`
`is_union` tinyint NOT NULL DEFAULT 1 COMMENT '是否联合执法:1=是,0=否',
`union_bureau_dept_names` varchar(500) DEFAULT NULL COMMENT '联合执法单位(多个逗号分隔)',
`reserve_status` tinyint NOT NULL DEFAULT 0 COMMENT '预约状态:0=草稿,1=待预约(未发起联合),2=预约中,3=预约完成,4=重新预约中,5=待预约(还有位置可以加入),6=待预约(人数已满)',
`reserve_status` tinyint NOT NULL DEFAULT 0 COMMENT '预约状态:0=草稿,1=待预约(未发起联合),2=预约中,3=预约完成,4=重新预约中,5=待预约(还有位置可以加入),6=待预约(人数已满),7=预约完成(人数已满)',
`task_id` varchar(50) NULL DEFAULT '' COMMENT '任务id:空串表示还没生成任务',
`system_reserved` tinyint NOT NULL DEFAULT 0 COMMENT '是否已经经过系统推荐预约:1=是,0=否',

6
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EfcodeCpPlanVo.java

@ -236,5 +236,11 @@ public class EfcodeCpPlanVo implements Serializable {
@com.baomidou.mybatisplus.annotation.TableField(exist = false)
private Boolean canReserve;
/**
* 关联的预约记录列表(非数据库字段,后置填充)
*/
@com.baomidou.mybatisplus.annotation.TableField(exist = false)
private java.util.List<EfcodeCpReserveVo> reserves;
}

13
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EfcodeCpReserveVo.java

@ -96,6 +96,16 @@ public class EfcodeCpReserveVo implements Serializable {
@ExcelProperty(value = "来自:系统匹配=1,手动匹配=0")
private Long resFrom;
/**
* 发送者用户ID
*/
private String senderId;
/**
* 接收者用户ID
*/
private String receiverId;
// ===== 计划关联字段(非数据库映射,由服务层填充) =====
/** 计划日期 */
@ -116,5 +126,8 @@ public class EfcodeCpReserveVo implements Serializable {
/** 执法人员姓名 */
private String enforceUserNames;
/** 执法人员电话号码 */
private String enforceUserTels;
}

113
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java

@ -13,6 +13,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.zdxt.enforcementcode.domain.bo.EfcodeCpPlanBo;
import com.zdxt.enforcementcode.domain.vo.EfcodeCpPlanVo;
import com.zdxt.enforcementcode.domain.vo.EfcodeCpReserveVo;
import com.zdxt.enforcementcode.domain.EfcodeCpPlan;
import com.zdxt.enforcementcode.domain.EfcodeCpPlanUsers;
import com.zdxt.enforcementcode.mapper.EfcodeCpPlanMapper;
@ -81,6 +82,8 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService {
Page<EfcodeCpPlanVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
// 后置计算 editEnable
computeEditEnable(result.getRecords());
// 后置填充关联预约记录
fillReserves(result.getRecords());
return TableDataInfo.build(result);
}
@ -162,6 +165,109 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService {
}
}
/**
* 后置填充关联预约记录:通过plan_group_id查询同组的其他计划信息,组装为1:N结构
* <p>展示逻辑:对于我的每条计划,找到同一plan_group_id下的其他计划,将对方的单位/人员/电话/执法事项填充到reserve VO中</p>
*/
private void fillReserves(List<EfcodeCpPlanVo> records) {
if (records == null || records.isEmpty()) {
return;
}
List<String> myPlanIds = records.stream().map(EfcodeCpPlanVo::getId).collect(Collectors.toList());
if (myPlanIds.isEmpty()) {
return;
}
// 1. 查询我的计划关联的预约记录,获取plan_group_id
List<EfcodeCpReserve> myReserves = efcodeCpReserveMapper.selectList(
Wrappers.<EfcodeCpReserve>lambdaQuery()
.in(EfcodeCpReserve::getPlanId, myPlanIds)
);
if (myReserves.isEmpty()) {
records.forEach(vo -> vo.setReserves(new ArrayList<>()));
return;
}
// 2. 建立 我的planId → plan_group_id 的映射
Map<String, String> planToGroupMap = new HashMap<>();
Set<String> groupIds = new HashSet<>();
for (EfcodeCpReserve r : myReserves) {
if (StringUtils.isNotBlank(r.getPlanGroupId())) {
planToGroupMap.put(r.getPlanId(), r.getPlanGroupId());
groupIds.add(r.getPlanGroupId());
}
}
if (groupIds.isEmpty()) {
records.forEach(vo -> vo.setReserves(new ArrayList<>()));
return;
}
// 3. 查询同组的其他预约记录(plan_id不是我的计划)
List<EfcodeCpReserve> otherReserves = efcodeCpReserveMapper.selectList(
Wrappers.<EfcodeCpReserve>lambdaQuery()
.in(EfcodeCpReserve::getPlanGroupId, groupIds)
.notIn(EfcodeCpReserve::getPlanId, myPlanIds)
.orderByDesc(EfcodeCpReserve::getReserveTime)
);
// 4. 查询其他计划的详情(单位、人员、电话、执法事项)
Set<String> otherPlanIds = otherReserves.stream()
.map(EfcodeCpReserve::getPlanId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
Map<String, EfcodeCpPlan> otherPlanMap = new HashMap<>();
if (!otherPlanIds.isEmpty()) {
List<EfcodeCpPlan> otherPlans = baseMapper.selectByIds(new ArrayList<>(otherPlanIds));
for (EfcodeCpPlan p : otherPlans) {
otherPlanMap.put(p.getId(), p);
}
}
// 5. 按plan_group_id分组其他预约记录
Map<String, List<EfcodeCpReserve>> groupToOtherReserves = otherReserves.stream()
.collect(Collectors.groupingBy(EfcodeCpReserve::getPlanGroupId));
// 6. 填充到每条计划VO
for (EfcodeCpPlanVo vo : records) {
String groupId = planToGroupMap.get(vo.getId());
if (StringUtils.isBlank(groupId)) {
vo.setReserves(new ArrayList<>());
continue;
}
List<EfcodeCpReserve> others = groupToOtherReserves.getOrDefault(groupId, new ArrayList<>());
List<EfcodeCpReserveVo> voList = others.stream().map(r -> {
EfcodeCpReserveVo rv = new EfcodeCpReserveVo();
rv.setId(r.getId());
rv.setPlanId(r.getPlanId());
rv.setPlanGroupId(r.getPlanGroupId());
rv.setReserveTime(r.getReserveTime());
rv.setOpStatus(r.getOpStatus());
rv.setOpOrder(r.getOpOrder());
rv.setOpTime(r.getOpTime());
rv.setOpMessage(r.getOpMessage());
rv.setOpSetDate(r.getOpSetDate());
rv.setOpSetTime(r.getOpSetTime());
rv.setResFrom(r.getResFrom());
rv.setSenderId(r.getSenderId());
rv.setReceiverId(r.getReceiverId());
// 填充对方计划详情
EfcodeCpPlan otherPlan = otherPlanMap.get(r.getPlanId());
if (otherPlan != null) {
rv.setBureauDeptName(otherPlan.getBureauDeptName());
rv.setEnforceUserNames(otherPlan.getEnforceUserNames());
rv.setEnforceUserTels(otherPlan.getEnforceUserTels());
rv.setEnforceContent(otherPlan.getEnforceContent());
rv.setPlanDate(otherPlan.getPlanDate());
rv.setPlanTime(otherPlan.getPlanTime());
rv.setEnterpriseName(otherPlan.getEnterpriseName());
}
return rv;
}).collect(Collectors.toList());
vo.setReserves(voList);
}
}
private LambdaQueryWrapper<EfcodeCpPlan> buildQueryWrapper(EfcodeCpPlanBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<EfcodeCpPlan> lqw = Wrappers.lambdaQuery();
@ -327,7 +433,7 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService {
* @throws ServiceException 如果存在重复记录则抛出
*/
private void checkDuplicate(EfcodeCpPlanBo bo) {
if (bo.getPlanType() == null || bo.getPlanDate() == null || bo.getPlanTime() == null
if (bo.getPlanDate() == null || bo.getPlanTime() == null
|| StringUtils.isBlank(bo.getEnterpriseId())) {
return;
}
@ -336,9 +442,8 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService {
if (StringUtils.isBlank(currentUserId) || "null".equals(currentUserId)) {
return;
}
// 1) 查找同计划类型+同日期+同时段+同企业的已有计划
// 1) 查找同日期+同时段+同企业的已有计划(不限制计划类型)
LambdaQueryWrapper<EfcodeCpPlan> lqw = Wrappers.lambdaQuery();
lqw.eq(EfcodeCpPlan::getPlanType, bo.getPlanType());
lqw.eq(EfcodeCpPlan::getPlanDate, bo.getPlanDate());
lqw.eq(EfcodeCpPlan::getPlanTime, bo.getPlanTime());
lqw.eq(EfcodeCpPlan::getEnterpriseId, bo.getEnterpriseId());
@ -360,7 +465,7 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService {
.eq(EfcodeCpPlanUsers::getUserId, currentUserId)
);
if (count > 0) {
throw new ServiceException("您在该日期该时段对该企业已有同类型执法计划,不可重复提交");
throw new ServiceException("您在该日期该时段对该企业已有执法计划,不可重复提交");
}
}

76
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java

@ -167,7 +167,7 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService {
}
/**
* 预约前置检查
* 预约前置检查(包含所有前置校验逻辑)
*/
@Override
public R<Void> checkCanReserve(String targetPlanId) {
@ -177,11 +177,48 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService {
if (targetPlan == null) {
return R.fail("目标计划不存在");
}
// 1.1 校验目标计划状态:只有待预约(1)、已取消(4)、预约中可加入(5)的计划才允许预约
Long reserveStatus = targetPlan.getReserveStatus();
if (reserveStatus == null || !Arrays.asList(1L, 4L, 5L).contains(reserveStatus)) {
return R.fail("该计划当前状态不允许预约");
}
// 2. 判断当前用户是否有同一时间+同一时段+同一企业的计划
EfcodeCpPlan myPlan = findMyMatchingPlan(targetPlan, currentUserId);
if (myPlan == null) {
return R.fail("请先添加我的计划再进行预约");
}
// 2.1 校验我的计划状态:只有待预约(1)、已取消(4)、预约中可加入(5)的计划才允许发起预约
Long myReserveStatus = myPlan.getReserveStatus();
if (myReserveStatus == null || !Arrays.asList(1L, 4L, 5L).contains(myReserveStatus)) {
return R.fail("您的计划当前状态不允许发起预约");
}
// 2.2 我的计划在efcode_cp_reserve只能存在一条op_status为3的记录
long pendingCount = baseMapper.selectCount(
Wrappers.<EfcodeCpReserve>lambdaQuery()
.eq(EfcodeCpReserve::getPlanId, myPlan.getId())
.eq(EfcodeCpReserve::getOpStatus, 3L)
);
if (pendingCount > 0) {
return R.fail("您的这条计划已有一条待确认的预约记录,请等待对方处理后再发起新预约");
}
// 2.3 如果我的计划已经作为plan_group_id而存在,则不允许发起预约,除非其他的所有记录的op_status都是2或4
String myPlanId = myPlan.getId();
long groupCount = baseMapper.selectCount(
Wrappers.<EfcodeCpReserve>lambdaQuery()
.eq(EfcodeCpReserve::getPlanGroupId, myPlanId)
);
if (groupCount > 0) {
List<EfcodeCpReserve> otherReserves = baseMapper.selectList(
Wrappers.<EfcodeCpReserve>lambdaQuery()
.eq(EfcodeCpReserve::getPlanGroupId, myPlanId)
.ne(EfcodeCpReserve::getPlanId, myPlanId)
);
boolean allRejectedOrCancelled = otherReserves.stream()
.allMatch(r -> r.getOpStatus() != null && (r.getOpStatus() == 2L || r.getOpStatus() == 4L));
if (!allRejectedOrCancelled) {
return R.fail("您的计划已有进行中的预约分组,无法再发起新预约");
}
}
// 3. 判断 plan_group_id 人数是否已达上限
String planGroupId = findExistingPlanGroupId(targetPlanId);
if (planGroupId != null) {
@ -205,17 +242,16 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService {
@Transactional(rollbackFor = Exception.class)
@Override
public R<Void> doReserve(String targetPlanId, String opMessage) {
// 前置校验(与 checkCanReserve 一致)
R<Void> checkResult = checkCanReserve(targetPlanId);
if (checkResult.getCode() != 200) {
return checkResult;
}
Long currentUserId = LoginHelper.getUserId();
// 1. 查询目标计划
EfcodeCpPlan targetPlan = cpPlanMapper.selectById(targetPlanId);
if (targetPlan == null) {
return R.fail("目标计划不存在");
}
// 2. 查找当前用户匹配的计划
EfcodeCpPlan myPlan = findMyMatchingPlan(targetPlan, currentUserId);
if (myPlan == null) {
return R.fail("请先添加我的计划再进行预约");
}
// 3. 判断目标计划在reserve表中是否已存在
String planGroupId = findExistingPlanGroupId(targetPlanId);
int maxUnionUnit = cpSettingService.getMaxUnionUnit();
@ -229,9 +265,9 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService {
String senderIdStr = String.valueOf(currentUserId);
String receiverIdStr = String.valueOf(targetPlan.getCreateBy());
// 目标计划记录
insertReserveRecord(targetPlanId, planGroupId, "", targetPlan.getPlanDate(), targetPlan.getPlanTime(), 0L, senderIdStr, receiverIdStr);
insertReserveRecord(targetPlanId, planGroupId, "", targetPlan.getPlanDate(), targetPlan.getPlanTime(), 0L, 1L, "manual_system_sender", receiverIdStr);
// 我的计划记录
insertReserveRecord(myPlan.getId(), planGroupId, opMessage, targetPlan.getPlanDate(), targetPlan.getPlanTime(), 0L, senderIdStr, receiverIdStr);
insertReserveRecord(myPlan.getId(), planGroupId, opMessage, targetPlan.getPlanDate(), targetPlan.getPlanTime(), 0L, 3L, senderIdStr, receiverIdStr);
} else {
// 已存在:检查人数上限
long activeCount = countActiveReserves(planGroupId);
@ -244,7 +280,7 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService {
return R.fail("您已经预约过该计划");
}
// 插入1条记录
insertReserveRecord(myPlan.getId(), planGroupId, opMessage, targetPlan.getPlanDate(), targetPlan.getPlanTime(), 0L, String.valueOf(currentUserId), String.valueOf(targetPlan.getCreateBy()));
insertReserveRecord(myPlan.getId(), planGroupId, opMessage, targetPlan.getPlanDate(), targetPlan.getPlanTime(), 0L, 3L, String.valueOf(currentUserId), String.valueOf(targetPlan.getCreateBy()));
}
// ===== 插入预约记录后:更新两个计划的 reserve_status =====
@ -323,16 +359,17 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService {
/**
* 插入一条预约记录
*
* @param opStatus 操作状态:1=同意,2=拒绝,3=待确认,4=超期取消
* @param senderId 发送者用户ID
* @param receiverId 接收者用户ID
*/
private void insertReserveRecord(String planId, String planGroupId, String opMessage, Date opSetDate, Long opSetTime, Long resFrom,
String senderId, String receiverId) {
Long opStatus, String senderId, String receiverId) {
EfcodeCpReserve record = new EfcodeCpReserve();
record.setPlanId(planId);
record.setPlanGroupId(planGroupId);
record.setReserveTime(new Date());
record.setOpStatus(3L); // 待确认
record.setOpStatus(opStatus);
record.setOpOrder(0L);
record.setOpMessage(opMessage);
record.setOpSetDate(opSetDate);
@ -389,6 +426,17 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService {
*/
private void afterOperate(EfcodeCpReserve reserve, String action) {
log.info("预约记录[{}]执行{}操作后置处理, planGroupId={}", reserve.getId(), action, reserve.getPlanGroupId());
// 同步更新对应计划的 reserve_status:同意→3(预约完成),拒绝→4(重新预约中)
if (StringUtils.isNotBlank(reserve.getPlanId())) {
Long newPlanStatus = "approve".equals(action) ? 3L : 4L;
EfcodeCpPlan planUpdate = new EfcodeCpPlan();
planUpdate.setId(reserve.getPlanId());
planUpdate.setReserveStatus(newPlanStatus);
cpPlanMapper.updateById(planUpdate);
log.info("[预约后置] 计划[{}] reserve_status 更新为 {}", reserve.getPlanId(), newPlanStatus);
}
// 同意操作后:检查同一 planGroupId 已同意数量是否达到最大联合单位数
if ("approve".equals(action) && StringUtils.isNotBlank(reserve.getPlanGroupId())) {
int maxUnionUnit = cpSettingService.getMaxUnionUnit();

4
zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml

@ -23,10 +23,12 @@
p.enterprise_name,
p.enforce_content,
p.bureau_dept_name,
p.enforce_user_names
p.enforce_user_names,
p.enforce_user_tels
FROM efcode_cp_reserve r
LEFT JOIN efcode_cp_plan p ON r.plan_id = p.id
<where>
r.sender_id != 'manual_system_sender'
<!-- 按来源过滤:0=手动预约,1=系统匹配 -->
<if test="resFrom != null">
AND r.res_from = #{resFrom}

16
zdxt-web-client/zdxt-efcode-enforcement-app/src/api/enforcement/task.js

@ -0,0 +1,16 @@
import { _get } from "@/http/index";
// region 任务管理接口
const taskBaseUrl = "/app-enf/cpTask";
// 我的任务列表(指定日期)
export const getMyTaskList = (params, success, error) => _get(taskBaseUrl + "/myList", params, success, error);
// 指定月份中有任务的日期列表(日历打点)
export const getMyTaskDates = (yearMonth, success, error) => _get(taskBaseUrl + "/myTaskDates", { yearMonth }, success, error);
// 我的任务详情(通过 planId 查询)
export const getMyTaskDetail = (planId, success, error) => _get(taskBaseUrl + "/myDetail", { planId }, success, error);
// endregion

4
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/plan/cp_plan_list.vue

@ -25,8 +25,8 @@
</van-tab>
</van-tabs>
<!-- 悬浮新增按钮 -->
<div class="add-btn" @click="onAdd">
<!-- 悬浮新增按钮(仅我的计划Tab显示) -->
<div v-if="activeTab === 'mine'" class="add-btn" @click="onAdd">
<van-icon name="plus" size="22" color="#fff" />
</div>
</div>

169
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/plan/cp_plan_mine.vue

@ -39,7 +39,11 @@
v-for="(item, index) in planList"
:key="item.id"
class="plan-card normal-card"
:class="{ 'pending-card': item.reserveStatus === 1 }"
:class="{
'pending-card': item.reserveStatus === 1,
'expanded-card': expandedId === item.id
}"
@click="onToggleExpand(item)"
>
<div class="card-header">
<div class="card-type">{{ item.isUnion === 1 ? '联合执法' : '非联合执法' }}</div>
@ -74,11 +78,53 @@
<div class="value gray">{{ item.enterpriseName }}</div>
</div>
<!-- 展开的预约信息区域 -->
<div v-if="expandedId === item.id && item.reserves && item.reserves.length > 0" class="reserve-section">
<div class="reserve-section-title">关联预约信息</div>
<div
v-for="(rv, ri) in item.reserves"
:key="rv.id || ri"
class="reserve-item"
>
<div class="reserve-row">
<span class="reserve-label">状态:</span>
<span class="reserve-value" :class="'op-status-' + rv.opStatus">{{ getOpStatusText(rv.opStatus) }}</span>
</div>
<div v-if="rv.bureauDeptName" class="reserve-row">
<span class="reserve-label">对方单位:</span>
<span class="reserve-value">{{ rv.bureauDeptName }}</span>
</div>
<div v-if="rv.enforceUserNames" class="reserve-row">
<span class="reserve-label">执法人员:</span>
<span class="reserve-value">{{ rv.enforceUserNames }}</span>
</div>
<div v-if="rv.enforceUserTels" class="reserve-row">
<span class="reserve-label">联系电话:</span>
<span class="reserve-value">{{ rv.enforceUserTels }}</span>
</div>
<div v-if="rv.enforceContent" class="reserve-row">
<span class="reserve-label">执法事项:</span>
<span class="reserve-value">{{ rv.enforceContent }}</span>
</div>
<div v-if="rv.opMessage" class="reserve-row">
<span class="reserve-label">留言:</span>
<span class="reserve-value">{{ rv.opMessage }}</span>
</div>
<div class="reserve-row">
<span class="reserve-label">来源:</span>
<span class="reserve-value">{{ rv.resFrom === 1 ? '系统匹配' : '手动预约' }}</span>
</div>
</div>
</div>
<div v-if="expandedId === item.id && (!item.reserves || item.reserves.length === 0)" class="reserve-section">
<div class="reserve-empty">暂无关联预约记录</div>
</div>
<div v-if="item.editEnable !== 0 || item.deleteEnable !== 0" class="card-actions">
<div
v-if="item.editEnable !== 0"
class="action-item blue-action"
@click="onEditPlan(item)"
@click.stop="onEditPlan(item)"
>
<van-icon name="edit" />
<span>编辑</span>
@ -86,7 +132,7 @@
<div
v-if="item.deleteEnable !== 0"
class="action-item blue-action"
@click="onDeletePlan(item)"
@click.stop="onDeletePlan(item)"
>
<van-icon name="delete-o" />
<span>删除</span>
@ -239,10 +285,16 @@ const finished = ref(false);
const refreshing = ref(false);
const pageNum = ref(1);
const pageSize = 10;
const expandedId = ref(null); // 当前展开的卡片ID
// 切换展开/收起
const onToggleExpand = (item) => {
expandedId.value = expandedId.value === item.id ? null : item.id;
};
// 状态文本映射
const getStatusText = (status) => {
const map = { 0: '草稿', 1: '待预约', 2: '预约中', 3: '预约完成' };
const map = { 0: '草稿', 1: '待预约(未发起联合)', 2: '预约中', 3: '预约完成', 4: '重新预约中', 5: '待预约(还有位置可以加入)', 6: '待预约(人数已满)', 7: '预约完成(人数已满)' };
return map[status] || '待确认';
};
@ -258,6 +310,18 @@ const getPersonList = (item) => {
return item.enforceUserNames.split(',');
};
// 预约操作状态文本
const getOpStatusText = (status) => {
const map = { 1: '已同意', 2: '已拒绝', 3: '待确认', 4: '超期取消' };
return map[status] || '未知';
};
// 格式化日期时间
const formatDateTime = (dateStr) => {
if (!dateStr) return '-';
return String(dateStr).substring(0, 16).replace('T', ' ');
};
// 加载数据
const onLoad = () => {
fetchList();
@ -500,6 +564,103 @@ const onDeletePlan = (item) => {
border: 1.5px solid rgba(35, 130, 207, 0.35);
}
/* 展开状态卡片:深蓝渐变背景(同预约处理选中效果) */
.expanded-card {
background: radial-gradient(circle at 78% 5%, rgba(65, 188, 255, 0.75) 0, rgba(65, 188, 255, 0.25) 20%, transparent 42%),
linear-gradient(135deg, #064ec6 0%, #0b63d9 48%, #268ee5 100%) !important;
border-color: transparent;
box-shadow: 0 6px 20px rgba(6, 78, 198, 0.25) !important;
.card-type {
background: rgba(255, 255, 255, 0.15) !important;
color: rgba(255, 255, 255, 0.9) !important;
}
.plan-title {
color: #fff !important;
}
.label {
color: #fff !important;
}
.value {
color: rgba(255, 255, 255, 0.7) !important;
}
.status {
color: #f0b66c !important;
}
.card-actions {
border-top-color: rgba(255, 255, 255, 0.18);
}
.action-item {
color: rgba(255, 255, 255, 0.85);
}
}
/* 预约信息展开区域 */
.reserve-section {
margin-top: 14px;
padding-top: 12px;
border-top: 1px dashed rgba(255, 255, 255, 0.25);
.reserve-section-title {
font-size: 14px;
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 10px;
}
.reserve-empty {
font-size: 13px;
color: rgba(255, 255, 255, 0.6);
text-align: center;
padding: 8px 0;
}
}
.reserve-item {
background: rgba(255, 255, 255, 0.12);
border-radius: 10px;
padding: 10px 12px;
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
}
.reserve-row {
display: flex;
align-items: center;
margin-bottom: 4px;
font-size: 13px;
&:last-child {
margin-bottom: 0;
}
.reserve-label {
width: 70px;
flex-shrink: 0;
color: rgba(255, 255, 255, 0.75);
font-weight: 500;
}
.reserve-value {
flex: 1;
color: rgba(255, 255, 255, 0.85);
}
.op-status-1 { color: #7dffaa; font-weight: 500; }
.op-status-2 { color: #ff8a8a; font-weight: 500; }
.op-status-3 { color: #f0b66c; font-weight: 500; }
.op-status-4 { color: rgba(255, 255, 255, 0.5); font-weight: 500; }
}
/* 普通卡片 */
.normal-card {
background: #fff;

18
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue

@ -44,7 +44,17 @@
<div class="info-row">
<div class="label">计划时间:</div>
<div class="value">{{ item.planDate || '-' }} {{ getPlanTimeText(item.planTime) }}</div>
<div class="value">{{ formatPlanDate(item.planDate) }} {{ getPlanTimeText(item.planTime) }}</div>
</div>
<div class="info-row">
<div class="label">执法人员:</div>
<div class="value">{{ item.enforceUserNames || '-' }}</div>
</div>
<div class="info-row">
<div class="label">联系电话:</div>
<div class="value">{{ item.enforceUserTels || '-' }}</div>
</div>
<div class="info-row">
@ -124,6 +134,12 @@ const getPlanTimeText = (time) => {
return map[time] || '';
};
// 格式化计划日期(只取年月日,去掉时分秒)
const formatPlanDate = (dateStr) => {
if (!dateStr) return '-';
return String(dateStr).substring(0, 10);
};
// 格式化日期
const formatDate = (dateStr) => {
if (!dateStr) return '-';

Loading…
Cancel
Save