This commit is contained in:
那些花儿
2025-07-30 15:19:03 +08:00
parent 65b7960783
commit 73974dbdd3
32 changed files with 1403 additions and 388 deletions
@@ -0,0 +1,32 @@
package com.budwk.app.flow.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.entity.ProcessCategory;
import io.swagger.annotations.Api;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.List;
@IocBean
@At("/flow/category")
@Ok("json:full")
@Api("流程分类")
public class FlowCategoryController {
@Inject
private Dao dao;
@At
@SaCheckLogin
public Result list() {
List<ProcessCategory> list = dao.query(ProcessCategory.class, Cnd.NEW());
return Result.success(list);
}
}
@@ -4,16 +4,20 @@ import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.vo.LabelValueVO;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.flow.engine.event.ProcessEvent;
import com.budwk.app.flow.engine.event.ProcessPublisher;
import com.budwk.app.flow.engine.model.ProcessModel;
import com.budwk.app.flow.engine.model.TaskModel;
import com.budwk.app.flow.entity.Candidate;
import com.budwk.app.flow.entity.ProcessDefine;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.vo.ProcessInstanceVO;
@@ -66,17 +70,9 @@ public class FlowCommonController {
todoTaskVos.add(taskVO);
}
// List<ProcessTaskVO> doneTaskVos = new ArrayList<>();
// List<ProcessTask> doneTaskList = flowEngine.processTaskService().getDoneTaskList(instanceId, null);
// for (ProcessTask doneTask : doneTaskList) {
// ProcessTaskVO taskVO = flowEngine.processTaskService().findById(doneTask.getId());
// doneTaskVos.add(taskVO);
// }
HashMap<String, Object> result = new HashMap<>();
result.put("processInstance", instanceVO);
result.put("todoTasks", todoTaskVos);
// result.put("doneTasks", doneTaskVos);
return Result.success(result);
}
@@ -105,12 +101,20 @@ public class FlowCommonController {
@At("/defineInfo")
@SaCheckLogin
@ApiOperation("流程定义详情")
public Result defineInfo(String defineKey) {
// 通过defineKey获取最新的流程定义
ProcessDefine define = flowEngine.processDefineService().getLastByName(defineKey);
if (define == null) {
return Result.error("未找到指定的流程定义");
public Result defineInfo(String defineKey, Long instanceId) {
ProcessDefine define = null;
if (StrUtil.isNotBlank(defineKey)) {
define = flowEngine.processDefineService().getLastByName(defineKey);
}
if (instanceId != null) {
ProcessInstance instance = dao.fetch(ProcessInstance.class, instanceId);
define = flowEngine.processDefineService().getById(instanceId);
}
if (define == null) {
return Result.error("没有流程定义");
}
return Result.success(define);
}
@@ -170,9 +174,34 @@ public class FlowCommonController {
@At
@SaCheckLogin
@ApiOperation("撤回流程实例")
public Result withdrawInstance(@Param("instanceId") Long instanceId) {
flowEngine.processInstanceService().withdraw(instanceId, SecurityUtil.getUserId());
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("重新发起流程实例")
public Result reissueInstance(@Param("instanceId") Long instanceId, @Param("bizData") String bizData) {
// // 把已撤销流程修改为进行中
// dao.update(ProcessInstance.class, Chain.make("state", ProcessInstanceStateEnum.DOING.getCode()), Cnd.where(ProcessInstance::getId, "=", instanceId));
// Dict args = Dict.create();
// args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.RE_APPLY.getCode());
// args.set(FlowConst.FORM_DATA, bizData);
//
// // 查找第一个提交任务节点
// ProcessInstance instance = dao.fetch(ProcessInstance.class, instanceId);
// ProcessModel processModel = flowEngine.processDefineService().getProcessModel(instance.getProcessDefineId());
//
// args.set(FlowConst.PROCESS_INSTANCE_ID_KEY, instanceId);
// args.set(FlowConst.INITIATOR,instance.getCreatedBy());
//
// // 构建执行参数对象
// Execution execution = new Execution();
// execution.setProcessModel(processModel);
// execution.setProcessInstance(instance);
// execution.setProcessInstanceId(instance.getId());
// execution.setEngine(flowEngine);
// execution.setArgs(args);
// execution.setOperator(SecurityUtil.getUserId());
// // 拿到开始节点模型,调用其execute方法
// processModel.getStart().execute(execution);
//
// return Result.success();
return Result.success();
}
@@ -215,6 +244,10 @@ public class FlowCommonController {
// 会签不同意,追加不同意标识
args.put(FlowConst.COUNTERSIGN_DISAGREE_FLAG, 1);
flowEngine.executeProcessTask(processTaskId, operator, args);
} else if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.RE_APPLY.getCode())) {
// 重新提交
// args.put(FlowConst.FORM_DATA, args.getStr(FlowConst.FORM_DATA));
flowEngine.executeProcessTask(processTaskId, operator, args);
} else {
// 默认执行
flowEngine.executeProcessTask(processTaskId, operator, args);
@@ -222,6 +255,32 @@ public class FlowCommonController {
return Result.success();
}
@At
@SaCheckLogin
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("撤销任务")
public Result revokeTask(@Param("taskId") Long taskId) {
// 1.撤销任务
List<ProcessTask> taskList = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskParentId, "=", taskId));
for (ProcessTask processTask : taskList) {
processTask.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode());
dao.update(processTask);
}
// 2.再次激活自己任务
ProcessTask processTask = dao.fetch(ProcessTask.class, taskId);
processTask.setTaskState(ProcessTaskStateEnum.DOING.getCode());
dao.update(processTask);
// 3.发送任务撤回事件 确保上面执行成功
for (ProcessTask task : taskList) {
ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_TASK_REVOKE).sourceId(task.getId()).build());
}
return Result.success();
}
@At("/candidate")
@SaCheckLogin
@ApiOperation("获取候选用户")
@@ -252,5 +311,13 @@ public class FlowCommonController {
return Result.success();
}
@At("/addCandidate")
@SaCheckLogin
@ApiOperation("代理")
public Result addCandidate(@Param("taskId") Long taskId, @Param("actorIds") String[] actorIds) {
flowEngine.processTaskService().addCandidateActor(taskId, Arrays.asList(actorIds));
return Result.success();
}
}
@@ -122,8 +122,9 @@ public class FlowDesignController {
jsonObject.set("category", processDesign.getCategory());
jsonObject.set("instanceUrl", processDesign.getInstanceUrl());
jsonObject.set("h5InstanceUrl", processDesign.getH5InstanceUrl());
jsonObject.set("instanceViewUrl",processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl",processDesign.getH5InstanceViewUrl());
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
jsonObject.set("icon", processDesign.getIcon());
processDesign.setContent(jsonObject);
dao.insert(processDesign);
return Result.success();
@@ -139,8 +140,9 @@ public class FlowDesignController {
jsonObject.set("category", processDesign.getCategory());
jsonObject.set("instanceUrl", processDesign.getInstanceUrl());
jsonObject.set("h5InstanceUrl", processDesign.getH5InstanceUrl());
jsonObject.set("instanceViewUrl",processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl",processDesign.getH5InstanceViewUrl());
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
jsonObject.set("icon", processDesign.getIcon());
processDesign.setContent(jsonObject);
processDesignService.update(processDesign);
return Result.success();
@@ -156,8 +158,9 @@ public class FlowDesignController {
jsonObject.set("category", processDesign.getCategory());
jsonObject.set("instanceUrl", processDesign.getInstanceUrl());
jsonObject.set("h5InstanceUrl", processDesign.getH5InstanceUrl());
jsonObject.set("instanceViewUrl",processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl",processDesign.getH5InstanceViewUrl());
jsonObject.set("instanceViewUrl", processDesign.getInstanceViewUrl());
jsonObject.set("h5InstanceViewUrl", processDesign.getH5InstanceViewUrl());
jsonObject.set("icon", processDesign.getIcon());
processDesign.setContent(jsonObject);
processDesignService.update(processDesign);
return Result.success();
@@ -43,14 +43,16 @@ public class FlowTodoCenter {
public Result todo(Integer pageNumber, Integer pageSize, String keyword, String categoty) {
Sql sql = Sqls.create("""
SELECT
t.id,
t.id AS taskId,
t.displayName AS taskName,
t.taskState,
t.formKey,
t.createdAt,
t.finishTime,
t.variable,
ins.state AS instanceState
ins.state AS instanceState,
ins.id AS instanceId,
ins.businessNo
FROM
wf_process_task t
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
@@ -76,12 +78,16 @@ public class FlowTodoCenter {
public Result done(Integer pageNumber, Integer pageSize, String keyword, String categoty) {
Sql sql = Sqls.create("""
SELECT
t.id,
t.id AS taskId,
t.displayName AS taskName,
t.taskState,
t.formKey,
t.createdAt,
t.finishTime,
ins.state AS instanceState
t.variable,
ins.state AS instanceState,
ins.id AS instanceId,
ins.businessNo
FROM
wf_process_task t
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
@@ -107,14 +113,14 @@ public class FlowTodoCenter {
public Result started(Integer pageNumber, Integer pageSize, String keyword, String categoty) {
Sql sql = Sqls.create("""
SELECT
ins.id,
ins.id AS instanceId,
ins.processDefineId,
ins.state,
ins.businessNo,
ins.operator,
ins.variable,
ins.createdAt,
GROUP_CONCAT(t.displayName) as taskName
GROUP_CONCAT(DISTINCT t.displayName) as taskName
FROM
wf_process_instance ins
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id and t.taskState = 10
@@ -19,6 +19,7 @@ import com.budwk.app.flow.service.ProcessDefineService;
import com.budwk.app.flow.service.ProcessInstanceService;
import com.budwk.app.flow.service.ProcessTaskService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.ioc.aop.Aop;
import java.util.Collections;
@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
@Data
public class ProcessModel extends BaseModel {
private String type; // 流程定义分类
private String icon; // 流程定义图标
private String category; // 流程定义分类
private String instanceUrl; // 启动实例要填写的表单key
private String h5InstanceUrl; // 启动实例要填写的手机端表单key
@@ -4,15 +4,17 @@ import com.budwk.app.flow.engine.model.BaseModel;
import lombok.Data;
import java.util.List;
/**
*
* logicFlow模型
*
* @author mldong
* @date 2023/4/26
*/
@Data
public class LfModel extends BaseModel {
private String type; // 流程定义分类
private String category;
private String expireTime;// 过期时间(常量或变量)
private String instanceUrl; // 启动实例的url,前后端分离后,定义为路由名或或路由地址
private String h5InstanceUrl;
@@ -78,6 +78,7 @@ public class ModelParser {
processModel.setName(lfModel.getName());
processModel.setDisplayName(lfModel.getDisplayName());
processModel.setType(lfModel.getType());
processModel.setCategory(lfModel.getCategory());
processModel.setInstanceUrl(lfModel.getInstanceUrl());
processModel.setH5InstanceUrl(lfModel.getH5InstanceUrl());
processModel.setInstanceViewUrl(lfModel.getInstanceViewUrl());
@@ -0,0 +1,28 @@
package com.budwk.app.flow.entity;
import com.budwk.app.base.model.BaseModel;
import lombok.Getter;
import lombok.Setter;
import org.nutz.dao.entity.annotation.*;
@Getter
@Setter
@Table("wf_process_category")
@Comment("流程分类")
public class ProcessCategory extends BaseModel {
@Id
@Comment("ID")
private Long id;
@Comment("名称")
@Column
@ColDefine(type = ColType.VARCHAR, width = 10)
private String name;
@Comment("图标")
@Column
@ColDefine(type = ColType.VARCHAR, width = 255)
private String icon;
}
@@ -1,5 +1,6 @@
package com.budwk.app.flow.entity;
import cn.hutool.extra.pinyin.PinyinUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.model.BaseModel;
import lombok.Getter;
@@ -72,4 +73,9 @@ public class ProcessDefine extends BaseModel {
@Column
private Integer version;
@Comment
@Column
@ColDefine(type = ColType.CHAR)
private Character pinyinName;
}
@@ -9,6 +9,9 @@ public enum ProcessEventTypeEnum{
PROCESS_INSTANCE_END(2, "流程实例结束事件"),
PROCESS_TASK_START(3, "流程任务开始事件"),
PROCESS_TASK_END(4, "流程任务结束事件"),
PROCESS_TASK_REVOKE(5, "流程任务撤销事件"),
;
private final Integer code;
@@ -0,0 +1,47 @@
package com.budwk.app.flow.listenter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.event.ProcessEvent;
import com.budwk.app.flow.engine.event.ProcessEventListener;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessEventTypeEnum;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* 流程任务撤销事件监听器
*/
@IocBean
public class ProcessTaskRevokeEventListener implements ProcessEventListener {
@Inject
private Dao dao;
@Override
public void onEvent(ProcessEvent event) {
if(event.getEventType()== ProcessEventTypeEnum.PROCESS_TASK_REVOKE){
Long taskId = event.getSourceId();
ProcessTask task = dao.fetch(ProcessTask.class, taskId);
String taskDisplayName = task.getDisplayName();
ProcessInstance instance = dao.fetch(ProcessInstance.class, task.getProcessInstanceId());
JSONObject variable = JSONUtil.parseObj(instance.getVariable());
String initiatorName = variable.getStr(FlowConst.INITIATOR_NAME);
String instanceName = variable.getStr(FlowConst.PROCESS_INSTANCE_NAME);
// 消息API提醒
String msgTemplate = "{},您的{}已被撤销,无需审核。";
String msg = StrUtil.format(msgTemplate, instanceName, taskDisplayName);
// 发送 go go go
}
}
}
@@ -233,4 +233,17 @@ public interface ProcessInstanceService extends BaseService<ProcessInstance> {
* @return
*/
List<LabelValueVO> getAssigneeTextData(Long processInstanceId);
/**
* 删除流程实例
* @param processInstanceId
*/
void deleteProcessInstanceById(Long processInstanceId);
/**
* 删除流程实例
* @param businessKey
*/
void deleteProcessInstanceByBusinessKey(String businessKey);
}
@@ -3,17 +3,14 @@ package com.budwk.app.flow.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.enums.UpAndDownParam;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.flow.engine.model.ProcessModel;
import com.budwk.app.flow.engine.parser.ModelParser;
import com.budwk.app.flow.entity.ProcessDefine;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.service.ProcessDefineService;
import com.budwk.app.flow.vo.ProcessDefineVO;
import org.nutz.aop.interceptor.ioc.TransAop;
@@ -23,9 +20,7 @@ import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.IocBean;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class ProcessDefineServiceImpl extends BaseServiceImpl<ProcessDefine> implements ProcessDefineService {
@@ -100,8 +95,6 @@ public class ProcessDefineServiceImpl extends BaseServiceImpl<ProcessDefine> imp
// 1. json定义文件转成流程模型
ProcessModel processModel = ModelParser.parse(defineJsonStr);
// 2. 根据名称查询,取最新版本的流程定义记录
List<ProcessDefine> processDefineList = query(Cnd.where(ProcessDefine::getName, "=", processModel.getName()).desc(ProcessDefine::getId));
ProcessDefine latestDefine = dao().fetch(ProcessDefine.class, Cnd.where(ProcessDefine::getName, "=", processModel.getName()).desc(ProcessDefine::getId));
ProcessDefine define = null;
@@ -122,9 +115,8 @@ public class ProcessDefineServiceImpl extends BaseServiceImpl<ProcessDefine> imp
define.setH5InstanceUrl(processModel.getH5InstanceUrl());
define.setInstanceViewUrl(processModel.getInstanceViewUrl());
define.setH5InstanceViewUrl(processModel.getH5InstanceViewUrl());
// define.setInstanceViewUrl(latestDefine.getInstanceViewUrl());
// define.setH5InstanceViewUrl(latestDefine.getH5InstanceViewUrl());
define.setIcon(processModel.getIcon());
define.setPinyinName(PinyinUtil.getFirstLetter(define.getDisplayName().charAt(0)));
define.setState(1);
define.setContent(JSONUtil.parseObj(defineJsonStr));
@@ -9,11 +9,9 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.expression.ExpressionUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.enums.YesNoEnum;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.vo.LabelValueVO;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.Context;
import com.budwk.app.flow.engine.DecisionHandler;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.flow.engine.core.Execution;
@@ -93,7 +91,13 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl<ProcessInstance>
ProcessInstanceVO instanceVO = new ProcessInstanceVO();
BeanUtil.fillBeanWithMapIgnoreCase(nutMap, instanceVO, true);
instanceVO.setJsonObject(processDefineService.getDefineJsonObject(instanceVO.getProcessDefineId()));
ProcessDefine define = processDefineService.fetch(instanceVO.getProcessDefineId());
// instanceVO.setJsonObject(processDefineService.getDefineJsonObject(instanceVO.getProcessDefineId()));
instanceVO.setJsonObject(JSONUtil.parseObj(define));
return instanceVO;
}
@@ -319,24 +323,38 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl<ProcessInstance>
@Override
public void withdraw(Long processInstanceId, String operator) {
Date now = new Date();
// Date now = new Date();
// 1. 将该流程实例状态修改为撤回
ProcessInstance processInstance = new ProcessInstance();
processInstance.setId(processInstanceId);
processInstance.setState(ProcessInstanceStateEnum.WITHDRAW.getCode());
processInstance.setUpdatedAt(System.currentTimeMillis());
processInstance.setUpdatedBy(operator);
int update = dao().update(ProcessInstance.class, Chain.from(processInstance), Cnd.where(ProcessInstance::getId, "=", processInstanceId)
.and(ProcessInstance::getState, "=", ProcessInstanceStateEnum.DOING.getCode()));
int updated = dao().updateIgnoreNull(processInstance);
if (update >= 1) {
// 2. 将该流程实例产生的任务状态修改为撤回
ProcessTask processTask = new ProcessTask();
processTask.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode());
processTask.setUpdatedAt(System.currentTimeMillis());
processTask.setUpdatedBy(operator);
dao().update(ProcessTask.class, Chain.from(processTask), Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId)
.and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode()));
// 2. 将该流程实例产生的任务状态修改为撤回
if (updated > 0) {
List<ProcessTask> taskList = dao().query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId).and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode()));
for (ProcessTask processTask : taskList) {
processTask.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode());
processTask.setUpdatedAt(System.currentTimeMillis());
processTask.setUpdatedBy(operator);
}
dao().update(taskList, "taskState|updateAt|updateBy");
}
// int update = dao().update(ProcessInstance.class, Chain.from(processInstance), Cnd.where(ProcessInstance::getId, "=", processInstanceId)
// .and(ProcessInstance::getState, "=", ProcessInstanceStateEnum.DOING.getCode()));
//
// if (update >= 1) {
// // 2. 将该流程实例产生的任务状态修改为撤回
// ProcessTask processTask = new ProcessTask();
// processTask.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode());
// processTask.setUpdatedAt(System.currentTimeMillis());
// processTask.setUpdatedBy(operator);
// dao().update(ProcessTask.class, Chain.from(processTask), Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId)
// .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode()));
// }
}
/**
@@ -531,4 +549,22 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl<ProcessInstance>
.collect(Collectors.toList());
return result;
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void deleteProcessInstanceById(Long processInstanceId) {
List<ProcessTask> taskList = dao().query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId));
List<Long> taskIds = taskList.stream().map(ProcessTask::getId).toList();
dao().clear(ProcessTask.class, Cnd.where(ProcessTask::getId, "in", taskIds));
dao().clear(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "=", processInstanceId));
}
@Override
public void deleteProcessInstanceByBusinessKey(String businessKey) {
ProcessInstance processInstance = fetch(Cnd.where(ProcessInstance::getBusinessNo, "=", businessKey));
if (processInstance != null) {
deleteProcessInstanceById(processInstance.getId());
}
}
}
@@ -108,7 +108,7 @@ public class ProcessTaskServiceImpl extends BaseServiceImpl<ProcessTask> impleme
@Override
public List<ProcessTask> getDoneTaskList(Long processInstanceId, String[] taskNames) {
Cnd cnd = Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId).and(ProcessTask::getTaskState, "!=", ProcessTaskStateEnum.DOING.getCode());
Cnd cnd = Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId).and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode());
cnd.andEX(ProcessTask::getTaskName, "", taskNames);
List<ProcessTask> processTaskList = query(cnd);
return processTaskList;
@@ -47,6 +47,16 @@ public class SysHomeV4Controller {
}
/**
* 服务中心
*/
@At("/serv")
@Ok("beetl:/layouts/v4/serv.html")
@SaCheckLogin
public void serv(){
}
/**
* 子系统
* @param appId 应用ID
@@ -0,0 +1,69 @@
package com.budwk.app.sys.controller.v4;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.entity.ProcessCategory;
import com.budwk.app.sys.services.SysMenuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@IocBean
@At("/platform/v4/serv")
@Api(value = "服务中心接口", tags = "服务中心接口")
public class SysV4ServController {
@Inject
private Dao dao;
@Inject
private SysMenuService sysMenuService;
@At("/categories")
@Ok("json")
@SaCheckLogin
@ApiOperation("获取分类")
public Result categories() {
List<ProcessCategory> list = dao.query(ProcessCategory.class, Cnd.NEW());
return Result.success(list);
}
@At("/list")
@Ok("json:full")
@SaCheckLogin
@ApiOperation("获取应用列表")
public Result list(@Param("categoryId") String categoryId, @Param("letter") String letter, @Param("keyword") String keyword, HttpServletRequest req) {
Sql sql = Sqls.create("""
SELECT t.*
FROM wf_process_define t
INNER JOIN (
SELECT name, MAX(id) AS max_id
FROM wf_process_define
GROUP BY name
) sub ON t.name = sub.name AND t.id = sub.max_id
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(keyword)) {
cnd.where().andLike("t.disPlayName", keyword);
}
cnd.andEX("t.category", "=", categoryId);
sql.setCondition(cnd);
List<NutMap> list = sysMenuService.listMap(sql);
return Result.success(list);
}
}
@@ -3,17 +3,22 @@ package com.budwk.app.zhgh.democratic.suggestionBox.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.suggestionBox.service.SuggestionBoxService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@At("/platform/suggestionBox2/apply")
@@ -23,11 +28,13 @@ public class SuggestionApplyController {
@Inject
private SuggestionBoxService suggestionBoxService;
@Inject
private FlowEngine flowEngine;
@At("/index")
@Ok("beetl:/platform/zhgh/dayofficework/suggestionBox/apply/index.html")
@SaCheckLogin
public void index(){
public void index() {
}
@@ -62,7 +69,9 @@ public class SuggestionApplyController {
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariale
t.variable taskVariale,
IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = t.taskParentId) = 'startTask', 1, 0) canRevoke,
(select MAX(id) from wf_process_task where processInstanceId = ins.id and taskName = 'startTask') AS startTaskId
FROM
suggestion_box info
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
@@ -78,5 +87,14 @@ public class SuggestionApplyController {
return Result.success(pagination);
}
@At
@SaCheckLogin
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("删除")
public Result delete(@Param("id") String id) {
suggestionBoxService.delete(id);
flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id);
return Result.success();
}
}
@@ -68,12 +68,16 @@ public class SuggestionXghController {
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariale
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
IF(rt.id IS NOT NULL, 1, 0) AS canRevoke
FROM
wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN suggestion_box info ON info.id = ins.businessNo
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
$condition
""");
Cnd cnd = Cnd.NEW();
@@ -86,8 +90,6 @@ public class SuggestionXghController {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
cnd.desc("t.createdAt");
cnd.andEX("YEAR(info.submitTime)", "=", year);
cnd.and(Cnd.likeEX("info.title", title));
@@ -96,6 +98,8 @@ public class SuggestionXghController {
} else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
sql.setCondition(cnd);
Pagination<NutMap> pageVO = suggestionBoxService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pageVO);
@@ -664,7 +664,6 @@ td.no-b {
}
.flow-task-form .el-descriptions-item__label.is-bordered-label {
color: rgb(102, 102, 102) !important;
background: rgb(248, 249, 250) !important;
padding: 8px 12px !important;
border: 1px solid rgb(221, 221, 221) !important;
@@ -36,7 +36,7 @@
<div class="panel-content">
<div class="info-table">
<!-- 新申请场景 -->
<template v-if="isNewApplication">
<template v-if="isNewApplication || (processInstance && processInstance.state === 30)">
<div class="info-row">
<div class="info-label">流程名称</div>
<div class="info-value">{{ processDefinition.displayName || processDefinition.name }}</div>
@@ -82,13 +82,20 @@
</div>
<!-- 表单处理区域 -->
<div class="form-panel" v-if="isNewApplication || todoTasks.map((v) => v.id).includes(taskId)">
<div
class="form-panel"
v-if="isNewApplication || (processInstance && processInstance.state === 30) || todoTasks.map((v) => v.id).includes(taskId)"
>
<div class="panel-header">
<h3 class="panel-title">{{ formPanelTitle }}</h3>
</div>
<div class="panel-content">
<!-- 新申请表单 -->
<div v-if="isNewApplication" id="application-form-container" class="form-content">
<div
v-if="isNewApplication || (processInstance && processInstance.state === 30)"
id="application-form-container"
class="form-content"
>
<div v-if="pjaxLoading.apply" class="loading-state">
<i class="el-icon-loading"></i>
<span>正在加载申请表单...</span>
@@ -121,7 +128,72 @@
</div>
</template>
<script>
<script type="text/javascript">
class PjaxSync {
constructor() {
this.isLoading = false
this.queue = []
}
async request(url, options = {}) {
return new Promise((resolve, reject) => {
const requestData = { url, options, resolve, reject }
if (this.isLoading) {
this.queue.push(requestData)
return
}
this._executeRequest(requestData)
})
}
_executeRequest({ url, options, resolve, reject }) {
this.isLoading = true
const defaultOptions = {
timeout: 10000,
push: false,
replace: false,
...options
}
const successHandler = (event, data, status, xhr) => {
this._cleanup()
resolve({ data, status, xhr })
this._processQueue()
}
const errorHandler = (event, xhr, textStatus, errorThrown) => {
this._cleanup()
reject(new Error(textStatus || "PJAX request failed"))
this._processQueue()
}
$(document).one("pjax:success", successHandler)
$(document).one("pjax:error", errorHandler)
$.pjax({
url: url,
...defaultOptions
})
}
_cleanup() {
this.isLoading = false
$(document).off("pjax:success pjax:error")
}
_processQueue() {
if (this.queue.length > 0) {
const nextRequest = this.queue.shift()
this._executeRequest(nextRequest)
}
}
}
// 创建全局实例
const pjaxSync = new PjaxSync()
module.exports = {
name: "SnakerFlow",
data() {
@@ -221,7 +293,7 @@ module.exports = {
} else if (this.shouldShowTaskForm) {
return this.currentTask.displayName || "任务表单"
} else if (this.shouldShowHistoryProcess) {
return "办理过程"
return "申请表单"
} else {
return "表单信息"
}
@@ -236,7 +308,6 @@ module.exports = {
// 是否显示任务表单
shouldShowTaskForm() {
debugger
return this.taskId != null
},
@@ -277,20 +348,20 @@ module.exports = {
},
created() {
this.initComponent()
// 监听
window.addEventListener("message", (event) => {
// 监听消息
new BroadcastChannel("zhgh-global-channel").addEventListener("message", (event) => {
if (event.data.type === "task-complete") {
this.initComponent()
}
})
},
beforeDestroy() {
console.log("beforeDestroy")
},
methods: {
// 初始化组件
async initComponent() {
this.loading = true
debugger
if (this.isNewApplication) {
// 新申请:加载流程定义信息
await this.loadProcessDefinition()
@@ -299,6 +370,12 @@ module.exports = {
} else if (this.isExistingProcess) {
// 已有流程:加载流程实例信息
await this.loadProcessInstance()
if (this.processInstance && this.processInstance.state === 30) {
// 流程被撤回了 此时加载申请表单
// 加载流程申请表单
await this.loadApplicationForm()
}
// 加载任务信息
await this.loadCurrentTask()
// 如果有businessId且不是第一个任务节点,加载历史办理过程
@@ -317,9 +394,11 @@ module.exports = {
// 加载流程定义信息
async loadProcessDefinition() {
if (!this.defineKey) return
try {
const response = await $.get("/flow/common/defineInfo", { defineKey: this.defineKey })
const response = await $.get("/flow/common/defineInfo", {
defineKey: this.defineKey,
instanceId: this.instanceId
})
if (response.code === 0) {
this.processDefinition = response.data
}
@@ -334,20 +413,11 @@ module.exports = {
this.pjaxLoading.apply = true
$.pjax({
url: this.processDefinition.instanceUrl,
container: "#application-form-container",
push: false,
replace: false,
timeout: 10000
await pjaxSync.request(this.processDefinition.instanceUrl, {
container: "#application-form-container"
})
.done(() => {
this.pjaxLoading.apply = false
})
.fail(() => {
this.pjaxLoading.apply = false
console.log(this.processDefinition.instanceUrl, "申请表单加载失败")
})
this.pjaxLoading.apply = false
},
// 加载流程实例信息
@@ -356,6 +426,7 @@ module.exports = {
const { code, data, msg } = await $.get("/flow/common/instanceInfo", { instanceId: this.instanceId })
if (code === 0) {
this.processInstance = data.processInstance
this.processDefinition = data.processInstance?.jsonObject
this.todoTasks = data.todoTasks
}
},
@@ -381,43 +452,23 @@ module.exports = {
this.pjaxLoading.taskForm = true
$.pjax({
url: this.currentTask.taskModel.form,
container: "#task-form-container",
push: false,
replace: false,
timeout: 10000
await pjaxSync.request(this.currentTask.taskModel.form, {
container: "#task-form-container"
})
.done(() => {
this.pjaxLoading.taskForm = false
})
.fail(() => {
this.pjaxLoading.taskForm = false
console.log(this.currentTask.taskModel.form, "任务表单加载失败")
})
},
// 加载历史办理过程
async loadHistoryProcess() {
if (!this.businessId) return
const { instanceViewUrl } = this.processInstance.jsonObject
this.pjaxLoading.historyProcess = true
if (instanceViewUrl) {
$.pjax({
url: `${instanceViewUrl}?businessId=${this.businessId}&instanceId=${this.instanceId}`,
container: "#history-process-container",
push: false,
replace: false,
timeout: 10000
this.pjaxLoading.historyProcess = true
await pjaxSync.request(`${instanceViewUrl}?businessId=${this.businessId}&instanceId=${this.instanceId}`, {
container: "#history-process-container"
})
.done(() => {
this.pjaxLoading.historyProcess = false
})
.fail(() => {
this.pjaxLoading.historyProcess = false
console.log("历史办理过程加载失败")
})
this.pjaxLoading.historyProcess = false
}
},
@@ -149,8 +149,7 @@ module.exports = {
// 处理提交操作
handleSubmit() {
// 根据是否有流程实例来确定操作类型
// const submitAction = this.instanceId ? this.actionEnum.RE_APPLY : this.actionEnum.APPLY
const submitAction = this.actionEnum.APPLY
const submitAction = this.instanceId ? this.actionEnum.RE_APPLY : this.actionEnum.APPLY
// 触发父组件事件,传递提交操作
this.$emit("task-action", {
@@ -160,7 +159,6 @@ module.exports = {
defineId: this.defineId,
defineKey: this.defineKey,
businessId: this.businessId
// currentTask: this.taskInfo
})
},
@@ -201,7 +199,11 @@ module.exports = {
// 取消操作
handleCancel() {
this.$emit("cancel")
const func = () => window.close()
this.$emit("cancel", func)
if (!this.$listeners.cancel) {
func()
}
},
// 刷新任务信息
@@ -71,7 +71,7 @@
<script src="${base!}/assets/platform/plugins/form-create/form-create.min.js"></script>
<script src="${base!}/assets/platform/plugins/form-create/index.umd.js"></script>
<!-- <script src="${base!}/assets/platform/plugins/snaker/SnakerflowDesigner.umd.min.js"></script>-->
<!-- <script src="${base!}/assets/platform/plugins/snaker/SnakerflowDesigner.umd.min.js"></script>-->
<script src="${base!}/components/plugins/sysDict/DictData.js"></script>
<script src="${base!}/assets/platform/js/util/commonUtil.js"></script>
@@ -101,6 +101,11 @@
Vue.use(FcDesigner.formCreate)
</script>
<!--广播频道-->
<script type="text/javascript">
window.GlobalBroadcastChannel = new BroadcastChannel("zhgh-global-channel")
</script>
<!--ws-->
<script type="text/javascript">
class WebSocketPubSub {
@@ -334,12 +339,17 @@
Vue.component("excel-import", httpVueLoader("/components/plugins/sysImport/excelImport.vue?v=" + new Date().getTime()))
Vue.component("flow-form-button", httpVueLoader("/components/plugins/flowable/formButton.vue?v=" + new Date().getTime()))
Vue.component("snaker-flow", httpVueLoader("/components/plugins/snaker/snakerFlow.vue?v=" + new Date().getTime()))
Vue.component("snaker-flow-task-form-action", httpVueLoader("/components/plugins/snaker/snakerFlowTaskFormAction.vue?v=" + new Date().getTime()))
Vue.component("snaker-flow-history-approval", httpVueLoader("/components/plugins/snaker/snakerFlowHisApproval.vue?v=" + new Date().getTime()))
Vue.component(
"snaker-flow-task-form-action",
httpVueLoader("/components/plugins/snaker/snakerFlowTaskFormAction.vue?v=" + new Date().getTime())
)
Vue.component(
"snaker-flow-history-approval",
httpVueLoader("/components/plugins/snaker/snakerFlowHisApproval.vue?v=" + new Date().getTime())
)
</script>
<style>
.v4-header {
background-color: rgb(0, 109, 185);
box-shadow: 0 2px 10px rgba(0, 109, 185, 0.3);
@@ -555,6 +565,10 @@
<i class="fa fa-th-large"></i>
应用中心
</a>
<a href="/platform/v4/serv" data-pjax class="v4-nav-item">
<i class="fa fa-th-large"></i>
服务中心
</a>
<a href="/flow/todoCenter" data-pjax class="v4-nav-item">
<i class="fa fa-th-large"></i>
待办中心
@@ -0,0 +1,597 @@
<!--#
layout("/layouts/v4/baseLayout.html"){
#-->
<style>
.apps-hero {
position: relative;
overflow: hidden;
display: flex;
align-items: center;
/*border-radius: 8px 8px 0 0;*/
margin-bottom: 0;
}
.apps-hero img {
width: 100%;
height: 100%;
}
.apps-container {
display: flex;
/*height: calc(100vh - 64px - 48px - 180px);*/
background-color: #fff;
/*border-radius: 0 0 8px 8px;*/
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
flex: 1;
}
.apps-sidebar {
width: 220px;
background-color: #f7f7f7;
border-right: 1px solid #e8e8e8;
overflow-y: auto;
}
.apps-sidebar-item {
padding: 16px 20px;
cursor: pointer;
transition: all 0.3s;
font-size: 16px;
display: flex;
align-items: center;
gap: 10px;
}
.apps-sidebar-item.active {
background-color: #e6f7ff;
color: var(--color-primary);
border-right: 2px solid #1890ff;
}
.apps-sidebar-item:hover:not(.active) {
background-color: #f0f0f0;
}
.apps-content {
flex: 1;
padding: 20px;
overflow: auto;
display: flex;
flex-direction: column;
}
.apps-header {
padding: 0 0 20px 0;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
.apps-search-container {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.apps-search {
position: relative;
width: 400px;
}
.apps-search input {
width: 100%;
height: 36px;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 0 15px;
font-size: 14px;
box-sizing: border-box;
}
.apps-search .search-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
color: #999;
cursor: pointer;
}
.apps-filter {
margin-top: 20px;
}
.apps-filter-title {
color: #666;
font-size: 14px;
margin-bottom: 5px;
}
.apps-filter-tags {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.filter-tag {
padding: 5px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
}
.filter-tag.active {
background-color: var(--color-primary);
color: #fff;
}
.filter-tag:not(.active) {
background-color: #f0f0f0;
color: #333;
}
.filter-tag:not(.active):hover {
background-color: #e0e0e0;
}
.apps-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
flex: 1;
overflow-y: auto;
grid-auto-rows: min-content;
padding-top: 5px;
}
.app-card {
border: 1px solid #e8e8e8;
border-radius: 8px;
overflow: hidden;
transition: all 0.3s;
display: flex;
align-items: center;
padding: 15px;
position: relative;
}
.app-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
cursor: pointer;
}
.app-icon {
width: 48px;
height: 48px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
color: #fff;
font-size: 24px;
}
.app-icon img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 8px;
}
.app-icon i {
color: var(--color-primary);
font-size: 38px;
}
.favorite-icon {
position: absolute;
bottom: 10px;
right: 10px;
cursor: pointer;
transition: all 0.3s;
font-size: 18px;
color: #c0c4cc;
}
.favorite-icon.active {
color: #ff9800;
}
.favorite-icon:hover {
transform: scale(1.2);
}
.app-info {
flex: 1;
}
.app-title {
font-weight: 500;
margin-bottom: 5px;
font-size: 14px;
}
.app-desc {
color: #999;
font-size: 12px;
}
.alphabet-filter {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.alphabet-item {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
font-size: 14px;
}
.alphabet-item.active {
background-color: var(--color-primary);
color: #fff;
}
.alphabet-item:not(.active) {
background-color: #f0f0f0;
}
.alphabet-item:hover:not(.active) {
background-color: #e0e0e0;
}
.app-status {
position: absolute;
top: 0;
right: 0;
background-color: #ff9800;
color: #fff;
padding: 2px 10px;
font-size: 12px;
border-bottom-left-radius: 8px;
}
.apps-wrapper {
/*margin: 0 auto;*/
/*margin: 24px;*/
/*padding: 24px;*/
display: flex;
flex-direction: column;
height: calc(100vh - 64px);
}
.no-results {
display: flex;
justify-content: center;
align-items: center;
min-height: 300px;
padding: 40px 20px;
}
.no-results-content {
text-align: center;
max-width: 300px;
}
.no-results i {
color: #c0c4cc;
margin-bottom: 16px;
}
.no-results .title {
font-size: 16px;
color: #606266;
font-weight: 500;
margin-bottom: 8px;
}
.no-results .hint {
font-size: 14px;
color: #909399;
line-height: 1.5;
}
.page-item.disabled {
color: #c0c4cc;
cursor: not-allowed;
}
.page-item.disabled:hover {
border-color: #d9d9d9;
color: #c0c4cc;
}
.loading-container {
text-align: center;
padding: 40px 0;
color: #909399;
font-size: 14px;
}
</style>
<div class="apps-wrapper" id="app">
<div class="apps-hero">
<img src="https://i.cug.edu.cn/data/sys-attach/download/1i8hmpaqdwmvwajjvw2c8isi61jj0gkmosw0" alt="应用中心" />
</div>
<div class="apps-container">
<!-- Left Sidebar -->
<div class="apps-sidebar">
<div
v-for="category in allCategories"
:key="category.id"
:class="['apps-sidebar-item', activeCategory === category.id ? 'active' : '']"
@click="changeCategory(category.id)"
>
<i :class="category.icon"></i>
{{ category.name }}
</div>
</div>
<!-- Main Content -->
<div class="apps-content">
<div class="apps-header">
<div class="apps-search-container">
<div class="apps-search">
<input type="text" v-model="searchKeyword" @keyup.enter="search" placeholder="请输入内容" />
<i class="el-icon-search search-icon" @click="search"></i>
</div>
</div>
<!-- 字母过滤 -->
<div class="apps-filter">
<div class="apps-filter-title">首字母:</div>
<div class="apps-filter-tags alphabet-filter">
<div :class="['filter-tag', activeAlphabet === '' ? 'active' : '']" @click="changeAlphabet('')">全部</div>
<div
v-for="letter in alphabets"
:key="letter"
:class="['alphabet-item', activeAlphabet === letter ? 'active' : '']"
@click="changeAlphabet(letter)"
>
{{ letter }}
</div>
</div>
</div>
</div>
<!-- 应用网格 -->
<div class="apps-grid" v-if="applications.length > 0">
<div v-for="app in applications" :key="app.id" class="app-card" @click.stop="openApp(app)">
<div :class="['app-icon', app.iconType]">
<i v-if="app.icon" :class="app.icon"></i>
<i v-else class="fa fa-skype"></i>
</div>
<div class="app-info">
<div class="app-title">{{ app.displayName }}</div>
</div>
</div>
</div>
<!-- 加载状态 -->
<div class="loading-container" v-if="loading">
<i class="el-icon-loading"></i>
<p>加载中...</p>
</div>
<!-- 无搜索结果 -->
<div class="no-results" v-else-if="applications.length === 0">
<div class="no-results-content">
<i class="el-icon-document" style="font-size: 48px"></i>
<p class="title">没有找到匹配的服务</p>
<p class="hint">请尝试不同的搜索词或筛选条件</p>
</div>
</div>
</div>
</div>
</div>
<script>
new Vue({
el: "#app",
data: {
// 应用列表
applications: [],
// 分类列表
categories: [{ id: "all", name: "全部服务", icon: "fa fa-th-large" }],
// 动态加载的分类
dynamicCategories: [],
// 当前选中的分类
activeCategory: "all",
// 字母表筛选
alphabets: [
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z"
],
activeAlphabet: "",
// 搜索关键词
searchKeyword: "",
// 分页相关
currentPage: 1,
pageSize: 15,
totalPage: 1,
total: 0,
// 加载状态
loading: true
},
computed: {
// 所有分类(固定分类 + 动态分类)
allCategories() {
return [...this.categories, ...this.dynamicCategories]
}
},
mounted() {
// 页面加载时获取分类和应用数据
this.loadCategories()
this.loadApps()
},
methods: {
// 加载分类数据
loadCategories() {
$.get("/platform/v4/serv/categories")
.then((result) => {
if (result.code === 0) {
this.dynamicCategories = result.data || []
} else {
console.error("获取应用分类失败:", result.msg)
}
})
.fail((error) => {
console.error("获取应用分类异常:", error)
})
},
// 加载应用数据
loadApps() {
this.loading = true
// 构建请求参数
const params = {
categoryId:
this.activeCategory === "all"
? ""
: this.activeCategory === "favorites" || this.activeCategory === "recommended"
? this.activeCategory
: this.activeCategory,
letter: this.activeAlphabet,
keyword: this.searchKeyword
}
// 将参数转换为URL查询参数
const queryString = Object.keys(params)
.filter((key) => params[key] !== null && params[key] !== undefined && params[key] !== "")
.map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(params[key]))
.join("&")
// 如果是"我的收藏"分类
if (this.activeCategory === "favorites") {
this.loadFavorites()
return
}
// 发送请求获取普通应用列表
$.get("/platform/v4/serv/list?" + queryString)
.then((result) => {
if (result.code === 0) {
this.applications = result.data || []
} else {
console.error("获取应用列表失败:", result.msg)
}
this.loading = false
})
.fail((error) => {
console.error("获取应用列表异常:", error)
this.loading = false
})
},
// 加载收藏的应用
loadFavorites() {
$.get("/platform/v4/serv/favorite")
.then((result) => {
if (result.code === 0) {
this.applications = result.data || []
} else {
console.error("获取收藏应用失败:", result.msg)
}
})
.fail((error) => {
console.error("获取收藏应用异常:", error)
})
},
// 切换应用收藏状态
toggleFavorite(appId) {
const app = this.applications.find((a) => a.id === appId)
if (!app) return
const url = app.isFavorite ? "/platform/v4/serv/removeFavorite" : "/platform/v4/serv/addFavorite"
const params = { appId: appId }
$.post(url, params)
.then((result) => {
if (result.code === 0) {
app.isFavorite = !app.isFavorite
this.$message.success(app.isFavorite ? "收藏成功" : "取消收藏成功")
} else {
console.error(app.isFavorite ? "取消收藏失败:" : "收藏失败:", result.msg)
}
})
.fail((error) => {
console.error(app.isFavorite ? "取消收藏异常:" : "收藏异常:", error)
})
},
// 切换应用分类
changeCategory(categoryId) {
this.activeCategory = categoryId
this.currentPage = 1
// 如果是"我的收藏"分类
if (categoryId === "favorites") {
this.loadFavorites()
} else {
this.loadApps()
}
},
// 切换应用字母
changeAlphabet(letter) {
this.activeAlphabet = letter
this.currentPage = 1
this.loadApps()
},
// 搜索
search() {
this.loadApps()
},
// 打开应用
openApp(app) {
console.log(app)
// 新标签页打开
window.open("/flow/common/approval/form?defineKey=" + app.name, "_blank")
}
}
})
</script>
<!--#
}
#-->
@@ -84,7 +84,8 @@ layout("/layouts/platform.html"){
<el-input v-model="formData.h5InstanceViewUrl" placeholder="请输入手机端发起地址"></el-input>
</el-form-item>
<el-form-item label="图标" prop="icon">
<el-input v-model="formData.icon" placeholder="请输入图标"></el-input>
<el-input maxlength="100" placeholder="图标" v-model="formData.icon"></el-input>
<i :class="formData.icon" v-if="formData.icon"></i>
</el-form-item>
</el-form>
<template #footer>
@@ -201,11 +202,11 @@ layout("/layouts/platform.html"){
},
listCategory() {
// this.$axios.get("/platform/warmFlow/category/list").then((res) => {
// if (res.code === 0) {
// this.categoryOptions = res.data
// }
// })
this.$axios.post("/flow/category/list").then((res) => {
if (res.code === 0) {
this.categoryOptions = res.data
}
})
}
},
created() {
@@ -2,172 +2,6 @@
layout("/layouts/v4/baseLayout.html"){
#-->
<div id="app" v-cloak>
<div class="task-todo-center">
<!-- 统计数据区域 -->
<el-row :gutter="20" class="statistics-section">
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon todo-icon">
<i class="el-icon-s-claim"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{todoCount}}</div>
<div class="stat-label">待办任务</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon done-icon">
<i class="el-icon-finished"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{doneCount}}</div>
<div class="stat-label">已办任务</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon started-icon">
<i class="el-icon-s-promotion"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{startedCount}}</div>
<div class="stat-label">我发起的</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 搜索筛选区域 -->
<el-card shadow="never" class="filter-section">
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item>
<el-input v-model="searchForm.keyword" placeholder="请输入关键词搜索" prefix-icon="el-icon-search" clearable></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.category" placeholder="所有流程类型" clearable>
<el-option v-for="type in processTypes" :key="type.value" :label="type.label" :value="type.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search">搜索</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 任务列表区域 -->
<el-card shadow="never" class="task-section">
<div slot="header" class="task-header">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="待办任务" name="todo"></el-tab-pane>
<el-tab-pane label="已办任务" name="done"></el-tab-pane>
<el-tab-pane label="我发起的" name="started"></el-tab-pane>
</el-tabs>
</div>
<!-- 表格展示 -->
<el-table
v-loading="loading"
:data="tasks"
style="width: 100%"
:key="activeTab"
:header-cell-style="{backgroundColor: '#f5f7fa'}"
:row-class-name="tableRowClassName"
>
<el-table-column prop="processInstanceName" label="流程名称" min-width="180" show-overflow-tooltip>
<template slot-scope="scope">
<span class="task-title">{{scope.row.processInstanceName || scope.row.title}}</span>
</template>
</el-table-column>
<el-table-column prop="taskName" label="任务节点" min-width="150" show-overflow-tooltip></el-table-column>
<el-table-column prop="processDefinitionName" label="流程类型" min-width="150" show-overflow-tooltip>
<template slot-scope="scope">{{scope.row.processDefinitionName || scope.row.processType}}</template>
</el-table-column>
<el-table-column prop="initiatorName" label="申请人" min-width="120">
<template slot-scope="{row}">
{{row.variable.initiatorName}}
</template>
</el-table-column>
<el-table-column label="时间" min-width="180">
<template slot-scope="scope">
<div v-if="activeTab === 'todo'">
<i class="el-icon-time"></i>
{{scope.row.createdAt}}
</div>
<div v-else-if="activeTab === 'done'">
<i class="el-icon-check"></i>
{{scope.row.finishTime}}
</div>
<div v-else-if="activeTab === 'started'">
<i class="el-icon-s-promotion"></i>
{{scope.row.createdAt}}
</div>
</template>
</el-table-column>
<el-table-column v-if="activeTab === 'started'" label="状态" width="100">
<template slot-scope="{row}">
{{processStatusMap[row.state]?.text}}
</template>
</el-table-column>
<el-table-column label="操作" width="250" fixed="right">
<template slot-scope="scope">
<el-button v-if="activeTab === 'todo'" type="primary" size="mini" @click="handleTask(scope.row)">处理</el-button>
<el-button type="info" size="mini" @click="viewTaskDetail(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<!-- 空状态展示 -->
<el-empty v-if="tasks.length === 0" :description="getEmptyText()"></el-empty>
<!-- 分页 -->
<div class="pagination-container" v-if="tasks.length > 0">
<el-pagination
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
layout="prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
</el-card>
<!-- 任务详情对话框 -->
<el-dialog title="任务详情" :visible.sync="dialogVisible" width="600px" :close-on-click-modal="false">
<div v-if="currentTask" class="task-detail">
<el-descriptions :column="1" border>
<el-descriptions-item label="流程名称">{{currentTask.processInstanceName || currentTask.title}}</el-descriptions-item>
<el-descriptions-item label="任务名称">{{currentTask.taskName || '-'}}</el-descriptions-item>
<el-descriptions-item label="流程类型">{{currentTask.processDefinitionName || currentTask.processType}}</el-descriptions-item>
<el-descriptions-item label="申请人">{{currentTask.applyUserName || '-'}}</el-descriptions-item>
<el-descriptions-item label="申请部门">{{currentTask.applyUserUnitId || '-'}}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{currentTask.createTime}}</el-descriptions-item>
<el-descriptions-item v-if="currentTask.description" label="任务描述">
<div class="description-content">{{currentTask.description}}</div>
</el-descriptions-item>
<el-descriptions-item v-if="currentTask.taskMobileFormUrl" label="表单链接">
<el-link type="primary" :href="currentTask.taskMobileFormUrl" target="_blank">点击查看详细表单</el-link>
</el-descriptions-item>
</el-descriptions>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">关闭</el-button>
<el-button v-if="activeTab === 'todo'" type="primary" @click="handleTask(currentTask)">处理任务</el-button>
</span>
</el-dialog>
</div>
</div>
<style>
.task-todo-center {
padding: 20px;
@@ -369,20 +203,138 @@ layout("/layouts/v4/baseLayout.html"){
height: 28px;
border-radius: 4px;
}
/* 任务详情样式 */
.task-detail .el-descriptions {
margin-bottom: 20px;
}
.description-content {
background-color: #f5f7fa;
padding: 10px;
border-radius: 4px;
white-space: pre-wrap;
}
</style>
<div id="app" v-cloak>
<div class="task-todo-center">
<!-- 统计数据区域 -->
<el-row :gutter="20" class="statistics-section">
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon todo-icon">
<i class="el-icon-s-claim"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{todoCount}}</div>
<div class="stat-label">待办任务</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon done-icon">
<i class="el-icon-finished"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{doneCount}}</div>
<div class="stat-label">已办任务</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon started-icon">
<i class="el-icon-s-promotion"></i>
</div>
<div class="stat-info">
<div class="stat-value">{{startedCount}}</div>
<div class="stat-label">我发起的</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 搜索筛选区域 -->
<el-card shadow="never" class="filter-section">
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item>
<el-input v-model="searchForm.keyword" placeholder="请输入关键词搜索" prefix-icon="el-icon-search" clearable></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.category" placeholder="所有流程分类" clearable>
<el-option v-for="type in categoryOptions" :key="type.id" :label="type.name" :value="type.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search">搜索</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 任务列表区域 -->
<el-card shadow="never" class="task-section">
<div slot="header" class="task-header">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="待办任务" name="todo"></el-tab-pane>
<el-tab-pane label="已办任务" name="done"></el-tab-pane>
<el-tab-pane label="我发起的" name="started"></el-tab-pane>
</el-tabs>
</div>
<!-- 表格展示 -->
<el-table v-loading="loading" :data="tasks" style="width: 100%" :key="activeTab" :header-cell-style="{backgroundColor: '#f5f7fa'}">
<el-table-column prop="processInstanceName" label="流程名称" min-width="180" show-overflow-tooltip>
<template slot-scope="scope">
<span class="task-title">{{scope.row.variable?.instanceName}}</span>
</template>
</el-table-column>
<el-table-column prop="taskName" label="任务节点" min-width="150" show-overflow-tooltip></el-table-column>
<el-table-column prop="categoryName" label="流程分类" min-width="150" show-overflow-tooltip>
<template slot-scope="{row}">{{categoryOptions.find(item => item.id === row.category)?.name}}</template>
</el-table-column>
<el-table-column prop="initiatorName" label="申请人" min-width="120">
<template slot-scope="{row}">{{row.variable.initiatorName}}</template>
</el-table-column>
<!-- <el-table-column label="时间" min-width="180">-->
<!-- <template slot-scope="scope">-->
<!-- <div v-if="activeTab === 'todo'">-->
<!-- <i class="el-icon-time"></i>-->
<!-- {{scope.row.createdAt}}-->
<!-- </div>-->
<!-- <div v-else-if="activeTab === 'done'">-->
<!-- <i class="el-icon-check"></i>-->
<!-- {{scope.row.finishTime}}-->
<!-- </div>-->
<!-- <div v-else-if="activeTab === 'started'">-->
<!-- <i class="el-icon-s-promotion"></i>-->
<!-- {{scope.row.createdAt}}-->
<!-- </div>-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column v-if="activeTab === 'started'" label="状态" width="100">
<template slot-scope="{row}">{{processStatusMap[row.state]?.text}}</template>
</el-table-column>
<el-table-column label="操作" width="100px" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="openView(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<!-- 空状态展示 -->
<el-empty v-if="tasks.length === 0" :description="getEmptyText()"></el-empty>
<!-- 分页 -->
<div class="pagination-container" v-if="tasks.length > 0">
<el-pagination
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
layout="prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
</el-card>
</div>
</div>
<script>
new Vue({
el: "#app",
@@ -400,7 +352,7 @@ layout("/layouts/v4/baseLayout.html"){
},
// 流程类型选项
processTypes: [],
categoryOptions: [],
// 任务列表
tasks: [],
@@ -436,9 +388,7 @@ layout("/layouts/v4/baseLayout.html"){
INTERRUPT: 40,
PENDING: 50,
ABANDON: 99
},
}
}
},
@@ -450,12 +400,18 @@ layout("/layouts/v4/baseLayout.html"){
created() {
this.initData()
// 监听
window.GlobalBroadcastChannel.addEventListener("message", (event) => {
if (event.data.type === "task-complete") {
this.initData()
}
})
},
methods: {
// 初始化数据
async initData() {
await Promise.all([this.getStatistics(), this.getProcessTypes(), this.getTasks()])
await Promise.all([this.getStatistics(), this.listCategory(), this.getTasks()])
},
// 获取统计数据
@@ -475,16 +431,12 @@ layout("/layouts/v4/baseLayout.html"){
},
// 获取流程类型
async getProcessTypes() {
try {
const res = await $.post("/platform/warmFlow/todoCenter/category")
async listCategory() {
this.$axios.post("/flow/category/list").then((res) => {
if (res.code === 0) {
this.processTypes = res.data
this.categoryOptions = res.data
}
} catch (error) {
this.$message.error("获取流程类型失败")
console.error("获取流程类型失败:", error)
}
})
},
// 获取任务列表
@@ -549,31 +501,12 @@ layout("/layouts/v4/baseLayout.html"){
},
// 处理任务
async handleTask(task) {
if (!task) return
// 如果有表单链接,直接跳转
if (task.taskMobileFormUrl) {
window.open(task.taskMobileFormUrl, "_blank")
return
}
},
// 获取状态对应的类型
getStatusType(task) {
if (!task.status) return "info"
switch (task.status) {
case "审批中":
return "primary"
case "已完成":
return "success"
case "已通过":
return "success"
case "已拒绝":
return "danger"
default:
return "info"
async openView(task) {
const { taskId, taskState, instanceId, businessNo } = task
if (taskState === this.taskStateEnum.DOING) {
window.open("/flow/common/approval/form?instanceId=" + instanceId + "&taskId=" + taskId + "&businessId=" + businessNo)
} else {
window.open("/flow/common/approval/form?instanceId=" + instanceId + "&businessId=" + businessNo)
}
},
@@ -589,11 +522,6 @@ layout("/layouts/v4/baseLayout.html"){
default:
return "暂无数据"
}
},
// 表格行类名
tableRowClassName({ row, rowIndex }) {
return ""
}
}
})
@@ -17,7 +17,7 @@
</el-descriptions-item>
</el-descriptions>
</el-form>
<snaker-flow-task-form-action @task-action="handleTaskAction" @save-draft="handleSaveDraft" @cancel="handleCancel"></snaker-flow-task-form-action>
<snaker-flow-task-form-action @task-action="handleTaskAction" @save-draft="handleSaveDraft"></snaker-flow-task-form-action>
</div>
<script>
@@ -27,6 +27,7 @@
data() {
return {
businessId: GetQueryString("businessId"),
instanceId: GetQueryString("instanceId"),
formData: {},
formRules: {
title: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
@@ -39,20 +40,54 @@
console.log(val)
this.$refs.formRef.validate((valid) => {
if (valid) {
this.$axios
.post("/flow/common/startInstanceAndExecute", {
...val,
bizData: JSON.stringify(this.formData)
})
.then((res) => {
if (res.code === 0) {
this.$message.success("操作成功")
window.parent.postMessage({ type: "task-complete" }, "*")
}
})
if (!this.instanceId) {
this.handleApply(val)
} else {
this.handleReApply(val)
}
}
})
},
// 提交申请
handleApply(val) {
this.$axios
.post("/flow/common/startInstanceAndExecute", {
...val,
bizData: JSON.stringify(this.formData)
})
.then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
window.GlobalBroadcastChannel.postMessage({
type: "task-complete"
})
}
})
},
// 重新提交申请
handleReApply(val) {
this.$axios
.post("/flow/common/executeTask", {
data: JSON.stringify({
processTaskId: val.taskId,
processInstanceId: val.instanceId,
submitType: val.submitType,
f_data: JSON.stringify(this.formData)
})
})
.then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
window.GlobalBroadcastChannel.postMessage({
type: "task-complete"
})
}
})
},
// 保存申请
handleSaveDraft(val) {
this.$refs.formRef.validateField(["title"], (errMsg) => {
if (errMsg) {
@@ -78,12 +113,15 @@
})
})
},
handleCancel() {
// 关闭浏览器窗口
window.close()
},
// 初始化
init() {
if (this.businessId) {
this.$axios.post("/platform/suggestionBox/view/info", { id: this.businessId }).then((res) => {
if (res.code === 0) {
this.formData = res.data
}
})
} else {
const { username, loginname, id, unit, union, mobile } = this.$store.state.user
this.formData = {
@@ -33,12 +33,12 @@ layout("/layouts/platform.html"){
</el-table-column>
<el-table-column label="操作" fixed="right" width="300px">
<template slot-scope="{row}">
<el-button @click="onOpen(row.id)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskKey === 'apply' || !row.instanceId" @click="onEdit(row)" size="mini" type="primary">编辑</el-button>
<el-button v-if="['waiting'].includes(row.flow_status)" size="mini" type="danger" @click="onRevoke(row.id)"></el-button>
<el-button v-if="row.taskKey === 'apply' || !row.instanceId" @click="onDelete(row.id)" size="mini" type="danger">
删除
</el-button>
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskKey === 'startTask'" @click="onEdit(row)" size="mini" type="primary">编辑</el-button>
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger"></el-button>
<!-- <el-button v-if="row.instanceState === 10" size="mini" type="danger" @click="onWithDraw(row)">撤销</el-button>-->
<!-- v-if="row.instanceState === 30"-->
<el-button @click="onDelete(row.id)" size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -58,7 +58,9 @@ layout("/layouts/platform.html"){
}
},
methods: {
onOpen(id) {},
openView(row) {
window.open("/flow/common/approval/form?" + "instanceId=" + (row.instanceId || "") + "&businessId=" + row.id + "&defineKey=XWTG")
},
onEdit(row) {
window.open(
"/flow/common/approval/form?taskId=" +
@@ -70,8 +72,50 @@ layout("/layouts/platform.html"){
"&defineKey=XWTG"
)
},
onRevoke(id) {},
onDelete(id) {}
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/flow/common/revokeTask", { taskId: row.startTaskId }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
}
})
})
},
onWithDraw({ instanceId }) {
this.$confirm("您确定要撤销申请吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/flow/common/withdrawInstance", { instanceId }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
}
})
})
},
onDelete(id) {
this.$confirm("您确定要删除吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/platform/suggestionBox2/apply/delete", { id }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
}
})
})
}
},
created() {
this.pageData()
@@ -5,7 +5,7 @@
<el-descriptions-item label="工号">{{viewData.loginName}}</el-descriptions-item>
<el-descriptions-item label="单位">{{viewData.unitName}}</el-descriptions-item>
<el-descriptions-item label="工会">{{viewData.unionName}}</el-descriptions-item>
<el-descriptions-item label="标题" :span="2">{{viewData.viewData}}</el-descriptions-item>
<el-descriptions-item label="标题" :span="2">{{viewData.title}}</el-descriptions-item>
<el-descriptions-item label="填写意见建议内容" :span="2">{{viewData.content}}</el-descriptions-item>
</el-descriptions>
@@ -21,6 +21,7 @@
methods: {
handleTaskAction(val) {
console.log(val)
this.$axios
.post("/flow/common/executeTask", {
data: JSON.stringify({
@@ -32,12 +33,10 @@
if (res.code === 0) {
this.$message.success("操作成功")
// 发送完成消息
window.parent.postMessage(
{
type: "task-complete"
},
"*"
)
window.GlobalBroadcastChannel.postMessage({
type: "task-complete",
payload: val
})
}
})
},
@@ -30,16 +30,17 @@ layout("/layouts/platform.html"){
<el-table-column prop="unitName" label="投稿人单位"></el-table-column>
<el-table-column prop="unionName" label="投稿人工会"></el-table-column>
<el-table-column prop="submitTime" label="投稿时间"></el-table-column>
<el-table-column prop="taskName" label="当前节点"></el-table-column>
<el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
<template slot-scope="{row}">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="300px">
<el-table-column label="操作" fixed="right" width="200px">
<template slot-scope="{row}">
<el-button @click="onOpen(row.id)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskState === 10" @click="openApproval(row)" size="mini" type="primary">审核</el-button>
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回</el-button>
<!-- <el-button v-if="row.taskState === 10" @click="openView(row)" size="mini" type="primary">审核</el-button>-->
</template>
</el-table-column>
</el-table>
@@ -62,9 +63,11 @@ layout("/layouts/platform.html"){
}
},
methods: {
onOpen() {},
openApproval(row) {
openView(row) {
window.open("/flow/common/approval/form?taskId=" + row.taskId + "&instanceId=" + row.instanceId + "&businessId=" + row.businessNo)
},
onRevoke(row) {
console.log(row)
}
},
created() {