Flowable7.x学习笔记(十六)分页查询我的待办

前言

        我的待办具体区分为3种情况,第一个就是办理人指定就是我,我可以直接审批;第二种就是我是候选人,我需要先拾取任务然后再办理;第三种是我是候选组,我需要切换到指定的角色去拾取任务再办理。如果任务已经拾取过,应该还要支持归还任务。

        接下来我从动作解析以及代码实现完整的实现整体的功能,需要说明的是文章中代码只有比较核心的代码,完整代码我会在文章结尾标明gitee仓库地址和分支。

一、任务拾取和归还

① 拾取

        拾取操作将某个任务从候选人/候选组池中分配给具体用户,使其成为该任务的受理人,认领后它将从候选池中移除。

        组任务分配:对于指定了“候选组”而非“受理人”的用户任务,不同用户可以竞相拾取,完成后再进行归还或流转。

        防止重复处理:一旦某人认领,其他候选人即不能再拾取,避免重复处理同一任务。

        工作量可见性:通过查询已认领任务,可以统计各人的工作量和进度。

② 归还

        归还操作则是将已认领的任务放回到候选池,取消当前用户的受理人身份,使任务重新回到候选组或候选用户队列中。

        任务重分配:当拾取任务的用户无法继续处理(例如休假、权限不足等),可归还让其他候选人再拾取。

        防止任务滞留:若一人长时间未处理,可自动或手动归还以避免流程阻塞。

        动态负载均衡:系统或管理员可根据当前负载情况,将任务归还后由空闲人员重新拾取,提升整体吞吐。

二、Feign接口

        由于我把Flowable单独设置为一个模块,所有查询用户信息比如角色列表,用户具体信息这些需要从别的模块通过feign接口rpc远程调用,如果架构和我一样可以参考,如果是单体服务可以直接本服务调用。

① 定义远程接口

        这里是要有两个接口,一个是根据用户ID查询角色ID列表;一个是根据用户ID查询用户信息;后续在查询我的待办任务时候会用到。

/*** 根据用户ID查询角色ID列表** @param userId 用户ID,可选参数,用于查询角色ID列表* @return 返回一个Result对象,其中包含用户的角色ID列表*/
@PostMapping("/systemClient/api/v1/user/queryRoleIdsByUserId")
Result<List<Long>> queryRoleIdsByUserId(@RequestParam(required = false , name = "userId") Long userId);/*** 根据用户ID查询用户信息** @param userId 用户ID,作为查询条件,用于定位特定的用户信息* @return 返回一个Result对象,其中包含查询到的Oauth2BasicUserVO用户信息*/
@PostMapping("/systemClient/api/v1/user/queryUserById")
Result<Oauth2BasicUserVO> queryUserById(@RequestParam(required = false , name = "userId") Long userId);

② 远程服务实现远程接口

Ⅰ 定义接口

/*** 根据用户ID查询角色ID列表** @param userId 用户ID,可选参数,如果未提供,则默认为null* @return 返回一个Result对象,其中包含角色ID列表*/
@PostMapping("/queryRoleIdsByUserId")
public Result<List<Long>> queryRoleIdsByUserId(@RequestParam(required = false, name = "userId") Long userId) {try {// 查询所有未删除的角色信息List<Long> roleIds = sysRoleService.queryRoleIdsByUserId(userId);// 返回成功结果return Result.success(roleIds);} catch (Exception e) {// 记录错误日志并返回错误结果log.error("查询所有角色失败,失败原因:{}", e.getMessage(), e);return Result.error("查询所有角色失败,失败原因:" + e.getMessage());}
}/*** 根据用户ID查询用户信息** @param userId 用户ID,可选参数,用于指定要查询的用户* @return 返回一个Result对象,其中包含查询到的用户信息*/
@PostMapping("/queryUserById")
public Result<Oauth2BasicUserVO> queryUserById(@RequestParam(required = false, name = "userId") Long userId) {try {// 查询所有未删除的角色信息Oauth2BasicUser oauth2BasicUser = oauth2BasicUserService.getById(userId);// 使用 Oauth2BasicUserStructMapper 转换输出结果Oauth2BasicUserVO oauth2BasicUserVO =Oauth2BasicUserStructMapper.INSTANCE.toApiVO(oauth2BasicUser);return Result.success(oauth2BasicUserVO);} catch (Exception e) {// 记录错误日志并返回错误结果log.error("根据用户ID查询用户信息失败,失败原因:{}", e.getMessage(), e);return Result.error("根据用户ID查询用户信息失败,失败原因:" + e.getMessage());}
}

Ⅱ 定义接口的服务

/*** 根据用户ID查询角色ID列表** @param userId 用户ID,用于查询角色信息* @return 返回一个包含用户所拥有的角色ID的列表如果用户没有关联任何角色,则返回空列表*/
List<Long> queryRoleIdsByUserId(Long userId);

Ⅲ 实现接口的服务

/*** 根据用户ID查询角色ID清单** @param userId 用户ID,用于查询角色ID* @return 用户的角色ID清单,如果用户没有角色或查询失败,则返回空列表*/
@Override
public List<Long> queryRoleIdsByUserId(Long userId) {// 参数判空if (userId == null) {return List.of();}try {// 根据用户ID查询用户角色绑定关系,并只获取角色ID字段List<SysUserRole> sysUserRoles = sysUserRoleService.lambdaQuery().select(SysUserRole::getRoleId)  // 只查询需要的字段.eq(SysUserRole::getUserId, userId).list();// 返回用户角色ID清单return sysUserRoles.stream().map(SysUserRole::getRoleId).toList();} catch (Exception e) {log.error("根据用户ID查询用户角色ID清单失败,失败原因:", e);}// 如果查询过程中出现异常,返回空列表return List.of();
}

三、我的待办接口

① 定义请求参数

        这里只需要简单的分页信息即可,我的信息直接从当前登陆人的session种获取。

package com.ceair.entity.request;import lombok.Data;import java.io.Serial;
import java.io.Serializable;/*** @author wangbaohai* @ClassName PageReq* @description: 分页请求参数* @date 2025年02月16日* @version: 1.0.0*/
@Data
public class PageReq implements Serializable {@Serialprivate static final long serialVersionUID = 1L;/*** 分页查询的页码和每页大小。** pageNo: 当前页码,默认为1。* pageSize: 每页显示的记录数,默认为10。*/private Long current = 1L;private Long size = 10L;}

② 定义响应参数

        每个参数的定义请参考代码中的字段注释吧,也不是页面都要展示,不过多一些以防后续要用。

package com.ceair.entity.vo;import lombok.Data;import java.io.Serial;
import java.io.Serializable;
import java.util.List;/*** @author wangbaohai* @ClassName TaskListInfoVO* @description: 任务列表信息VO* @date 2025年04月30日* @version: 1.0.0*/
@Data
public class TaskListInfoVO implements Serializable {@Serialprivate static final long serialVersionUID = 1L;// 任务清单List<TaskVO> taskList;// 任务总数Long taskCount;}

package com.ceair.entity.vo;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.flowable.variable.api.persistence.entity.VariableInstance;
import org.springframework.format.annotation.DateTimeFormat;import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Map;/*** @author wangbaohai* @ClassName TaskVO* @description: 任务实体VO* @date 2025年04月30日* @version: 1.0.0*/
@Data
public class TaskVO implements Serializable {@Serialprivate static final long serialVersionUID = 1L;// 任务编号private String taskId;// 任务执行编号private String executionId;// 任务名称private String taskName;//  任务Keyprivate String taskDefKey;// 任务执行人Idprivate String assigneeId;// 任务执行人名称private String assigneeName;// 流程发起人Idprivate String startUserId;// 流程发起人名称private String startUserName;// 流程类型private String category;// 流程变量信息private Object procVars;// 局部变量信息private Object taskLocalVars;// 流程部署编号private String deployId;// 流程IDprivate String procDefId;// 流程keyprivate String procDefKey;// 流程定义名称private String procDefName;// 流程定义内置使用版本private int procDefVersion;// 流程实例IDprivate String procInsId;// 历史流程实例IDprivate String hisProcInsId;// 任务耗时private String duration;// 候选执行人private String candidate;// 关联的流程变量信息private Map<String, VariableInstance> variableInstances;private Map<String, Object> variables;// 任务发起时间@DateTimeFormat(pattern = "yyyy-MM-dd")@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")private LocalDateTime startTime;//任务创建时间@DateTimeFormat(pattern = "yyyy-MM-dd")@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")private LocalDateTime createTime;//任务完成时间@DateTimeFormat(pattern = "yyyy-MM-dd")@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")private LocalDateTime finishTime;// 审批人当前操作的标识 0:审批 1:拾取 2:审批或者归还private Integer status;}

③ 定义功能接口

package com.ceair.controller;import com.ceair.entity.request.PageReq;
import com.ceair.entity.result.Result;
import com.ceair.entity.vo.TaskListInfoVO;
import com.ceair.service.IMayTaskService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author wangbaohai* @ClassName MyTaskController* @description: 任务信息相关接口* @date 2025年05月01日* @version: 1.0.0*/
@RestController
@RequestMapping("/api/v1/myTask")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "我的任务信息管理", description = "我的任务信息相关接口")
public class MyTaskController {private final IMayTaskService mayTaskService;/*** 分页查询我的待办任务。* <p>* 权限控制:需要拥有 '/api/v1/task/myTodoTask' 权限才能访问。* 接口参数:分页请求对象(pageReq),用于指定分页信息。* 接口用途:在API文档中标识该接口的功能为“分页查询我的待办任务”。* 请求方式:POST 请求,路径为 "/myTodoTask"。** @param pageReq 分页请求对象,包含分页参数(页码、页大小等)。* @return Result<TaskListInfoVO> 返回封装后的分页任务列表信息。*/@PreAuthorize("hasAnyAuthority('/api/v1/myTask/myTodoTask')")@Parameter(name = "pageReq", description = "分页请求对象", required = true)@Operation(summary = "分页查询我的待办任务")@PostMapping("/myTodoTask")public Result<TaskListInfoVO> myTodoTask(@RequestBody PageReq pageReq) {try {// 调用业务层查询我的待办任务TaskListInfoVO taskListInfoVO = mayTaskService.myTodoTask(pageReq);// 返回封装后的分页任务列表信息return Result.success(taskListInfoVO);} catch (Exception e) {log.error("查询我的待办任务失败,原因:{}", e.getMessage());return Result.error("查询我的待办任务失败,原因:" + e.getMessage());}}}

④ 定义服务接口

⑤ 实现服务接口

        这块其实就是本文最核心的代码,其中要使用到Flowable引擎的TaskQuery创建查询工具实现查询功能,另外需要使用Flowable引擎的RepositoryService工具查询流程定义数据补充任务信息,以及需要使用Flowable引擎的HistoryService工具查询流程的发起人信息。

        其中查询当前用户的角色id列表和用户名称都需要用到我们第二步创建的feign接口实现。

package com.ceair.service.impl;import cn.hutool.core.date.DateUtil;
import com.ceair.api.SystemFeignClient;
import com.ceair.entity.model.UserInfo;
import com.ceair.entity.request.PageReq;
import com.ceair.entity.result.Result;
import com.ceair.entity.vo.Oauth2BasicUserVO;
import com.ceair.entity.vo.TaskListInfoVO;
import com.ceair.entity.vo.TaskVO;
import com.ceair.exception.BusinessException;
import com.ceair.service.IMayTaskService;
import com.ceair.util.UserInfoUtils;
import io.micrometer.common.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.springframework.stereotype.Service;import java.util.*;
import java.util.stream.Collectors;/*** @author wangbaohai* @ClassName MayTaskServiceImpl* @description: 我的任务信息接口实现* @date 2025年05月01日* @version: 1.0.0*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MayTaskServiceImpl implements IMayTaskService {private final RepositoryService repositoryService;private final SystemFeignClient systemFeignClient;private final TaskService taskService;private final HistoryService historyService;// 用户工具private final UserInfoUtils userInfoUtils;/*** 查询当前用户代办的任务列表** @param pageReq 分页请求对象,包含当前页码和每页大小* @return 返回包含任务列表和总任务数的TaskListInfoVO对象* @throws IllegalArgumentException 如果分页信息为空,则抛出此异常* @throws BusinessException        如果用户未登录或查询角色ID失败,则抛出此异常*/@Overridepublic TaskListInfoVO myTodoTask(PageReq pageReq) {try {// 初始化TaskListInfoVO taskListInfoVO = new TaskListInfoVO();// 分页信息判空if (pageReq == null) {log.error("查询我的待办任务失败,原因:分页信息不能为空");throw new IllegalArgumentException("查询我的待办任务失败,原因:分页信息不能为空");}// 获取分页信息,如果不存在默认查询第一页,每页10条数据long current = (Objects.nonNull(pageReq.getCurrent()) && pageReq.getCurrent() > 0) ? pageReq.getCurrent() :1L;long size = (Objects.nonNull(pageReq.getSize()) && pageReq.getSize() > 0) ? pageReq.getSize() : 10L;// 获取当前用户UserInfo userInfo = userInfoUtils.getUserInfoFromAuthentication();if (userInfo == null) {log.error("查询我的待办任务失败,原因:用户未登录");throw new BusinessException("查询我的待办任务失败,原因:用户未登录");}// 缓存用户ID字符串形式,避免重复调用String userIdStr = userInfo.getId().toString();String account = userInfo.getAccount();// 通过 feign 接口获取当前用的角色ID清单Collection<String> roleIds;Result<List<Long>> roleResult = systemFeignClient.queryRoleIdsByUserId(userInfo.getId());if (roleResult.getCode() != 200 || roleResult.getData() == null) {log.warn("查询我的待办任务失败,原因:{}", roleResult.getMessage());roleIds = Collections.emptyList();} else {roleIds = roleResult.getData().stream().map(String::valueOf).collect(Collectors.toList());}// 设置查询工具(需要查询激活的,办理人/候选人/候选组是我的任务)TaskQuery taskQuery = taskService.createTaskQuery().active().or().taskAssignee(userIdStr).taskCandidateUser(userIdStr);if (!roleIds.isEmpty()) {taskQuery.taskCandidateGroupIn(roleIds);}taskQuery.endOr().orderByTaskCreateTime().desc();// 查询工具设置分页属性并且实行查询动作List<Task> tasks = taskQuery.listPage((int) ((current - 1) * size), (int) (current * size));// 记录待办任务数量long count = taskQuery.count();// 初始化结果数据listList<TaskVO> taskVOS = new ArrayList<>();// 翻译任务数据tasks.stream().filter(Objects::nonNull).forEach(task -> {TaskVO taskVO = new TaskVO();taskVO.setTaskId(task.getId());taskVO.setExecutionId(task.getExecutionId());taskVO.setTaskName(task.getName());taskVO.setProcDefId(task.getProcessDefinitionId());taskVO.setTaskDefKey(task.getTaskDefinitionKey());taskVO.setAssigneeId(task.getAssignee());taskVO.setAssigneeName(StringUtils.isBlank(task.getAssignee()) ? "" : account);// 查询确认 流程定义数据ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();if (processDefinition != null) {taskVO.setProcDefName(processDefinition.getName());taskVO.setProcDefKey(processDefinition.getKey());taskVO.setProcInsId(task.getProcessInstanceId());}// 查询确认 流程发起人HistoricProcessInstance historicProcessInstance =historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();if (historicProcessInstance != null&& StringUtils.isNotBlank(historicProcessInstance.getStartUserId())) {// 确认 流程发起人String startUserId = historicProcessInstance.getStartUserId();taskVO.setStartUserId(startUserId);// 确认流程发起时间taskVO.setStartTime(DateUtil.toLocalDateTime(historicProcessInstance.getStartTime()));// 通过feign接口使用用户ID获取用户信息Result<Oauth2BasicUserVO> userResult =systemFeignClient.queryUserById(Long.valueOf(startUserId));if (userResult.getCode() == 200 && userResult.getData() != null) {taskVO.setStartUserName(userResult.getData().getName());}}// 确认任务对于当前办理人是需要办理还是拾取还是归还taskVO.setStatus(judgeStatus(task.getProcessDefinitionId(), task.getTaskDefinitionKey(),task.getAssignee()));// 集成封装taskVOS.add(taskVO);});// 返回结果taskListInfoVO.setTaskCount(count);taskListInfoVO.setTaskList(taskVOS);return taskListInfoVO;} catch (IllegalArgumentException e) {log.error("查询我的待办任务失败,原因:参数错误", e);throw new BusinessException("查询我的待办任务失败,原因:参数错误", e);} catch (BusinessException e) {log.error("查询我的待办任务失败,原因:业务异常", e);throw new BusinessException("查询我的待办任务失败,原因:业务异常", e);} catch (Exception e) {log.error("查询我的待办任务失败,原因:未知异常", e);throw new BusinessException("查询我的待办任务失败,原因:未知异常", e);}}/*** 根据流程定义ID、任务定义键和指定的办理人判断任务状态** @param processDefinitionId 流程定义ID,用于识别特定的业务流程* @param taskDefinitionKey   任务定义键,用于在流程中定位特定的任务* @param assignee            指定的办理人,用于判断任务的当前状态* @return 返回任务的状态代码:0-审批,1-拾取,2-审批或归还;如果无法判断状态,则返回null*/private Integer judgeStatus(String processDefinitionId, String taskDefinitionKey, String assignee) {// 参数校验if (StringUtils.isBlank(processDefinitionId) || StringUtils.isBlank(taskDefinitionKey)) {return null;}try {// 获取 BpmnModel 对象BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);Process mainProcess = bpmnModel != null ? bpmnModel.getMainProcess() : null;if (mainProcess == null) {return null;}Collection<FlowElement> flowElements = mainProcess.getFlowElements();// 查找目标用户任务return flowElements.stream().filter(Objects::nonNull).filter(flowElement -> flowElement instanceof UserTask).map(flowElement -> (UserTask) flowElement).filter(userTask -> taskDefinitionKey.equals(userTask.getId())).findFirst().map(userTask -> {if (!StringUtils.isBlank(userTask.getAssignee())) {// 流程节点指定办理人:审批状态return 0; // 审批} else {if (StringUtils.isBlank(assignee)) {// 未指定实际办理人:拾取状态return 1; // 拾取} else {// 已有实际办理人:审批/归还状态return 2; // 审批或归还}}}).orElse(null);} catch (Exception e) {log.error("判断流程状态失败,具体原因为: {}", e.getMessage(), e);return null;}}}

四、我的待办界面

① 定义前端实体

// 任务实体 VO
export interface TaskVO {taskId: string // 任务编号 (Java String)executionId: string // 任务执行编号taskName: string // 任务名称taskDefKey: string // 任务 KeyassigneeId: string // 任务执行人 IdassigneeName: string // 任务执行人名称startUserId: string // 流程发起人 IdstartUserName: string // 流程发起人名称category: string // 流程类型procVars: any // 流程变量信息 (Java Object)taskLocalVars: any // 局部变量信息deployId: string // 流程部署编号procDefId: string // 流程定义 IDprocDefKey: string // 流程 KeyprocDefName: string // 流程定义名称procDefVersion: number // 流程定义版本号 (Java int)procInsId: string // 流程实例 IDhisProcInsId: string // 历史流程实例 IDduration: string // 任务耗时 (Java String)candidate: string // 候选执行人variableInstances: Record<string, any> // 关联的流程变量信息 (Java Map<String,VariableInstance>)variables: Record<string, any> // 关联的流程变量 (Java Map<String,Object>)startTime: string // 任务发起时间 (Java LocalDateTime → string)createTime: string // 任务创建时间finishTime: string // 任务完成时间status: number // 审批人当前操作的标识 (Java Integer)
}// 任务列表信息 VO
export interface TaskListInfoVO {taskList: TaskVO[] // 任务清单 (Java List<TaskVO>)taskCount: number // 任务总数 (Java Long → number)
}export interface PageReq {current: number // 当前页码,后端默认 1size: number // 每页记录数,后端默认 10
}

② 封装前端接口

import type { PageReq } from './taskType'
import request from '@/utils/http/request'/*** 分页查询我的待办任务*/
export function getMyTaskPage(data: PageReq) {return request.post<any>({url: '/pm-process/api/v1/myTask/myTodoTask',data,})
}

③ 绘制页面

        界面里包含了一个列表,列表里的操作列有3个按钮,但是会根据数据的状态动态调整是否展示。

<script lang="ts" setup>
import type { TaskVO } from '@/api/task/taskType'
import { getMyTaskPage } from '@/api/task/taskApi'
import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'// 定义当前页码
const currentPage = ref<number>(1)
// 默认页行数
const pageSize = ref<number>(10)
// 数据总数
const total = ref<number>(0)
// 定义响应式数据 myTaskList,用于存储我的任务列表数据
const myTaskList = ref<TaskVO[]>([])
// 表格列定义
const tableColumns = [{ label: '#', type: 'index', align: 'center', width: '50px' },{ label: '任务编号', prop: 'taskId', align: 'center' },{ label: '任务名称', prop: 'taskName', align: 'center' },{ label: '任务执行人名称', prop: 'assigneeName', align: 'center' },{ label: '流程发起人名称', prop: 'startUserName', align: 'center' },{ label: '任务发起时间', prop: 'startTime', align: 'center' },{ label: '流程定义名称', prop: 'procDefName', align: 'center' },{ label: '流程实例ID', prop: 'procInsId', align: 'center' },{ label: '操作', align: 'center', width: '200px' },
]onMounted(() => {// 初始化分页参数并加载第一页任务数据currentPage.value = 1 // 设置当前页为第一页pageSize.value = 10 // 每页展示10条任务记录getMyTaskPageData() // 调用获取我的任务分页数据的方法
})/*** 异步函数:获取我的任务列表数据* 该函数通过调用后端接口,获取当前用户的任务列表,并根据分页参数进行数据更新*/
async function getMyTaskPageData() {try {// 设置分页参数const pageReq = {current: currentPage.value,size: pageSize.value,}// 调用接口获取我的任务列表数据const result: any = await getMyTaskPage(pageReq)// 如果接口调用成功且返回的状态码为200,则更新数据if (result.success && result.code === 200) {// 更新数据myTaskList.value = result.data.taskList// 收集数据总数total.value = result.data.taskCount}else {// 如果接口调用失败,显示错误提示信息ElMessage({message: '查询失败',type: 'error',})}}catch (error) {// 捕获异常并提取错误信息let errorMessage = '未知错误'if (error instanceof Error) {errorMessage = error.message}// 显示操作失败的错误提示信息ElMessage({message: `查询失败: ${errorMessage || '未知错误'}`,type: 'error',})}
}/*** 处理页面数据函数* 本函数用于重新获取当前页面所需的数据* 它通过调用 getMyTaskPageData 函数来实现数据的重新加载*/
function handerPageData() {// 重新加载数据getMyTaskPageData()
}
</script><template><el-table style="margin: 10px 0px;" :border="true" :data="myTaskList"><!-- ID 区域 --><el-table-column type="selection" align="center" width="50px" /><!-- 表格数据 区域 --><el-table-columnv-for="(column, index) in tableColumns":key="index":type="column.type":label="column.label":prop="column.prop":align="column.align":width="column.width"><!-- 使用单个 template 包裹所有条件 --><template #default="scope"><!-- 判断是否是操作列 --><div v-if="column.label === '操作'"><el-button v-if="scope.row.status === 0 || scope.row.status === 2" type="primary">审批</el-button><el-button v-if="scope.row.status === 1" type="primary">拾取</el-button><el-button v-if="scope.row.status === 2" type="primary">归还</el-button></div></template></el-table-column></el-table><!-- 分页器 --><el-paginationv-model:current-page="currentPage"v-model:page-size="pageSize":page-sizes="[10, 20, 30, 40, 50]"layout="prev, pager, next, jumper,->, sizes, total":total="Math.max(total, 0)"@current-change="getMyTaskPageData"@size-change="handerPageData"/>
</template><style scoped></style>

五、增加菜单以及按钮权限

六、功能验证

① 指定办理人的场景

        这种场景我们应该点亮办理按钮

        验证下来是ok的,能看到任务并且任务是直接审批的。

② 指定候选人的场景

        这种场景我们应该只能点亮拾取按钮

        验证下来是ok的,能看到任务并且任务是可拾取的。

③ 指定候选组的场景

        admin的角色是超级管理员

        验证下来是ok的,能看到任务并且任务是可拾取的。

七、后记

        任务的归还点亮需要完整拾取功能,这个我在下一个文章中再来实现吧,本文的完整代码仓库地址请查看专栏第一篇文章的说明。

本文的后端分支是 process-9

本文的前端分支是 process-11

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/78076.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EBO的使用

EBO 其实就是个索引&#xff0c;绑定在相应的VAO中&#xff0c;用来描述绘制顺序。比如在OpenGL绘制三角形的时候&#xff0c;假设有四个顶点&#xff0c;我称他们分别为1&#xff0c;2&#xff0c;3&#xff0c;4号顶点&#xff0c;常规绘制三角形函数是按三个点为一组&#x…

界面控件DevExpress WPF v25.1预览 - AI功能增强(语义搜索)

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

零基础做自动驾驶集成测试(仿真)

图 1&#xff1a;使用 GPUDrive 进行极快的多代理模拟。上图&#xff1a;GPUDrive 中 Waymo Open Motion Dataset 场景的鸟瞰图&#xff0c;方框表示受控智能体&#xff0c;圆圈表示其目标。底部&#xff1a;相应的代理视图&#xff0c;以一个代理为中心。可以根据用户的目标轻…

EasyRTC嵌入式音视频实时通话SDK技术,打造低延迟、高安全的远程技术支持

一、背景 在当今数字化时代&#xff0c;远程技术支持已成为解决各类技术问题的关键手段。随着企业业务的拓展和技术的日益复杂&#xff0c;快速、高效地解决远程设备与系统的技术难题变得至关重要。EasyRTC作为一款高性能的实时通信解决方案&#xff0c;为远程技术支持提供了创…

【C语言常用字符串解析】

总结一下在 C 语言中用于字符串解析&#xff08;特别是从文件中读取行并提取数据&#xff09;的常用函数、 核心任务&#xff1a; 通常是从文件中读取一行文本&#xff08;一个字符串&#xff09;&#xff0c;然后从这个字符串中提取出需要的数据&#xff08;比如数字、单词等…

SpringTas定时任务使用详解

文章目录 Spring Task概述1、环境配置2.注解实现定时任务2.注解实现定时任务4. cron表达式详解&#xff1a; Spring Task概述 在开发中&#xff0c;我们经常会用到定时任务&#xff0c;而Spring Task 则是Spring提供的定时任务框架。 其它定时任务实现框架又jdk自带Timer和Qua…

数字智慧方案6172丨智慧医院扩建信息化整体规划方案(60页PPT)(文末有下载方式)

资料解读&#xff1a;智慧医院扩建信息化整体规划方案 详细资料请看本解读文章的最后内容。 在信息技术飞速发展的当下&#xff0c;医疗行业的信息化建设成为提升医疗服务水平、优化医院管理的关键路径。这份智慧医院扩建信息化整体规划方案&#xff0c;针对医院扩建过程中的信…

ts全局导入接口

为了在项目中全局导入 ITableColumn 接口&#xff0c;避免每次使用时手动导入&#xff0c;可以通过以下步骤实现&#xff1a; 1. 全局导入的实现方式 在 Vue 项目中&#xff0c;可以通过在 src 目录下创建一个 global.d.ts 文件&#xff0c;将 ITableColumn 接口声明为全局类型…

汽车启动原理是什么?

好的&#xff01;同学们&#xff0c;今天我们来讨论汽车的启动原理&#xff0c;重点分析其中的动力来源和摩擦力作用。我会结合物理概念&#xff0c;用尽量直观的方式讲解。 1. 汽车为什么会动&#xff1f;——动力的来源 汽车发动机&#xff08;内燃机或电动机&#xff09;工…

【音频】Qt6实现MP3播放器

1、简介 解码MP3有很多种方法,比如:FFmpeg、GStreamer、Qt、libmpg123 库等,下面介绍使用,只使用Qt的接口方法解码、播放MP3。 开发配置: 1)操作系统:Windows11 2)Qt版本:Qt6.5.1 3)编译器:MinGW_64 2、获取音频输出设备 QMediaDevices 用于获取媒体设备,包括音…

【Linux】VSCode用法

描述 部分图片和经验来源于网络&#xff0c;若有侵权麻烦联系我删除&#xff0c;主要是做笔记的时候忘记写来源了&#xff0c;做完笔记很久才写博客。 专栏目录&#xff1a;记录自己的嵌入式学习之路-CSDN博客 目录 1 安装环境及运行C/C 1.1 安装及配置步骤 1.2 运…

WPF之RadioButton控件详解

文章目录 一、RadioButton简介二、RadioButton的基本用法1. 创建基本的RadioButton2. 分组管理3. 设置默认选中 三、RadioButton的重要属性和事件1. 关键属性2. 主要事件3. 事件处理流程4. 监听选中状态变化 四、数据绑定与RadioButton1. 基本数据绑定2. 数据绑定流程3. 使用枚…

笔试专题(十三)

文章目录 kotori和气球题解代码 走迷宫题解代码 主持人调度&#xff08;二&#xff09;&#xff08;难题&#xff09;题解代码 kotori和气球 题目链接 题解 1. 数学-排列组合 2. 每次乘完之后对109取模 代码 #include<iostream>using namespace std;int main() {int…

图形图像基础知识(1)---- RGB/YUV 颜色格式

目录 常见RAW颜色格式RGB类型YUV类型YUV类型汇总YUV444类型YUV422类型YUV420类型 参考实例 常见RAW颜色格式 RGB 类型&#xff1a; ARGB1010102&#xff0c;ABGR1010102&#xff0c;BGRA1010102&#xff0c;RGBA1010102 ARGB8888&#xff0c;ABGR8888&#xff0c;BGRA8888&…

CentOS7.9安装OpenSSL 1.1.1t和OpenSSH9.9p1

一、临时开启telnet登录方式&#xff0c;避免升级失败无法登录系统 &#xff08;注意telnet登录方式存在安全隐患&#xff0c;升级openssh相关服务后要记得关闭&#xff09; 1.安装telnet服务 yum -y install xinetd telnet* 2.允许root用户通过telnet登陆&#xff0c;编辑…

使用DCI和RTIT技术进行精准调优--看录像

使用DCI和RTIT技术进行精准调优_哔哩哔哩_bilibili 每次看录像都记录一下。 PT/RTIT简介 DCI技术即通过USB3.0接口去访问调试目标机的JTAG组件&#xff0c;凡是运行在CPU上的代码均可以进行调试&#xff0c;这就没有了使用WinDBG调试时&#xff0c;会出现的不能调试敏感代码…

从MCP基础到FastMCP实战应用

MCP(https://github.com/modelcontextprotocol) MCP&#xff08;模型上下文协议&#xff09; 是一种专为 基于LLM的工具调用外部工具而设计的协议 &#xff0c; 本质上是 LLM ↔ 工具之间的RPC&#xff08;远程过程调用&#xff09; 的一种安全且一致的处理方式&#xff0c; 是…

深入理解C语言中的整形提升与算术转换

深入理解C语言中的整形提升与算术转换 一.整形提升&#xff1a;概念与原理 在C语言中&#xff0c;整形提升&#xff08;Integer Promotion&#xff09;是一个重要但容易被忽视的概念。它指的是在表达式中&#xff0c;任何小于int类型的整型&#xff08;如char、short&#xf…

编程题python常用技巧-持续

1.字典 1.1排序 在Python中&#xff0c;要按照字典的值进行排序&#xff0c;可以按照以下步骤操作&#xff1a; 方法说明 ‌获取键值对列表‌&#xff1a;使用 dict.items() 获取字典的键值对视图。‌排序键值对‌&#xff1a;使用 sorted() 函数&#xff0c;并通过 key 参…

硬件工程师面试常见问题(11)

第五十一问&#xff1a;器件手册的翻译题目 要学英语啊&#xff0c;孩子。 第五十二问&#xff1a;二极管三极管常识题 1.二极管的导通电压一般是 0.7V 2.MOS管根据掺杂类型可以分为 NMOS和PMOS 3.晶体三极管在工作时,发射结和集电结均处于正向偏置,该晶体管工作在一饱和态。…