当前位置: 首页 > news >正文

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

相关文章:

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

前言 我的待办具体区分为3种情况&#xff0c;第一个就是办理人指定就是我&#xff0c;我可以直接审批&#xff1b;第二种就是我是候选人&#xff0c;我需要先拾取任务然后再办理&#xff1b;第三种是我是候选组&#xff0c;我需要切换到指定的角色去拾取任务再办理。如果任务已…...

Annotate better with CVAT

WIN10 配置标注环境 WSL + Docker Desktop 安装手册 https://docs.cvat.ai/docs/administration/basics/installation/ hebing@hello:~$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE cvat/ui …...

QML Image 组件详解

目录 引言相关阅读QML Image元素基础知识 项目结构示例解析1. 本地资源图像加载2. 网络图像加载3. 图像填充模式 应用主结构 总结下载链接 引言 本文将介绍QML中Image元素的基本用法和关键特性&#xff0c;包括加载本地资源图像、处理网络图像、以及调整图像的填充模式。通过一…...

BOFZ 緩衝區溢出shell脚本檢測工具

地址:https://github.com/MartinxMax/bofz BOFZ BOFZ 是一款簡單的緩衝區溢出掃描器&#xff0c;旨在檢測指定可執行文件中的緩衝區溢出漏洞。 此工具可用於快速測試應用程序或二進制文件中是否存在常見的安全缺陷&#xff0c;特別是那些由於對用戶輸入處理時邊界檢查不當而引…...

【Dify系列教程重置精品版】第五章:Dify配置Ollama

上一章我们在Dify上尝试配置了“月之暗面”。这一章我们在Dify上配置另一个模型“Ollama”。 什么是ollama呢?简单来说:它允许用户在个人计算机或服务器上快速部署和管理多种开源大语言模型,如 Llama3、Phi3、Gemma2 等,而无需依赖昂贵的云服务或专业的技术背景。 反正就是…...

RISC-V AIA SPEC学习(四)

第五章 Interrupts for Machine andSupervisor Levels 核心内容​​ 1.主要中断类型与默认优先级:​​ 定义了机器级别(M-level)和监管者级别(S-level)的标准中断类型(如MEI、SEI、MTI等)。默认优先级规则:本地中断(如软件/定时器)优先级高于外部中断,RAS事件(如低/高…...

Leetcode刷题报告2——双指针法

文章目录 前言[15. 三数之和](https://leetcode.cn/problems/3sum/)题干题解知识点总结 [42. 接雨水](https://leetcode.cn/problems/trapping-rain-water/)题干题解 前言 这部分总共是4道题&#xff0c;我就挑两道比较典型的题写一下博客吧。 双指针法的核心思路是通过合理的…...

线段树原理和代码详解

目录 线段树维护的信息类型 线段树的结构 线段树的初始化 线段树的功能&#xff1a; 单点修改&#xff0c;区间查询 区间修改&#xff0c;区间查询 以下内容均为个人见解&#xff0c;如有不足还请指出&#xff0c;作者会及时修改&#xff01; 期待大家的点赞、收藏、评论&…...

xray-poc编写示例

禁止未授权扫描和测试行为&#xff01;&#xff01;&#xff01; 1. SQL 时间盲注检测 (Time-Based Blind SQLi) name: generic/time-based-sqli rules:- method: GETpath: "/product?id1 AND (SELECT 1 FROM (SELECT SLEEP(5))a)--"expression: |response.status…...

[2-01-01].前端开发工具

前端学习大纲 一、VsCode: 1.1、下载地址 https://code.visualstudio.com/ 1.2.插件安装 为方便后续开发&#xff0c;建议安装如下插件 1.3.创建项目 先创建一个空的文件夹&#xff0c;如project_xxxx。然后打开vscode&#xff0c;再在vscode里面选择 File -> Open Fol…...

自动化实现web端Google SignUp——selenium

案例&#xff1a;自动化获取Google注册页面——selenium 前言 提示&#xff1a;通过案例掌握selenium语法 涉及技术&#xff1a;Python Selenium 在本文中&#xff0c;我们将通过一个实际案例来学习如何使用Selenium自动化工具模拟Google账号注册流程。这个案例涵盖了Selen…...

如何阅读GitHub上的深度学习项目

一、前期准备&#xff1a;构建知识基础 1. 必备工具与环境 开发工具&#xff1a; IDE&#xff1a;VS Code&#xff08;推荐&#xff0c;轻量化插件丰富&#xff0c;如 Python、PyTorch 插件&#xff09;、PyCharm&#xff08;适合大型项目&#xff09;。版本控制&#xff1a;…...

【LeetCode 热题 100】3.无重复字符的最长子串:详解滑动窗口解法

&#x1f4cc; 原题链接&#xff1a;Longest Substring Without Repeating Characters &#x1f4d6; 一、题目描述 给定一个字符串 s&#xff0c;请你找出其中不含有重复字符的最长子串的长度。 示例&#xff1a; 输入: s "abcabcbb" 输出: 3 解释: 最长不重复子…...

Android12 Rom定制设置默认语言为中文

Android12 Rom定制设置默认语言为中文 1.前言&#xff1a; 最近在做客制化定制时需要默认语言为中文&#xff0c;而且可以切换输入法&#xff0c;之前讲解过在ROM中如何设置默认输入法&#xff0c;这里就不展开了&#xff0c;其实这个需求很简单&#xff0c;就是调试的时候发现…...

【设计模式】GoF设计模式之备忘录模式(Memento Pattern)

设计模式之备忘录模式 Memento Pattern V1.0核心概念角色代码示例程序运行结果代码讲解 适用场景 V1.0 核心概念 备忘录模式的核心是定义一个备忘录类&#xff08;Memento&#xff09;&#xff0c;这个类的实例能够表示发起人类&#xff08;Originator&#xff09;的一种状态…...

springboot分层打包,减少重复构建和传输的开销

在 Spring Boot 中&#xff0c;分层打包&#xff08;Layered Packaging&#xff09; 是一种优化策略&#xff0c;特别针对 容器化部署&#xff08;如 Docker&#xff09; 的场景设计。它的核心思想是将应用的不同部分&#xff08;依赖、资源、代码等&#xff09;划分为独立的层…...

Linux——虚拟地址空间

1.虚拟地址空间 进程地址空间又叫虚拟地址空间 我们大家知道程序在运行时使用的空间被划分为多个不同的区域&#xff0c;每个区域都有不同的作用 正文代码&#xff1a;存放程序的可执行代码 通常都是只读的初始化数据&#xff1a;未初始化数据堆区&#xff1a;用于动态分配内存…...

GPU虚拟化实现(七)

GPU虚拟化实现(七) 章节回顾进程管理资源限制和环境变量利用率监控线程信号处理退出处理代码具体运作流程怎么限制SM的总结章节回顾 在上一章,分析了项目的主要代码模块功能:共享内存和初始化、GPU 内存管理、GPU 利用率管理以及锁机制,在这一章将继续分析其他的代码模块…...

【QNX+Android虚拟化方案】137 - msm-5.4 Kernel U盘 插入中断、枚举、匹配完整流程详解

【QNX+Android虚拟化方案】137 - msm-5.4 Kernel U盘 插入中断、枚举、匹配完整流程详解 1. HUB提交中断URB给HCD控制器,URB完成回调函数为 hub_irq()2. U盘插入后,触发运行 hub_irq() 中断回调函数2.1 高通 DWC3 Host HCD 初始化流程2.2 urb->complete(urb) 中断回调流程…...

分布式锁的几种实现

前几天看一个面试视频&#xff0c;提到了分布式锁一直想写写&#xff0c;但奈何考试太多&#xff0c;直到今天才有时间。好啦&#xff0c;开始今天的文章吧。 一.定义 分布式锁&#xff1a;当多个进程不在同一个系统中(比如分布式系统中控制共享资源访问)&#xff0c;用分布式…...

Android 解绑服务问题:java.lang.IllegalArgumentException: Service not registered

问题与处理策略 问题描述 在 Android 项目中&#xff0c;解绑&#xff08;unbindService()&#xff09;一个服务&#xff08;Service&#xff09;时&#xff0c;报如下错误 java.lang.IllegalArgumentException: Service not registered问题原因 错误表明在解绑服务时&…...

注册登录页面项目

关系型数据库地址&#xff1a;C:\Users\ASUS\AppData\Local\Temp\HuaweiDevEcoStudioDatabases\rdb #注册页面register.ets import dataRdb from ohos.data.rdbconst STORE_CONFIG {name: weather4.db } const TABLE_NAME weather_info const SQL_CREATE_TABLE CREATE TAB…...

从 Python 基础到 Django 实战 —— 数据类型驱动的 Web 开发之旅

主题简介&#xff1a; 本主题以 Python 基础数据类型为核心&#xff0c;结合 Django 框架的开发流程&#xff0c;系统讲解如何通过掌握数字、字符串、列表、元组、字典等基础类型&#xff0c;快速构建功能完善的 Web 应用。通过理论与实践结合&#xff0c;帮助学员从零基础 Py…...

数字智慧方案5971丨智慧农业大数据平台解决方案(59页PPT)(文末有下载方式)

详细资料请看本解读文章的最后内容。 资料解读&#xff1a;智慧农业大数据平台解决方案 在现代农业发展进程中&#xff0c;智慧农业大数据平台解决方案正成为推动农业变革的关键力量。这一方案从项目简介到大数据展示&#xff0c;各个环节紧密相连&#xff0c;致力于为农业发展…...

MOOS-ivp使用(一)——水下机器人系统的入门与使用

MOOS-ivp使用&#xff08;一&#xff09;——水下机器人系统的入门与使用 MOOS-ivp&#xff08;Marine Operational Oceanographic System for Intelligent Vehicle Planning&#xff09;是专为水下机器人&#xff08;如AUV&#xff09;设计的开源框架。类似于ROS&#xff0c;…...

【网络服务器】——回声服务器(echo)

作用 实现回声服务器的客户端/服务器程序&#xff0c;客户端通过网络连接到服务器&#xff0c;并发送任意一串英文信息&#xff0c;服务器端接收信息后&#xff0c;执行数据处理函数&#xff1a;将每个字符转换为大写并回送给客户端显示。 客户端&#xff1a;发送字符信息 服…...

IDEA在项目中添加模块出现Error adding module to project: null(向项目添加模块时出错: null)的解决方法

解决方法 &#xff08;1&#xff09;打开当前项目的结构...

(34)VTK C++开发示例 ---将图片映射到平面

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;VTK开发 &#x1f448; 1. 概述 演示如何将图片作为纹理贴图到一个平面上。 这段代码的功能是使用 VTK&#xff08;Visualization Toolkit&#xff0…...

微软与Meta大幅增加人工智能基础设施投入

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

华为云服务器VoceChat在线聊天室部署

目录 1. 项目介绍2. 准备条件3. Docker环境部署3.1 安装Docker&#xff08;CentOS 7&#xff09;3.2 安装Docker Compose3.3 Docker常用命令 4. 创建配置文件4.1 创建工作目录4.2 创建docker-compose.yml文件4.3 保存配置文件 5. 部署运行5.1 启动服务5.2 检查服务状态5.3 防火…...

ERP系统(技术面)知识积累

本文为本人在准备某公司信息技术类岗位的面试时所作的笔记&#xff0c;该公司有技术面&#xff0c;此岗位入职后负责的是ERP系统的运行和维护&#xff0c;所以可能会问ERP系统相关的问题。故我写此文以做准备。 ERP简介 ERP&#xff0c;全称Enterprise Resource Planning&…...

Python学习笔记(第三部分)

接续 Python.md 文件的第三部分 类 类的创建的基本使用 创建一个类 class Dog(): 文档字符串&#xff1a;这是一次模拟小狗的简单尝试 def __init__(self,name,age):self.name nameself.age agedef sit(self):print(self.name.title() " is now sitting.")def ro…...

【浅尝Java】Java简介第一个Java程序(含JDK、JRE与JVM关系、javcdoc的使用)

&#x1f35e;自我激励&#xff1a;每天努力一点点&#xff0c;技术变化看得见 文章目录 Java语言概述Java是什么Java语言的重要性Java语言发展简史Java语言特性 第一个Java程序main方法示例运行Java程序JDK、JRE、JVM之间的关系注释基本规则注释规范 标识符关键字 Java语言概述…...

【FreeRTOS-列表和列表项】

参照正点原子以及以下gitee笔记整理本博客&#xff0c;并将实验结果附在文末。 https://gitee.com/xrbin/FreeRTOS_learning/tree/master 一、列表和列表项的简介(熟悉) 1、什么是列表 答&#xff1a;列表是FreeRTOS中的一个数据结构&#xff0c;概念上和链表有点类似&#…...

22.2Linux的I2C驱动实验(编程)_csdn

我尽量讲的更详细&#xff0c;为了关注我的粉丝&#xff01;&#xff01;&#xff01; 这里我们用到的是stm32mp157的板子&#xff0c;所以我们看一下I2C用到的引脚。 1、硬件原理图分析 可以看到在这块板子上面用的SDA和SCL总线是PA11,PA12。所以要修改设备树和镜像文件&…...

socket-IO复用技术

五个I/O模型 1、阻塞I/O 2、非阻塞I/O 3、I/O复用&#xff08;select和poll&#xff09; 4、信号驱动I/O 5、异步I/O I/O复用 是一种在单线程或单进程环境下&#xff0c;同时监听多个 I/O 事件的技术。它允许程序高效地处理多个输入输出流&#xff08;如网络套接字、文件描…...

上位机知识篇---二进制操作

文章目录 前言接收数据示例:0xAA 0x12 0x34 0x55合并高/低字节数据RGB565颜色值:0xF800(红色)Python中负数右移接收帧:01 03 02 12 34 CRC前言 本文简单对单片机、上位机中的映射(Mapping)和位移操作符(Bit Shifting)等相关知识进行了简单介绍. 一、单片机与上位机中…...

openEuler 22.03 安装 Mysql 5.7,TAR离线安装

目录 一、检查系统是否安装其他版本Mariadb数据库二、环境检查2.1 必要环境检查2.2 在线安装&#xff08;有网络&#xff09;2.3 离线安装&#xff08;无网络&#xff09; 二、下载Mysql2.1 在线下载2.2 离线下载 三、安装Mysql四、配置Mysql五、开放防火墙端口六、数据备份七、…...

《排序算法总结》

引言&#xff1a; 编程学到现在&#xff0c;我们已经接触了很多种排序算法&#xff0c;这篇文章我就对常见的几种排序算法进行一个小结。 一&#xff1a; 排序算法分类&#xff1a; 二&#xff1a; 插入排序&#xff1a; 直接插入排序&#xff1a; 1. 概念&#xff1a; 直…...

【Java学习笔记】递归

递归&#xff08;recursion&#xff09; 思想&#xff1a;把一个复杂的问题拆分成一个简单问题和子问题&#xff0c;子问题又是更小规模的复杂问题&#xff0c;循环往复 本质&#xff1a;栈的使用 递归的注意事项 &#xff08;1&#xff09;需要有递归出口&#xff0c;否者就…...

体系学习1:C语言与指针1——预定义、进制打印、传参为数组

1、不对一段代码进行编译 #if 0 statement #endif2、输出地址 int d[3]{1,2,3}; printf("%p",(void*)d);//p期待的是void*类型的数据3、不同进制的打印 int data 1200; char hed[9];//为\0预留位置&#xff01;&#xff01;&#xff01; sprintf(hed,"%08X&…...

使用Java正则表达式进行分组与匹配文本提取

在Java开发中&#xff0c;正则表达式&#xff08;Regex&#xff09;是处理字符串的强大工具&#xff0c;广泛应用于数据验证、文本解析和格式转换等场景。通过正则表达式的分组功能&#xff0c;开发者可以精确地提取匹配模式的子部分&#xff0c;而不仅仅是整个匹配内容。Java的…...

RAGFlow上传3M是excel表格到知识库,提示上传的文件总大小过大

环境&#xff1a; Ragflowv0.17.2 问题描述&#xff1a; RAGFlow上传3M是excel表格到知识库&#xff0c;提示上传的文件总大小过大 解决方案&#xff1a; 定位问题&#xff1a; 1.查询Nginx 日志 Nginx 日志 检查 Nginx 配置中日志路径是否正确&#xff0c;确保日志文件有…...

2025年4月文章一览

2025年4月编程人总共更新了30篇文章&#xff1a; 1.2025年3月文章一览 2.《Operating System Concepts》阅读笔记&#xff1a;p528-p544 3.《Operating System Concepts》阅读笔记&#xff1a;p545-p551 4.《Operating System Concepts》阅读笔记&#xff1a;p552-p579 5.…...

2025大模型微调视频课程全套(附下载)

2025大模型微调视频课程全套&#xff0c;共10课。主要内容如下&#xff1a; 1、大模型的发展 2、Transformer & LLMs 3、大模型微调预览&Lora微调&Alpaca模型微调 4、Alpaca&AdaLoRA&QLoRA模型微调 5、Efficient Fine-tuning&Efficient Inference&…...

【Python Web开发】04-Cookie和Session

文章目录 1. Cookie1.1 定义1.2 工作原理1.3 用途1.4 优缺点 2. Session2.1 定义2.2 工作原理2.3 用途2.4 优缺点 3. Cookie 与 Session 的关系4. 安全性考量5. Python 中使用 Cookie 和 Session 在 HTTP 协议里&#xff0c;Cookie 和 Session 是用于管理客户端与服务器之间会话…...

从股指到期指,哪些因素影响基差?

当我们谈论股指期货&#xff08;简称“期指”&#xff09;与股票现货指数&#xff08;简称“股指”&#xff09;的基差时&#xff0c;其实是在探讨期货价格与现货价格之间的“差价”。这个差价受多种因素影响&#xff0c;时而扩大&#xff0c;时而缩小&#xff0c;甚至可能“翻…...

n8n 中文系列教程_15. 【工具篇】n8n中文版与汉化指南:从原理到实践

n8n 作为一款强大的开源自动化工具&#xff0c;目前尚未推出官方中文版&#xff0c;但社区提供了汉化方案。不过&#xff0c;对于技术用户&#xff0c;我们更推荐使用英文原版&#xff0c;以便更好地查阅文档和解决问题。如果你仍希望尝试汉化&#xff0c;本文将详细介绍如何通…...

3D版同步帧游戏

以下是实现一个3D版同步帧游戏的详细步骤与完整代码示例。我们将以第一人称射击游戏(FPS)为原型,重点讲解3D空间中的同步机制优化。 项目升级:3D版核心改动 1. 3D坐标系与消息结构 // common/messages.go type Vector3 struct {X float32 `json:"x"`Y float32 `…...

C语言中数字转化为字符串的方法

C语言中数字转化为字符串的方法 1. 使用 sprintf 函数 这是 stdio.h 头文件中的标准库函数 &#xff0c;功能类似于 printf &#xff0c;但不是输出到控制台&#xff0c;而是将格式化后的内容输出到字符数组&#xff08;字符串&#xff09;中。 示例代码&#xff1a; c #inc…...