This commit is contained in:
那些花儿
2025-07-29 14:25:33 +08:00
parent 72546696fa
commit 9d8002ec93
40 changed files with 1410 additions and 13555 deletions
@@ -5,9 +5,12 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
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.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;
@@ -27,9 +30,7 @@ import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.*;
@IocBean
@At("/flow/common")
@@ -163,6 +164,14 @@ public class FlowCommonController {
return Result.success();
}
@At
@SaCheckLogin
@ApiOperation("撤回流程实例")
public Result withdrawInstance(@Param("instanceId") Long instanceId) {
flowEngine.processInstanceService().withdraw(instanceId, SecurityUtil.getUserId());
return Result.success();
}
@At("/executeTask")
@SaCheckLogin
@@ -177,6 +186,14 @@ public class FlowCommonController {
submitType = ProcessSubmitTypeEnum.AGREE.getCode();
}
args.put(FlowConst.SUBMIT_TYPE, submitType);
// 设置办理人信息到表单参数
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "userName", SecurityUtil.getUserUsername());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "loginName", SecurityUtil.getUserLoginname());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitId", SecurityUtil.getUnitId());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitName", SecurityUtil.getUnitId());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unionId", SecurityUtil.getUnionId());
if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.ROLLBACK.getCode())) {
// 退回上一个节点
flowEngine.executeAndJumpTask(processTaskId, operator, args, null);
@@ -201,4 +218,35 @@ public class FlowCommonController {
return Result.success();
}
@At("/candidate")
@SaCheckLogin
@ApiOperation("获取候选用户")
public Result candidate(@Param("taskId") Long taskId) {
ProcessTask task = flowEngine.processTaskService().fetch(taskId);
ProcessInstance instance = flowEngine.processInstanceService().fetch(task.getProcessInstanceId());
ProcessModel processModel = flowEngine.processDefineService().getProcessModel(instance.getProcessDefineId());
// 是否存在候选处理
List<TaskModel> nextTaskModels = processModel.getNextTaskModels(task.getTaskName());
boolean b = nextTaskModels.stream().anyMatch(item -> item.getCandidateHandler() != null);
List<Candidate> candidates = processModel.getNextTaskModelCandidates(task.getTaskName());
return Result.success(Map.of("candidates", candidates, "support", b));
}
@At("/jumpTaskNames")
@SaCheckLogin
@ApiOperation("获取可跳转的任务节点")
public Result jumpAbleTaskNames(@Param("instanceId") Long instanceId) {
List<LabelValueVO> vos = flowEngine.processTaskService().jumpAbleTaskNameList(instanceId);
return Result.success(vos);
}
@At("/surrogate")
@SaCheckLogin
@ApiOperation("代理")
public Result surrogate(@Param("taskId") Long taskId, @Param("actorIds") String[] actorIds) {
flowEngine.processTaskService().addTaskActor(taskId, Arrays.asList(actorIds));
return Result.success();
}
}
@@ -4,11 +4,10 @@ import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.AssignmentHandler;
import com.budwk.app.flow.engine.CandidateHandler;
import com.budwk.app.flow.entity.ProcessDesign;
import com.budwk.app.flow.service.ProcessDesignService;
import com.budwk.app.sys.services.SysUserService;
@@ -47,6 +46,8 @@ public class FlowDesignController {
// 获取所有任务参与者处理类
public static final List<JSONObject> ASSIGMENT_HANDLER_LIST;
// 获取所有候选用户处理类
public static final List<JSONObject> CANDIDATE_HANDLER_LIST;
static {
Set<Class<?>> classes = ClassScanner.scanPackageBySuper("com.budwk.app", AssignmentHandler.class);
@@ -68,6 +69,26 @@ public class FlowDesignController {
ASSIGMENT_HANDLER_LIST = Collections.unmodifiableList(list);
}
static {
Set<Class<?>> classes = ClassScanner.scanPackageBySuper("com.budwk.app", CandidateHandler.class);
ArrayList<JSONObject> list = new ArrayList<>(classes.size());
for (Class<?> aClass : classes) {
try {
CandidateHandler handler = (CandidateHandler) ReflectUtil.newInstance(aClass);
JSONObject jsonObject = new JSONObject();
jsonObject.set("value", handler.getClass().getName());
jsonObject.set("order", handler.getOrder());
jsonObject.set("name", handler.getMessage());
list.add(jsonObject);
} catch (Exception e) {
log.error("初始化CandidateHandler失败: {}", aClass.getName());
}
}
// 排序 按order
list.sort(Comparator.comparingInt(o -> o.getInt("order")));
CANDIDATE_HANDLER_LIST = Collections.unmodifiableList(list);
}
@At("")
@Ok("beetl:/platform/flow/design/index.html")
@@ -101,6 +122,8 @@ 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());
processDesign.setContent(jsonObject);
dao.insert(processDesign);
return Result.success();
@@ -116,6 +139,8 @@ 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());
processDesign.setContent(jsonObject);
processDesignService.update(processDesign);
return Result.success();
@@ -125,6 +150,15 @@ public class FlowDesignController {
@SaCheckLogin
@ApiOperation("修改流程设计")
public Result updateContent(@Param("design") ProcessDesign processDesign) {
JSONObject jsonObject = processDesign.getContent();
jsonObject.set("name", processDesign.getName());
jsonObject.set("displayName", processDesign.getDisplayName());
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());
processDesign.setContent(jsonObject);
processDesignService.update(processDesign);
return Result.success();
}
@@ -160,9 +194,17 @@ public class FlowDesignController {
@ApiOperation("获取流程设计任务参与者处理类")
public Result assigmentHandlerClass() {
return Result.success(ASSIGMENT_HANDLER_LIST);
}
@At
@SaCheckLogin
@ApiOperation("获取流程设计任务参与者处理类")
public Result candidateHandlerClass() {
return Result.success(CANDIDATE_HANDLER_LIST);
}
@At
@SaCheckLogin
@ApiOperation("获取流程设计任务参与者分页数据")
@@ -19,4 +19,12 @@ public interface CandidateHandler {
* @return
*/
List<Candidate> handle(TaskModel model);
default String getMessage() {
return this.getClass().getSimpleName();
}
default int getOrder() {
return Integer.MIN_VALUE;
}
}
@@ -25,6 +25,8 @@ public class ProcessModel extends BaseModel {
private String category; // 流程定义分类
private String instanceUrl; // 启动实例要填写的表单key
private String h5InstanceUrl; // 启动实例要填写的手机端表单key
private String instanceViewUrl;
private String h5InstanceViewUrl;
private String expireTime; // 期待完成时间变量key
private String instanceNoClass; // 实例编号生成器实现类
private String preInterceptors; // 节点前置拦截器
@@ -74,9 +76,7 @@ public class ProcessModel extends BaseModel {
NodeModel nodeModel = getNode(nodeName);
if(nodeModel == null) return res;
// 获取所有输出边的目标节点
List<NodeModel> nextNodeModelList = nodeModel.getOutputs().stream().map(item->{
return item.getTarget();
}).collect(Collectors.toList());
List<NodeModel> nextNodeModelList = nodeModel.getOutputs().stream().map(TransitionModel::getTarget).toList();
nextNodeModelList.forEach(item->{
if(item instanceof TaskModel) {
res.add((TaskModel) item);
@@ -15,6 +15,9 @@ public class LfModel extends BaseModel {
private String type; // 流程定义分类
private String expireTime;// 过期时间(常量或变量)
private String instanceUrl; // 启动实例的url,前后端分离后,定义为路由名或或路由地址
private String h5InstanceUrl;
private String instanceViewUrl;
private String h5InstanceViewUrl;
private String instanceNoClass; // 启动流程时,流程实例的流水号生成类
private String preInterceptors; // 节点前置拦截器
private String postInterceptors; // 节点后置拦截器
@@ -79,6 +79,9 @@ public class ModelParser {
processModel.setDisplayName(lfModel.getDisplayName());
processModel.setType(lfModel.getType());
processModel.setInstanceUrl(lfModel.getInstanceUrl());
processModel.setH5InstanceUrl(lfModel.getH5InstanceUrl());
processModel.setInstanceViewUrl(lfModel.getInstanceViewUrl());
processModel.setH5InstanceViewUrl(lfModel.getH5InstanceViewUrl());
processModel.setInstanceNoClass(lfModel.getInstanceNoClass());
processModel.setPostInterceptors(lfModel.getPostInterceptors());
processModel.setPreInterceptors(lfModel.getPreInterceptors());
@@ -47,6 +47,14 @@ public class ProcessDefine extends BaseModel {
@Column
private String h5InstanceUrl;
@Comment("电脑端发起地址")
@Column
private String instanceViewUrl;
@Comment("手机端发起地址")
@Column
private String h5InstanceViewUrl;
@Comment("图标")
@Column
private String icon;
@@ -50,6 +50,14 @@ public class ProcessDesign extends BaseModel {
@Column
private String h5InstanceUrl;
@Comment("电脑端发起地址")
@Column
private String instanceViewUrl;
@Comment("手机端发起地址")
@Column
private String h5InstanceViewUrl;
@Comment("图标")
@Column
private String icon;
@@ -34,4 +34,16 @@ public class ProcessTaskActor extends BaseModel {
@Column
private String actorId;
@Comment("参与者账号")
@Column
private String actorAccount;
@Comment("参与者名称")
@Column
private String actorName;
@Comment("参与者单位")
@Column
private String actorUnitName;
}
@@ -0,0 +1,30 @@
package com.budwk.app.flow.handler;
import cn.hutool.core.lang.Dict;
import com.budwk.app.flow.engine.CandidateHandler;
import com.budwk.app.flow.engine.model.TaskModel;
import com.budwk.app.flow.entity.Candidate;
import java.util.List;
//@IocBean 此处加了会从容器里面获取 反之反射获取
public class FLowDemoCandidateHandler implements CandidateHandler {
@Override
public List<Candidate> handle(TaskModel model) {
Candidate build = Candidate.builder().userId("1").userName("测试1").ext(
Dict.of("loginName", "superadmin", "unitName", "工会")
).build();
return List.of(build);
}
@Override
public String getMessage() {
return "候选人测试DEMO";
}
@Override
public int getOrder() {
return 10;
}
}
@@ -120,6 +120,12 @@ public class ProcessDefineServiceImpl extends BaseServiceImpl<ProcessDefine> imp
define.setCategory(processModel.getCategory());
define.setInstanceUrl(processModel.getInstanceUrl());
define.setH5InstanceUrl(processModel.getH5InstanceUrl());
define.setInstanceViewUrl(processModel.getInstanceViewUrl());
define.setH5InstanceViewUrl(processModel.getH5InstanceViewUrl());
// define.setInstanceViewUrl(latestDefine.getInstanceViewUrl());
// define.setH5InstanceViewUrl(latestDefine.getH5InstanceViewUrl());
define.setState(1);
define.setContent(JSONUtil.parseObj(defineJsonStr));
insert(define);
@@ -7,7 +7,6 @@ import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
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.exception.BaseException;
@@ -35,9 +34,12 @@ import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import org.nutz.aop.interceptor.ioc.TransAop;
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.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import java.util.ArrayList;
import java.util.Collection;
@@ -201,16 +203,31 @@ public class ProcessTaskServiceImpl extends BaseServiceImpl<ProcessTask> impleme
public void addTaskActor(Long processTaskId, List<String> actors) {
if (CollectionUtil.isEmpty(actors)) return;
List<String> dbActors = getTaskActors(processTaskId);
actors.stream().filter(actor -> {
return !dbActors.contains(actor);
}).forEach(actor -> {
List<String> newActors = actors.stream().filter(actor -> !dbActors.contains(actor)).toList();
Sql sql = Sqls.create("select id,username,loginname,unitname from vw_user where id in (@ids)");
sql.setParam("ids", newActors);
List<NutMap> list = listMap(sql);
for (NutMap actor : list) {
ProcessTaskActor processTaskActor = new ProcessTaskActor();
processTaskActor.setProcessTaskId(processTaskId);
processTaskActor.setActorId(actor);
processTaskActor.setActorId(actor.getString("id"));
processTaskActor.setActorName(actor.getString("username"));
processTaskActor.setActorAccount(actor.getString("loginname"));
processTaskActor.setActorUnitName(actor.getString("unitname"));
processTaskActor.setCreatedAt(System.currentTimeMillis());
System.out.println(StrUtil.format("给任务:{},添加参与者:{}", processTaskId, actor));
System.out.println(StrUtil.format("给任务:{},添加参与者:{}", processTaskId, actor.toString()));
insert(processTaskActor);
});
}
// actors.stream().filter(actor -> !dbActors.contains(actor)).forEach(actor -> {
// ProcessTaskActor processTaskActor = new ProcessTaskActor();
// processTaskActor.setProcessTaskId(processTaskId);
// processTaskActor.setActorId(actor);
// processTaskActor.setCreatedAt(System.currentTimeMillis());
// System.out.println(StrUtil.format("给任务:{},添加参与者:{}", processTaskId, actor));
// insert(processTaskActor);
// });
}
@Override
@@ -2,7 +2,9 @@ package com.budwk.app.flow.vo;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.core.Execution;
import com.budwk.app.flow.engine.model.TaskModel;
import com.budwk.app.flow.entity.ProcessTask;
@@ -39,6 +41,9 @@ public class ProcessTaskVO extends ProcessTask {
private Dict taskFormData;
// @Schema(description = "任务参与者ID")
private List<String> taskActorIdList;
private List<JSONObject> taskActorList;
// @Schema(description = "当前用户是否可执行")
private boolean executable;
// @Schema(description = "节点定义信息")
@@ -47,7 +52,6 @@ public class ProcessTaskVO extends ProcessTask {
if(this.ext!=null) return this.ext;
String variable = getVariable();
if(ObjectUtil.isEmpty(ext) && JSONUtil.isTypeJSON(variable)){
// this.ext = JsonTool.fromJson(variable, Dict.class);
this.ext = JSONUtil.toBean(variable, Dict.class);
}
return ext;
@@ -57,37 +61,21 @@ public class ProcessTaskVO extends ProcessTask {
if(this.instanceExt!=null) return this.instanceExt;
String variable = getVariable();
if(ObjectUtil.isEmpty(instanceExt) && JSONUtil.isTypeJSON(instanceVariable)){
// this.instanceExt = JsonTool.fromJson(instanceVariable,Dict.class);
this.instanceExt = JSONUtil.toBean(instanceVariable, Dict.class);
}
return instanceExt;
}
// public Dict getTaskFormData() {
// // f_前辍才是表单数据
// this.taskFormData = Dict.of();
// Dict ext = this.getExt();
// List<String> formDataKeys = ext.keySet().stream().filter(key->key.startsWith(FlowConst.TASK_FORM_DATA_PREFIX)).collect(Collectors.toList());
// formDataKeys.forEach(key->{
// this.taskFormData.put(key,ext.get(key));
// this.taskFormData.put(key.replaceAll(FlowConst.TASK_FORM_DATA_PREFIX,""),ext.get(key));
// });
// return taskFormData;
// }
/**
* 后续个任务
*/
private List<TaskModel> nextTaskModels;
// private List<TaskModel> nextTaskModels() {
// List<TaskModel> nextModels = new ArrayList<>();
//
// TaskModel model = getTaskModel();
//
//
// }
public Dict getTaskFormData() {
// f_前辍才是表单数据
this.taskFormData = Dict.of();
Dict ext = this.getExt();
List<String> formDataKeys = ext.keySet().stream().filter(key->key.startsWith(FlowConst.TASK_FORM_DATA_PREFIX)).toList();
formDataKeys.forEach(key->{
this.taskFormData.put(key,ext.get(key));
this.taskFormData.put(key.replaceAll(FlowConst.TASK_FORM_DATA_PREFIX,""),ext.get(key));
});
return taskFormData;
}
}
@@ -0,0 +1,68 @@
package com.budwk.app.zhgh.dayofficework.huimin.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.dayofficework.huimin.models.Huimin;
import com.budwk.app.zhgh.dayofficework.huimin.service.HuiminService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
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 org.nutz.mvc.annotation.Param;
@IocBean
@At("/platform/huimin/manage")
@Api(("惠民服务管理"))
@Slf4j
@Ok("json:full")
public class HuiminManageController {
@Inject
private HuiminService huiminService;
@At("")
@SaCheckLogin
@Ok("beetl:/platform/zhgh/dayofficework/huimin/manage/index.html")
public void index() {
}
@At
@SaCheckLogin
public Result pageData(PageForm pageForm) {
Cnd cnd = Cnd.NEW();
Pagination pagination = huiminService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
return Result.success(pagination);
}
@At
@SaCheckLogin
@ApiOperation("新增惠民服务")
public Result insert(@Param("data") Huimin huimin) {
huiminService.insert(huimin);
return Result.success();
}
@At
@SaCheckLogin
@ApiOperation("修改惠民服务")
public Result update(@Param("data") Huimin huimin) {
huiminService.updateIgnoreNull(huimin);
return Result.success();
}
@At
@SaCheckLogin
@ApiOperation("删除惠民服务")
public Result delete(String id) {
huiminService.delete(id);
return Result.success();
}
}
@@ -0,0 +1,42 @@
package com.budwk.app.zhgh.dayofficework.huimin.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("huimin")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("惠民服务")
public class Huimin extends BaseModel {
@Name
@Comment("id")
@PrevInsert(uu32 = true)
private String id;
@Comment("标题")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Column
private String title;
@Comment("封面")
@ColDefine(type = ColType.VARCHAR, width = 255)
@Column
private String cover;
@Comment("内容")
@ColDefine(type = ColType.TEXT)
@Column
private String content;
@Comment("是否启用")
@ColDefine(type = ColType.BOOLEAN)
@Column
@Default("1")
private Boolean enable;
}
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.dayofficework.huimin.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.huimin.models.Huimin;
public interface HuiminService extends BaseService<Huimin> {
}
@@ -0,0 +1,14 @@
package com.budwk.app.zhgh.dayofficework.huimin.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.huimin.models.Huimin;
import com.budwk.app.zhgh.dayofficework.huimin.service.HuiminService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class HuiminServiceImpl extends BaseServiceImpl<Huimin> implements HuiminService {
public HuiminServiceImpl(Dao dao) {
super(dao);
}
}
@@ -0,0 +1,82 @@
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.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.suggestionBox.service.SuggestionBoxService;
import io.swagger.annotations.Api;
import org.nutz.dao.Cnd;
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;
@IocBean
@At("/platform/suggestionBox2/apply")
@Ok("json:full")
@Api("意见箱")
public class SuggestionApplyController {
@Inject
private SuggestionBoxService suggestionBoxService;
@At("/index")
@Ok("beetl:/platform/zhgh/dayofficework/suggestionBox/apply/index.html")
@SaCheckLogin
public void index(){
}
@At("/form")
@Ok("beetl:/platform/zhgh/dayofficework/suggestionBox/apply/form.html")
@SaCheckLogin
public void form() {
}
@At
@SaCheckLogin
public Result pageData(Integer pageNumber, Integer pageSize) {
Sql sql = Sqls.create("""
SELECT
info.id,
info.title,
info.userName,
info.loginName,
info.unitName,
info.unionName,
info.submitTime,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariale,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariale
FROM
suggestion_box info
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("info.createdBy", "=", SecurityUtil.getUserId());
cnd.desc("info.submitTime");
sql.setCondition(cnd);
Pagination<NutMap> pagination = suggestionBoxService.listPageMap(pageNumber, pageSize, sql);
return Result.success(pagination);
}
}
@@ -36,34 +36,34 @@ public class SuggestionBoxController {
@Inject
private FlowEngine flowEngine;
@At
@SaCheckLogin
@Ok("beetl:/platform/zhghh5/democratic/suggestionBox/index.html")
public void apply() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/suggestionBox/index.html")
@SaCheckLogin
public void h5Index() {
}
@At("/h5/write")
@Ok("beetl:/platform/zhghh5/democratic/suggestionBox/write.html")
@SaCheckLogin
public void h5Write() {
}
@At("/h5/mine")
@Ok("beetl:/platform/zhghh5/democratic/suggestionBox/mine.html")
@SaCheckLogin
public void h5Mine() {
}
// @At
// @SaCheckLogin
// @Ok("beetl:/platform/zhghh5/democratic/suggestionBox/index.html")
// public void apply() {
//
// }
//
//
// @At("/h5")
// @Ok("beetl:/platform/zhghh5/democratic/suggestionBox/index.html")
// @SaCheckLogin
// public void h5Index() {
//
// }
//
// @At("/h5/write")
// @Ok("beetl:/platform/zhghh5/democratic/suggestionBox/write.html")
// @SaCheckLogin
// public void h5Write() {
//
// }
//
// @At("/h5/mine")
// @Ok("beetl:/platform/zhghh5/democratic/suggestionBox/mine.html")
// @SaCheckLogin
// public void h5Mine() {
//
// }
@At
@SaCheckLogin
@@ -0,0 +1,36 @@
package com.budwk.app.zhgh.democratic.suggestionBox.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.democratic.suggestionBox.models.SuggestionBox;
import io.swagger.annotations.Api;
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;
@IocBean
@At("/platform/suggestionBox/view")
@Ok("json:full")
@Api("意见箱")
public class SuggestionViewController {
@Inject
private Dao dao;
@At("/index")
@Ok("beetl:/platform/zhgh/dayofficework/suggestionBox/view/index.html")
@SaCheckLogin
public void index() {
}
@At("/info")
@SaCheckLogin
public Result info(String id) {
SuggestionBox box = dao.fetch(SuggestionBox.class, id);
return Result.success(box);
}
}
@@ -0,0 +1,105 @@
package com.budwk.app.zhgh.democratic.suggestionBox.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.suggestionBox.service.SuggestionBoxService;
import io.swagger.annotations.Api;
import org.nutz.dao.Cnd;
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 java.util.List;
@IocBean
@At("/platform/suggestionBox2/xgh")
@Ok("json:full")
@Api("意见箱")
public class SuggestionXghController {
@Inject
private SuggestionBoxService suggestionBoxService;
@At("/index")
@Ok("beetl:/platform/zhgh/dayofficework/suggestionBox/xgh/index.html")
@SaCheckLogin
public void index() {
}
@At("/form")
@Ok("beetl:/platform/zhgh/dayofficework/suggestionBox/xgh/form.html")
@SaCheckLogin
public void form() {
}
@At
@SaCheckLogin
public Result pageData(PageForm pageForm, String title, Integer year, boolean approval) {
Sql sql = Sqls.create("""
SELECT
info.id,
info.title,
info.userName,
info.loginName,
info.unitName,
info.unionName,
info.submitTime,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariale,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariale
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
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", "c6331519-031c-4ec5-92b0-bc7d9647bff4");
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
if (approval) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.WITHDRAW.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
cnd.desc("t.createdAt");
cnd.andEX("YEAR(info.submitTime)", "=", year);
cnd.and(Cnd.likeEX("info.title", title));
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("info.submitTime");
} else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
sql.setCondition(cnd);
Pagination<NutMap> pageVO = suggestionBoxService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pageVO);
}
}
@@ -11,18 +11,17 @@ import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.json.Json;
public class SuggestionBoxInterceptor implements FlowInterceptor {
public class SuggestionBoxApplyInterceptor implements FlowInterceptor {
@Override
public void intercept(Execution execution) {
System.out.println("..................");
String formDataStr = execution.getArgs().getStr(FlowConst.FORM_DATA);
SuggestionBox suggestionBox = Json.fromJson(SuggestionBox.class, formDataStr);
Dao dao = ServiceContext.find(Dao.class);
dao.insertOrUpdate(suggestionBox);
execution.getArgs().set(FlowConst.FORM_DATA, Json.toJson(suggestionBox));
int instanceId = execution.getArgs().getInt(FlowConst.PROCESS_INSTANCE_ID_KEY);
dao.update(ProcessInstance.class, Chain.make("businessNo", suggestionBox.getId()), Cnd.where(ProcessInstance::getId, "=", instanceId));
}
}
@@ -26,37 +26,37 @@ public class SuggestionBox extends BaseModel {
@Column
@Comment("提交人ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String submitterId;
private String userId;
@Column
@Comment("提交人姓名")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String submitterName;
private String userName;
@Column
@Comment("提交人工号")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String submitterLoginName;
private String loginName;
@Column
@Comment("提交人单位ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String submitterUnitId;
private String unitId;
@Column
@Comment("提交人单位名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String submitterUnitName;
private String unitName;
@Column
@Comment("提交人分工会ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String submitterUnionId;
private String unionId;
@Column
@Comment("提交人分工会名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String submitterUnionName;
private String unionName;
@Column
@Comment("提交时间")
@@ -0,0 +1,7 @@
package com.budwk.app.zhgh.democratic.suggestionBox.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.democratic.suggestionBox.models.SuggestionBox;
public interface SuggestionBoxService extends BaseService<SuggestionBox> {
}
@@ -0,0 +1,14 @@
package com.budwk.app.zhgh.democratic.suggestionBox.service.impl;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.democratic.suggestionBox.models.SuggestionBox;
import com.budwk.app.zhgh.democratic.suggestionBox.service.SuggestionBoxService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
public class SuggestionBoxServiceImpl extends BaseServiceImpl<SuggestionBox> implements SuggestionBoxService {
public SuggestionBoxServiceImpl(Dao dao) {
super(dao);
}
}
@@ -665,7 +665,6 @@ td.no-b {
.flow-task-form .el-descriptions-item__label.is-bordered-label {
color: rgb(102, 102, 102) !important;
font-weight: bold !important;
background: rgb(248, 249, 250) !important;
padding: 8px 12px !important;
border: 1px solid rgb(221, 221, 221) !important;
@@ -1,6 +1,7 @@
const initTableMixins = {
data() {
return {
pageDataUrl: "",
submitLoading: false,
searchMore: false,
tableSize: "",
@@ -32,7 +33,7 @@ const initTableMixins = {
}
},
methods: {
dropdownCommand({action, value}) {
dropdownCommand({ action, value }) {
if (action) action(value)
},
columnChange(val) {
@@ -64,8 +65,8 @@ const initTableMixins = {
this.pageForm.pageSize = val
this.pageData()
},
pageData(url = null, data = null) {
const address = url ? url : loc() + "/pageData"
pageData(data = null) {
const address = this.pageDataUrl ? this.pageDataUrl : loc() + "/pageData"
this.tableLoading = true
this.$axios.post(address, data ? data : this.pageForm).then((res) => {
this.tableLoading = false
@@ -1,6 +1,7 @@
const initTableMixins = {
data() {
return {
pageDataUrl: "",
submitLoading: false,
searchMore: false,
tableSize: "",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,13 +6,13 @@
<div class="title-section">
<h2 class="page-title">{{ pageTitle }}</h2>
<div class="page-breadcrumb">
<span>工作流管理</span>
<span class="separator">></span>
<!-- <span>工作流管理</span>-->
<!-- <span class="separator">></span>-->
<span>{{ isNewApplication ? "发起申请" : "任务处理" }}</span>
</div>
</div>
<div class="status-section">
<span class="status-label">状态</span>
<span class="status-label">流程状态</span>
<span class="status-badge" :class="statusClass">{{ processStatusText }}</span>
</div>
</div>
@@ -69,7 +69,7 @@
<!-- 历史办理过程面板 -->
<div v-if="shouldShowHistoryProcess" class="history-panel">
<div class="panel-header">
<h3 class="panel-title">办理过程</h3>
<h3 class="panel-title">详细办理过程</h3>
</div>
<div class="panel-content">
<div id="history-process-container" class="form-content">
@@ -330,7 +330,6 @@ module.exports = {
// 加载申请表单
async loadApplicationForm() {
debugger
if (!this.defineKey || !this.processDefinition.instanceUrl) return
this.pjaxLoading.apply = true
@@ -401,23 +400,25 @@ module.exports = {
// 加载历史办理过程
async loadHistoryProcess() {
if (!this.businessId) return
const { instanceViewUrl } = this.processInstance.jsonObject
this.pjaxLoading.historyProcess = true
$.pjax({
url: `/platform/article/common/fullForm?businessId=${this.businessId}`,
container: "#history-process-container",
push: false,
replace: false,
timeout: 10000
})
.done(() => {
this.pjaxLoading.historyProcess = false
})
.fail(() => {
this.pjaxLoading.historyProcess = false
console.log("历史办理过程加载失败")
if (instanceViewUrl) {
$.pjax({
url: `${instanceViewUrl}?businessId=${this.businessId}&instanceId=${this.instanceId}`,
container: "#history-process-container",
push: false,
replace: false,
timeout: 10000
})
.done(() => {
this.pjaxLoading.historyProcess = false
})
.fail(() => {
this.pjaxLoading.historyProcess = false
console.log("历史办理过程加载失败")
})
}
},
// 显示流程图
@@ -1,5 +1,18 @@
<template>
<div class="snaker-task-action" v-if="!loading || taskInfo">
<!-- 指定下一节点处理人 -->
<div class="next-handler-section" v-if="!isFirstTaskNodeOrNew && supportCandidate">
<el-checkbox v-model="specifyNextHandler" @change="onSpecifyNextHandlerChange">指定下一节点处理人</el-checkbox>
<div class="handler-select-wrapper" v-if="specifyNextHandler">
<el-select v-model="selectedHandler" placeholder="请选择处理人" clearable filterable class="handler-select">
<el-option v-for="candidate in candidates" :key="candidate.userId" :label="candidate.userName" :value="candidate.userId">
<span style="float: left">{{ candidate.userName }}({{ candidate.ext?.loginName }})</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ candidate.ext?.unitName }}</span>
</el-option>
</el-select>
</div>
</div>
<!-- 第一个任务节点或新申请显示提交保存草稿取消 -->
<template v-if="isFirstTaskNodeOrNew">
<el-button type="primary" @click="handleSubmit" :loading="actionLoading" icon="el-icon-upload2">提交</el-button>
@@ -75,6 +88,11 @@ module.exports = {
NORMAL: 0, // 普通任务
COUNTERSIGN: 1 // 会签任务
},
// 指定下一节点处理人相关
specifyNextHandler: false, // 是否指定下一节点处理人
selectedHandler: null, // 选中的处理人ID
candidates: [], // 候选人列表
supportCandidate: false,
taskId: GetQueryString("taskId"),
instanceId: GetQueryString("instanceId"),
@@ -104,6 +122,7 @@ module.exports = {
// 只有当有任务ID时才加载任务信息
if (this.taskId) {
this.loadTaskInfo()
this.loadCandidates()
}
},
methods: {
@@ -149,8 +168,8 @@ module.exports = {
handleAction(actionKey) {
if (this.actionLoading || !this.taskInfo) return
// 触发父组件事件,传递操作类型和任务信息
this.$emit("task-action", {
// 构建操作数据
const actionData = {
submitType: this.actionEnum[actionKey],
taskId: this.taskId,
processTaskId: this.taskId,
@@ -158,7 +177,15 @@ module.exports = {
defineId: this.defineId,
defineKey: this.defineKey,
businessId: this.businessId
})
}
// 如果指定了下一节点处理人,添加到操作数据中
if (this.specifyNextHandler && this.selectedHandler) {
actionData.tf_nextNodeOperator = this.selectedHandler
}
// 触发父组件事件,传递操作类型和任务信息
this.$emit("task-action", actionData)
},
// 保存草稿
@@ -168,7 +195,7 @@ module.exports = {
this.$emit("save-draft", {
submitType: this.actionEnum.APPLY,
defineKey: this.defineKey,
defineId: this.defineId,
defineId: this.defineId
})
},
@@ -182,6 +209,25 @@ module.exports = {
if (this.taskId) {
this.loadTaskInfo()
}
},
// 处理指定下一节点处理人勾选框变化
onSpecifyNextHandlerChange(checked) {
if (!checked) {
this.selectedHandler = null
}
},
// 加载候选人列表
loadCandidates() {
$.get("/flow/common/candidate", {
taskId: this.taskId
}).then((res) => {
if (res.code === 0) {
this.candidates = res.data.candidates
this.supportCandidate = res.data.support
}
})
}
}
}
@@ -208,6 +254,25 @@ module.exports = {
min-height: 60px;
}
/* 指定下一节点处理人样式 */
.next-handler-section {
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
text-align: left;
}
.handler-select-wrapper {
margin-top: 10px;
}
.handler-select {
width: 100%;
max-width: 300px;
}
/* 响应式 */
@media (max-width: 768px) {
.snaker-task-action {
@@ -221,5 +286,14 @@ module.exports = {
width: 100%;
margin: 0;
}
.next-handler-section {
margin-bottom: 15px;
padding: 10px;
}
.handler-select {
max-width: 100%;
}
}
</style>
@@ -1,149 +1,173 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>流程设计器</title>
<script src="/assets/platform/plugins/vue/vue.js"></script>
<script src="/assets/platform/plugins/jquery/jquery.js"></script>
<script src="/assets/platform/plugins/element-ui/lib/index.js"></script>
<link rel="stylesheet" href="https://cdn.staticfile.net/element-ui/2.15.14/theme-chalk/index.css" />
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index.css" />
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/override.css" />
<link rel="stylesheet" href="${base!}/assets/platform/css/root.css" />
<!-- 引入 core 包和对应 css-->
<script src="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.12/dist/logic-flow.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.12/dist/style/index.css" />
<script src="/assets/platform/plugins/snaker/SnakerflowDesigner.umd.js"></script>
<style>
#app {
height: 100vh;
}
</style>
</head>
<body>
<div id="app">
<snaker-flow-designer ref="designer"
v-model="flowData"
@on-save="handleSave"
:show-doc="false"
node-render-type="html"
:extend-property-keys="[
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>流程设计器</title>
<script src="/assets/platform/plugins/vue/vue.js"></script>
<script src="/assets/platform/plugins/jquery/jquery.js"></script>
<script src="/assets/platform/plugins/element-ui/lib/index.js"></script>
<link rel="stylesheet" href="https://cdn.staticfile.net/element-ui/2.15.14/theme-chalk/index.css" />
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index.css" />
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/override.css" />
<link rel="stylesheet" href="${base!}/assets/platform/css/root.css" />
<!-- 引入 core 包和对应 css-->
<script src="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.12/dist/logic-flow.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.12/dist/style/index.css" />
<script src="/assets/platform/plugins/snaker/SnakerflowDesigner.umd.js"></script>
<style>
#app {
height: 100vh;
}
</style>
</head>
<body>
<div id="app">
<snaker-flow-designer
ref="designer"
v-model="flowData"
@on-save="handleSave"
:show-doc="false"
node-render-type="html"
:extend-property-keys="[
'candidateUsers',
'candidateGroups',
'candidateHandler',
'assigneeText',
'assigneeMode'
]"
>
<!-- <template v-slot:form-item-task-form="{model,field}">-->
<!-- <el-form-item label="form">-->
<!-- -->
<!-- </el-form-item>-->
<!-- </template>-->
>
<!-- <template v-slot:form-item-task-form="{model,field}">-->
<!-- <el-form-item label="form">-->
<!-- -->
<!-- </el-form-item>-->
<!-- </template>-->
<template v-slot:form-item-task-assignee="{model,field}">
<assignee-form :model="model" :field="field"
:start_node_next_node_ids="startNodeNextNodeIds"></assignee-form>
</template>
<template v-slot:form-item-task-assignee="{model,field}">
<assignee-form :model="model" :field="field" :start_node_next_node_ids="startNodeNextNodeIds"></assignee-form>
</template>
<template v-slot:form-item-task-assignment-handler="{model,field}">
<el-form-item label="参与者处理类">
<el-select v-model="model[field]" style="width: 100%" clearable
:disabled="startNodeNextNodeIds.includes(model.name)">
<el-option v-for="item in assignmentHandlerClassOptions" :key="item.value"
:value="item.value"
:label="item.name">
</el-option>
</el-select>
</el-form-item>
</template>
<template v-slot:form-item-task-assignment-handler="{model,field}">
<el-form-item label="参与者处理类">
<el-select v-model="model[field]" style="width: 100%" clearable :disabled="startNodeNextNodeIds.includes(model.name)">
<el-option
v-for="item in assignmentHandlerClassOptions"
:key="item.value"
:value="item.value"
:label="item.name"
></el-option>
</el-select>
</el-form-item>
</template>
</snaker-flow-designer>
</div>
</body>
<script>
<!--#include('assigneeForm.js'){}#-->
<template v-slot:form-item-task-candidate-handler="{model,field}">
<el-form-item label="候选者处理类">
<el-select v-model="model[field]" style="width: 100%" clearable :disabled="startNodeNextNodeIds.includes(model.name)">
<el-option
v-for="item in candidateHandlerClassOptions"
:key="item.value"
:value="item.value"
:label="item.name"
></el-option>
</el-select>
</el-form-item>
</template>
Vue.use(SnakerflowDesigner.default)
const vue = new Vue({
el: "#app",
components: {
"assignee-form": assigneeForm
},
data() {
return {
id: "${id!}",
designerData: {},
flowData: {},
assignmentHandlerClassOptions: []
}
},
computed: {
startNodeNextNodeIds() {
if (this.$refs.designer && this.$refs.designer.lf) {
const graphData = this.$refs.designer.lf.getGraphData()
if (graphData) {
const { nodes, edges } = graphData
const startNode = nodes.find(v => v.type === "snaker:start")
if (startNode) {
const targetNodeIds = edges.filter(v => v.sourceNodeId === startNode.id).map(v => v.targetNodeId)
return targetNodeIds
<!-- <template v-slot="{model,field}">-->
<!-- <el-form-item label="表单查看URL">-->
<!-- <el-input v-model="model[instanceViewUrl]"></el-input>-->
<!-- </el-form-item>-->
<!-- </template>-->
</snaker-flow-designer>
</div>
</body>
<script>
<!--#include('assigneeForm.js'){}#-->
Vue.use(SnakerflowDesigner.default)
const vue = new Vue({
el: "#app",
components: {
"assignee-form": assigneeForm
},
data() {
return {
id: "${id!}",
designerData: {},
flowData: {},
assignmentHandlerClassOptions: [],
candidateHandlerClassOptions: []
}
},
computed: {
startNodeNextNodeIds() {
if (this.$refs.designer && this.$refs.designer.lf) {
const graphData = this.$refs.designer.lf.getGraphData()
if (graphData) {
const { nodes, edges } = graphData
const startNode = nodes.find((v) => v.type === "snaker:start")
if (startNode) {
const targetNodeIds = edges.filter((v) => v.sourceNodeId === startNode.id).map((v) => v.targetNodeId)
return targetNodeIds
}
}
}
return []
}
return []
}
},
methods: {
handleSave(val) {
const design = {
...this.designerData,
content: val.json
}
$.post("/flow/design/updateContent", { design: JSON.stringify(design) }).then(res => {
if (res.code === 0) {
window.parent.postMessage("success")
} else {
window.parent.postMessage("error")
}
})
},
getDetail() {
$.get("/flow/design/detail", { id: this.id }).then((res) => {
if (res.code === 0) {
this.designerData = res.data
if (res.data.content) {
this.flowData = res.data.content
methods: {
handleSave(val) {
const design = {
...this.designerData,
content: val.json
}
$.post("/flow/design/updateContent", { design: JSON.stringify(design) }).then((res) => {
if (res.code === 0) {
window.parent.postMessage("success")
} else {
this.flowData = {}
window.parent.postMessage("error")
}
}
})
})
},
getDetail() {
$.get("/flow/design/detail", { id: this.id }).then((res) => {
if (res.code === 0) {
this.designerData = res.data
if (res.data.content) {
this.flowData = res.data.content
} else {
this.flowData = {}
}
}
})
},
init() {
$.get("/flow/design/assigmentHandlerClass").then((res) => {
if (res.code === 0) {
this.assignmentHandlerClassOptions = res.data
}
})
$.get("/flow/design/candidateHandlerClass").then((res) => {
if (res.code === 0) {
this.candidateHandlerClassOptions = res.data
}
})
},
getData() {
console.log(this.$refs.designer)
}
},
init() {
$.get("/flow/design/assigmentHandlerClass").then((res) => {
if (res.code === 0) {
this.assignmentHandlerClassOptions = res.data
}
})
created() {
this.init()
},
getData() {
console.log(this.$refs.designer)
mounted() {
if (this.id) {
this.getDetail()
}
}
},
created() {
this.init()
},
mounted() {
if (this.id) {
this.getDetail()
}
}
})
</script>
})
</script>
</html>
@@ -77,6 +77,12 @@ layout("/layouts/platform.html"){
<el-form-item label="手机端发起地址" prop="h5InstanceUrl">
<el-input v-model="formData.h5InstanceUrl" placeholder="请输入手机端发起地址"></el-input>
</el-form-item>
<el-form-item label="电脑端查看地址" prop="instanceViewUrl">
<el-input v-model="formData.instanceViewUrl" placeholder="请输入电脑端发起地址"></el-input>
</el-form-item>
<el-form-item label="手机端查看地址" prop="h5InstanceViewUrl">
<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-form-item>
@@ -0,0 +1,105 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="名称">
<el-input v-model="pageForm.displayName" placeholder="名称" clearable></el-input>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool>
<el-button type="primary" @click="onAdd" size="small" icon="el-icon-plus">新增</el-button>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="title" label="标题"></el-table-column>
<el-table-column prop="isDeployed" label="是否启用">
<template slot-scope="{row}">
<el-tag size="mini" v-if="row.enable" type="success"></el-tag>
<el-tag size="mini" v-else type="info"></el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="250px">
<template slot-scope="{row}">
<el-button size="mini" type="primary" @click="onEdit(row)">编辑</el-button>
<el-button type="danger" size="mini" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
<el-dialog :title="formData.id ? '编辑' : '新增'" :visible.sync="dialogVisible" width="70%">
<el-form :model="formData" ref="formRef" label-width="120px">
<el-form-item label="标题" prop="title" :rules="{ required: true, message: '请输入标题', trigger: 'blur' }">
<el-input v-model="formData.title" placeholder="请输入标题"></el-input>
</el-form-item>
<el-form-item label="封面图" prop="cover" :rules="{ required: true, message: '请上传封面图', trigger: 'blur' }">
<file-upload
:value.sync="formData.cover"
:upload_number="1"
upload_mode="image"
upload_result_category="interval"
upload_result_type="url"
></file-upload>
</el-form-item>
<el-form-item label="内容" prop="content" :rules="{ required: true, message: '请输入内容', trigger: 'blur' }">
<text-editor v-model="formData.content"></text-editor>
</el-form-item>
<el-form-item label="是否启用" prop="enable">
<el-switch v-model="formData.enable"></el-switch>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="onSubmit">确定</el-button>
</template>
</el-dialog>
</div>
<script>
new Vue({
el: "#app",
store,
mixins: [initTableMixins],
data() {
return {
dialogVisible: false,
formData: {}
}
},
methods: {
onAdd() {
this.formData = {}
this.dialogVisible = true
},
onEdit(row) {
this.dialogVisible = true
this.formData = { ...row }
},
onSubmit() {
$.post("/platform/huimin/manage/" + (this.formData.id ? "update" : "insert"), { data: JSON.stringify(this.formData) }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
this.dialogVisible = false
}
})
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,106 @@
<div id="suggestion-box-apply-form" v-cloak>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" label-suffix="" class="flow-task-form">
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{formData.userName}}</el-descriptions-item>
<el-descriptions-item label="工号">{{formData.loginName}}</el-descriptions-item>
<el-descriptions-item label="单位">{{formData.unitName}}</el-descriptions-item>
<el-descriptions-item label="工会">{{formData.unionName}}</el-descriptions-item>
<el-descriptions-item label="标题" :span="2">
<el-form-item prop="title" label="标题">
<el-input v-model="formData.title" maxlength="100" show-word-limit placeholder="请输入标题"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="填写意见建议内容" :span="2">
<el-form-item prop="content" label="填写意见建议内容">
<el-input v-model="formData.content" type="textarea" maxlength="500" show-word-limit placeholder="请输入内容"></el-input>
</el-form-item>
</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>
</div>
<script>
new Vue({
el: "#suggestion-box-apply-form",
store,
data() {
return {
businessId: GetQueryString("businessId"),
formData: {},
formRules: {
title: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
content: [{ required: true, message: "必填", trigger: ["change", "blur"] }]
}
}
},
methods: {
handleTaskAction(val) {
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" }, "*")
}
})
}
})
},
handleSaveDraft(val) {
this.$refs.formRef.validateField(["title"], (errMsg) => {
if (errMsg) {
this.$message.warning("请填写标题")
return
}
this.$axios
.post("/flow/common/startInstance", {
...val,
bizData: JSON.stringify(this.formData)
})
.then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
// 发送完成消息
window.parent.postMessage(
{
type: "task-complete"
},
"*"
)
}
})
})
},
handleCancel() {
// 关闭浏览器窗口
window.close()
},
init() {
if (this.businessId) {
} else {
const { username, loginname, id, unit, union, mobile } = this.$store.state.user
this.formData = {
userName: username,
loginName: loginname,
submitterId: id,
unitId: unit?.id,
unitName: unit?.name,
unionId: union?.id,
unionName: union?.name,
concat: mobile
}
}
}
},
created() {
this.init()
}
})
</script>
@@ -0,0 +1,84 @@
<!--#
layout("/layouts/platform.html"){
#-->
<guava ref="guava">
<div id="app">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" placeholder="年度" style="width: 100%"></el-date-picker>
</search-item>
<search-item label="标题">
<el-input v-model="pageForm.title" placeholder="标题" clearable></el-input>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool></table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="title" label="投稿标题"></el-table-column>
<el-table-column prop="loginName" label="投稿人工号"></el-table-column>
<el-table-column prop="userName" label="投稿人姓名"></el-table-column>
<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="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">
<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>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</div>
</guava>
<script>
new Vue({
el: "#app",
store,
mixins: [initTableMixins],
data() {
return {
pageDataUrl: "/platform/suggestionBox2/apply/pageData"
}
},
methods: {
onOpen(id) {},
onEdit(row) {
window.open(
"/flow/common/approval/form?taskId=" +
(row.taskId || "") +
"&instanceId=" +
(row.instanceId || "") +
"&businessId=" +
row.id +
"&defineKey=XWTG"
)
},
onRevoke(id) {},
onDelete(id) {}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,88 @@
<div id="suggestion-box-view">
<!-- <el-form ref="formRef" label-width="0" label-suffix="" class="flow-task-form">-->
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{viewData.userName}}</el-descriptions-item>
<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.content}}</el-descriptions-item>
</el-descriptions>
<template v-for="task in doneTasks">
<div class="task-panel mt10">
<div class="task-panel-header">{{ task.displayName }}</div>
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-if="task.ext.isFirstTaskNode">
<el-descriptions-item label="申请用户">{{ task.ext.initiatorName }}({{task.ext.initiatorAccount}})</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>
<el-descriptions-item label="办理结果">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
</el-descriptions-item>
</el-descriptions>
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>
<el-descriptions-item label="办理用户">{{ task.taskFormData.userName }}({{task.taskFormData.loginName}})</el-descriptions-item>
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
<el-descriptions-item label="办理结果">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
</el-descriptions-item>
<el-descriptions-item label="办理意见" v-if="!task.ext.isFirstTaskNode">{{ task.taskFormData.opinion }}</el-descriptions-item>
</el-descriptions>
</div>
</template>
<!-- </el-form>-->
</div>
<script>
new Vue({
el: "#suggestion-box-view",
store,
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
data() {
return {
businessId: GetQueryString("businessId"),
instanceId: GetQueryString("instanceId"),
viewData: {},
doneTasks: []
}
},
methods: {
info() {
this.$axios.post("/platform/suggestionBox/view/info", { id: this.businessId }).then((res) => {
if (res.code === 0) {
this.viewData = res.data
}
})
},
getDoneTasks() {
this.$axios.post("/flow/common/doneTasks", { instanceId: this.instanceId }).then((res) => {
if (res.code === 0) {
this.doneTasks = res.data
}
})
}
},
created() {
this.info()
this.getDoneTasks()
}
})
</script>
<style>
.task-panel {
background: white;
border-radius: 4px;
overflow: hidden;
}
.task-panel-header {
background: rgb(250, 250, 250);
border: 1px solid rgb(228, 231, 237);
border-bottom: none;
padding: 12px 16px;
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
@@ -0,0 +1,49 @@
<div id="suggestion-box-apply-form" v-cloak>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" label-suffix="" class="flow-task-form">
<el-form-item label="审批意见" prop="tf_opinion" :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item>
</el-form>
<snaker-flow-task-form-action @task-action="handleTaskAction" @cancel="handleCancel"></snaker-flow-task-form-action>
</div>
<script>
new Vue({
el: "#suggestion-box-apply-form",
store,
data() {
return {
businessId: GetQueryString("businessId"),
formData: {},
formRules: {}
}
},
methods: {
handleTaskAction(val) {
console.log(val)
this.$axios
.post("/flow/common/executeTask", {
data: JSON.stringify({
...val,
...this.formData
})
})
.then((res) => {
if (res.code === 0) {
this.$message.success("操作成功")
// 发送完成消息
window.parent.postMessage(
{
type: "task-complete"
},
"*"
)
}
})
},
handleCancel(val) {
console.log(val)
}
}
})
</script>
@@ -0,0 +1,78 @@
<!--#
layout("/layouts/platform.html"){
#-->
<guava ref="guava">
<div id="app">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" placeholder="年度" style="width: 100%"></el-date-picker>
</search-item>
<search-item label="标题">
<el-input v-model="pageForm.title" placeholder="标题" clearable></el-input>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool>
<el-radio-group v-model="pageForm.approval" size="small" @change="doSearch">
<el-radio-button :label="true">已审核</el-radio-button>
<el-radio-button :label="false">未审核</el-radio-button>
</el-radio-group>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="title" label="投稿标题"></el-table-column>
<el-table-column prop="loginName" label="投稿人工号"></el-table-column>
<el-table-column prop="userName" label="投稿人姓名"></el-table-column>
<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="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">
<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>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</div>
</guava>
<script>
new Vue({
el: "#app",
store,
mixins: [initTableMixins],
data() {
return {
pageDataUrl: "/platform/suggestionBox2/xgh/pageData",
pageForm: {
approval: false
}
}
},
methods: {
onOpen() {},
openApproval(row) {
window.open("/flow/common/approval/form?taskId=" + row.taskId + "&instanceId=" + row.instanceId + "&businessId=" + row.businessNo)
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->