提案流程

This commit is contained in:
2026-10-09 10:06:08 +08:00
parent fc39bc8020
commit aff992faf0
55 changed files with 1150 additions and 708 deletions
@@ -50,6 +50,7 @@ RoleConstant {
BRANCH_UNION_WENTI_SPORTS("分工会文体福利委员"),
UNIT_PARTY_SECRETARY("单位党委书记"),
UNIT_SCHOOL_LEADER("分管校领导"),
TEACHER_CONGRESS_DELEGATE_FORMAL("教代会正式代表"),
TEACHER_CONGRESS_DELEGATE_ATTENDANCE("教代会列席代表"),
@@ -0,0 +1,54 @@
package com.budwk.app.flow.handler;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.flow.engine.AssignmentHandler;
import com.budwk.app.flow.engine.core.Execution;
import com.budwk.app.flow.engine.core.ServiceContext;
import com.budwk.app.flow.engine.model.TaskModel;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import java.util.List;
/**
* 为校工会主席审核任务分配拥有校工会主席角色的用户。
*/
public class FlowSchoolUnionChairmanHandler implements AssignmentHandler {
/**
* 按校工会主席角色查询参与者,去除同一用户的重复角色记录。
*
* @param model 当前流程任务模型,无需额外传入人员参数
* @param execution 当前流程执行上下文,无需额外传入业务参数
* @return 用户 ID 列表,供流程引擎创建任务参与者;未配置角色用户时返回空列表
*/
@Override
public List<String> assign(TaskModel model, Execution execution) {
Dao dao = ServiceContext.find(Dao.class);
Sql sql = Sqls.create("""
SELECT
userRole.userId
FROM
`sys_user_role` userRole
LEFT JOIN sys_role role ON role.id = userRole.roleId
WHERE
role.`code` = @roleCode
GROUP BY
userRole.userId
""").setParam("roleCode", RoleConstant.SCHOOL_UNION_CHAIRMAN.name());
sql.setCallback(Sqls.callback.strList());
dao.execute(sql);
return sql.getList(String.class);
}
@Override
public String getMessage() {
return "获取校工会主席";
}
@Override
public int getOrder() {
return AssignmentHandler.super.getOrder();
}
}
@@ -0,0 +1,49 @@
package com.budwk.app.flow.handler;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.flow.engine.AssignmentHandler;
import com.budwk.app.flow.engine.core.Execution;
import com.budwk.app.flow.engine.core.ServiceContext;
import com.budwk.app.flow.engine.model.TaskModel;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import java.util.List;
/**
* 为分管校领导审核任务分配拥有 UNIT_SCHOOL_LEADER 角色的用户。
*/
public class FlowUnitSchoolLeaderHandler implements AssignmentHandler {
/**
* 按角色查询参与者,去除同一用户的重复角色记录。
*
* @param model 当前流程任务模型,无需额外传入人员参数
* @param execution 当前流程执行上下文,无需额外传入业务参数
* @return 用户 ID 列表,供流程引擎创建任务参与者;未配置角色用户时返回空列表
*/
@Override
public List<String> assign(TaskModel model, Execution execution) {
Dao dao = ServiceContext.find(Dao.class);
Sql sql = Sqls.create("""
SELECT
userRole.userId
FROM
sys_user_role userRole
LEFT JOIN sys_role role ON role.id = userRole.roleId
WHERE
role.`code` = @roleCode
GROUP BY
userRole.userId
""").setParam("roleCode", RoleConstant.UNIT_SCHOOL_LEADER.name());
sql.setCallback(Sqls.callback.strList());
dao.execute(sql);
return sql.getList(String.class);
}
@Override
public String getMessage() {
return "获取分管校领导";
}
}
@@ -11,7 +11,8 @@ import java.util.Map;
public interface SysDataUserPullService extends BaseService<Sys_user_source> {
/**
* 从信息中心拉取数据
* 从本地中间表同步单位,并将人员写入 sys_user_source 快照;不更新 sys_user。
* 返回本次人员快照的拉取时间。
*/
Date pull();
@@ -1,15 +1,6 @@
package com.budwk.app.sys.services.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.config.DataCenterProperties;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_unit;
@@ -17,41 +8,24 @@ import com.budwk.app.sys.services.SysDataUnitPullService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
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.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @version 1.0
* @Author zzr
* @name:SysDataUnitPullServiceImpl
* @Date 2025/9/15 17:52
* @注释
* 将本地单位中间表同步到系统单位表。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class SysDataUnitPullServiceImpl extends BaseServiceImpl<Sys_unit> implements SysDataUnitPullService {
private static final String DMP_UNIT_KEY = "unit";
private static final int DMP_PAGE_SIZE = 1000;
@Inject
private DataCenterProperties dataCenterProperties;
public SysDataUnitPullServiceImpl(Dao dao) {
super(dao);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void updateUnits() {
@@ -59,240 +33,65 @@ public class SysDataUnitPullServiceImpl extends BaseServiceImpl<Sys_unit> implem
}
/**
* 拉取单位数据
* 从 unit_middle 同步全部二级单位。
* unitIds 是旧调用链传入的已有单位 ID 列表;中间表同步按全表执行,不依赖此参数。
* 返回值为空;已存在单位只更新中间表明确提供的字段。
*/
@Override
@Aop(TransAop.READ_COMMITTED)
public void updateUnits(List<String> unitIds) {
String accessToken = getDmpAccessToken();
List<JSONObject> rawDataList = distinctDmpUnits(pullDmpUnits(getDmpUnitUrl(), accessToken));
Set<String> parentCodes = rawDataList.stream()
.map(this::getNormalizedParentCode)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
rawDataList.stream()
.sorted(Comparator.comparingInt(this::getUnitLevel))
.forEach(raw -> saveOrUpdateUnit(raw, parentCodes));
log.info("信息中心单位数据同步完成,本次接口返回单位数量: {},待检查单位编码数量: {}", rawDataList.size(), unitIds == null ? 0 : unitIds.size());
}
/**
* 获取信息中心开放平台访问令牌。
* 参数来自 data-center.token-url、data-center.key、data-center.secret;
* 返回值为后续单位接口 body 中 access_token 使用的字符串。
*/
private String getDmpAccessToken() {
String tokenUrl = dataCenterProperties.getTokenUrl();
if (StrUtil.isBlank(tokenUrl)) {
throw new BaseException("未配置信息中心token地址: data-center.token-url");
}
String key = dataCenterProperties.getKey();
if (StrUtil.isBlank(key)) {
throw new BaseException("未配置信息中心token key: data-center.key");
}
String secret = dataCenterProperties.getSecret();
if (StrUtil.isBlank(secret)) {
throw new BaseException("未配置信息中心token secret: data-center.secret");
List<NutMap> middleUnits = listMap(Sqls.create("SELECT DWDM, DWMC, BMFLMC FROM unit_middle"));
if (middleUnits.isEmpty()) {
throw new BaseException("本地单位中间表没有数据,已停止同步");
}
String params = URLUtil.buildQuery(Map.of("key", key, "secret", secret), StandardCharsets.UTF_8);
String tokenResBody = HttpUtil.createGet(tokenUrl + "?" + params).execute().body();
JSONObject tokenJsonBody = JSONUtil.parseObj(tokenResBody);
String accessToken = tokenJsonBody.getStr("access_token");
if (StrUtil.isBlank(accessToken) && tokenJsonBody.getJSONObject("result") != null) {
accessToken = tokenJsonBody.getJSONObject("result").getStr("access_token");
}
if (StrUtil.isBlank(accessToken) && tokenJsonBody.getJSONObject("data") != null) {
accessToken = tokenJsonBody.getJSONObject("data").getStr("access_token");
}
if (StrUtil.isBlank(accessToken)) {
accessToken = tokenJsonBody.getStr("token");
}
if (StrUtil.isBlank(accessToken)) {
throw new BaseException("获取信息中心token失败");
}
return accessToken;
}
/**
* 读取信息中心单位接口地址。
* 配置项为 data-center.urls.unit,返回值为单位全量接口URL。
*/
private String getDmpUnitUrl() {
String url = dataCenterProperties.getUrls().get(DMP_UNIT_KEY);
if (StrUtil.isBlank(url)) {
throw new BaseException("未配置信息中心单位接口地址: data-center.urls." + DMP_UNIT_KEY);
}
return url;
}
/**
* 分页拉取单位接口数据。
* 入参 url 为单位接口地址,accessToken 为信息中心令牌;
* 请求 body 包含 access_token、per_page、page;
* 返回值为 result.data 合并后的单位原始 JSON 列表。
*/
private List<JSONObject> pullDmpUnits(String url, String accessToken) {
List<JSONObject> rawDataList = new ArrayList<>();
int page = 1;
int maxPage = 1;
int total = 0;
do {
HttpRequest httpRequest = HttpUtil.createPost(url);
httpRequest.header(Header.CONTENT_TYPE, "application/json");
httpRequest.body(JSONUtil.toJsonStr(Map.of(
"access_token", accessToken,
"per_page", String.valueOf(DMP_PAGE_SIZE),
"page", String.valueOf(page)
)));
log.info("请求信息中心单位数据,第{}页: {}", page, httpRequest);
String resBody = httpRequest.execute().body();
log.info("请求信息中心单位数据第{}页结果: {}", page, resBody);
JSONObject jsonBody = JSONUtil.parseObj(resBody);
if (jsonBody.getInt("code") != 10000) {
throw new BaseException("获取信息中心单位数据失败,错误码: " + jsonBody.getInt("code") + ";错误原因:" + jsonBody.getStr("message"));
for (NutMap raw : middleUnits) {
String unitCode = StrUtil.trim(raw.getString("DWDM"));
String unitName = StrUtil.trim(raw.getString("DWMC"));
if (StrUtil.isBlank(unitCode) || unitCode.length() != 3 || StrUtil.isBlank(unitName)) {
throw new BaseException("单位中间表存在无效的二级单位代码或名称,已停止同步");
}
JSONObject result = jsonBody.getJSONObject("result");
if (result == null) {
break;
Sys_unit unit = fetch(unitCode);
if (unit == null) {
unit = new Sys_unit();
unit.setId(unitCode);
unit.setParentId("0");
unit.setPath(getUnitPath("0"));
unit.setAliasName(unitName);
unit.setUnitTypeCode(1);
unit.setHasChildren(false);
unit.setName(unitName);
unit.setUnitcode(unitCode);
unit.setUnitLevel(2);
if (StrUtil.isNotBlank(raw.getString("BMFLMC"))) {
unit.setUnitType(raw.getString("BMFLMC"));
}
dao().fastInsert(unit);
} else {
unit.setName(unitName);
unit.setUnitcode(unitCode);
unit.setUnitLevel(2);
unit.setUnitTypeCode(1);
unit.setParentId("0");
if (StrUtil.isBlank(unit.getPath())) {
unit.setPath(getUnitPath("0"));
}
if (StrUtil.isNotBlank(raw.getString("BMFLMC"))) {
unit.setUnitType(raw.getString("BMFLMC"));
}
dao().updateIgnoreNull(unit);
}
total = result.getInt("total", total);
maxPage = result.getInt("max_page", maxPage);
JSONArray data = result.getJSONArray("data");
if (CollUtil.isNotEmpty(data)) {
rawDataList.addAll(data.stream().map(v -> (JSONObject) v).toList());
}
log.info("信息中心单位数据拉取进度: {}/{}", rawDataList.size(), total);
page++;
} while (page <= maxPage);
return rawDataList;
}
log.info("本地单位中间表同步完成,单位数量: {}", middleUnits.size());
}
/**
* 按单位代码去重,避免接口分页或源数据重复导致同一 DWDM 重复写入。
*/
private List<JSONObject> distinctDmpUnits(List<JSONObject> rawDataList) {
Map<String, JSONObject> unitMap = new LinkedHashMap<>();
for (JSONObject raw : rawDataList) {
String unitCode = raw.getStr("DWDM");
if (StrUtil.isBlank(unitCode)) {
continue;
}
unitMap.putIfAbsent(unitCode, raw);
}
return new ArrayList<>(unitMap.values());
}
/**
* 保存或更新单位。
* DWDM 对应系统单位 id/unitcode,DWMC 对应名称,LSDWH 对应父级单位号,DWCC 对应信息中心原始单位层级。
*/
private void saveOrUpdateUnit(JSONObject raw, Set<String> parentCodes) {
String unitCode = raw.getStr("DWDM");
if (StrUtil.isBlank(unitCode)) {
return;
}
String parentId = getParentId(unitCode, getNormalizedParentCode(raw));
Sys_unit unit = fetch(unitCode);
if (unit == null) {
unit = new Sys_unit();
unit.setId(unitCode);
unit.setUnitcode(unitCode);
unit.setPath(getUnitPath(parentId));
setUnitValue(unit, raw, parentId, parentCodes);
dao().fastInsert(unit);
} else {
setUnitValue(unit, raw, parentId, parentCodes);
if (StrUtil.isBlank(unit.getPath())) {
unit.setPath(getUnitPath(parentId));
}
dao().updateIgnoreNull(unit);
}
}
/**
* 根据接口字段给系统单位赋值。
* DWYXBS 表示单位是否有效,DWCC 表示信息中心原始单位层次;
* 系统内置根单位占用一级层级,因此保存时统一将 DWCC 加一。
*/
private void setUnitValue(Sys_unit unit, JSONObject raw, String parentId, Set<String> parentCodes) {
String unitCode = raw.getStr("DWDM");
String unitName = raw.getStr("DWMC");
String aliasName = StrUtil.blankToDefault(raw.getStr("DWJC"), unitName);
Integer rawUnitLevel = raw.getInt("DWCC");
Integer unitLevel = getNormalizedUnitLevel(raw);
boolean availableBusinessUnit = "是".equals(raw.getStr("DWYXBS")) && Integer.valueOf(2).equals(rawUnitLevel);
unit.setParentId(parentId);
unit.setName(unitName);
unit.setAliasName(aliasName);
unit.setUnitcode(unitCode);
unit.setAddress(raw.getStr("DWDZ"));
unit.setUnitLevel(unitLevel);
unit.setUnitType(raw.getStr("DWLB"));
unit.setUnitTypeCode(availableBusinessUnit ? 1 : 0);
unit.setHasChildren(parentCodes.contains(unitCode));
}
/**
* 计算父级单位号。
* LSDWH 为空、等于自身或本地不存在该父级时按根级处理;系统根单位 0 允许直接作为父级。
*/
private String getParentId(String unitCode, String parentCode) {
if (StrUtil.isBlank(parentCode) || unitCode.equals(parentCode)) {
return "";
}
if ("0".equals(parentCode)) {
return parentCode;
}
Sys_unit parentUnit = fetch(parentCode);
return parentUnit == null ? "" : parentCode;
}
/**
* 根据父级 path 生成当前单位 path。
* 父级为空时生成根节点 path,父级存在时在父级 path 下生成子节点 path。
* 按系统根单位的 path 生成新二级单位路径,供单位树正常展示。
*/
private String getUnitPath(String parentId) {
String parentPath = "";
if (StrUtil.isNotBlank(parentId)) {
Sys_unit parentUnit = fetch(parentId);
parentPath = parentUnit == null ? "" : parentUnit.getPath();
}
Sys_unit parentUnit = fetch(parentId);
String parentPath = parentUnit == null ? "" : parentUnit.getPath();
return getSubPath("sys_unit", "path", parentPath);
}
/**
* 信息中心一级单位应挂在系统根单位 0 下,其余单位沿用接口返回的 LSDWH。
*/
private String getNormalizedParentCode(JSONObject raw) {
Integer rawUnitLevel = raw.getInt("DWCC");
if (Integer.valueOf(1).equals(rawUnitLevel)) {
return "0";
}
return raw.getStr("LSDWH");
}
/**
* 系统内置根单位占用一级层级,信息中心单位层次入库时统一加一。
*/
private Integer getNormalizedUnitLevel(JSONObject raw) {
Integer rawUnitLevel = raw.getInt("DWCC");
return rawUnitLevel == null ? null : rawUnitLevel + 1;
}
/**
* DWCC 为信息中心单位层次,排序时用于尽量先处理父级单位,便于后续子级找到父节点。
*/
private int getUnitLevel(JSONObject raw) {
return raw.getInt("DWCC", 0);
}
}
@@ -131,12 +131,18 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
log.info("开始全量更新用户数据");
// 获取角色信息
Sys_role publicRole = sysRoleService.getByCode(RoleConstant.PUBLIC);
// Sys_role publicRole = sysRoleService.getByCode(RoleConstant.PUBLIC);
// 获取单位信息,后续判断单位变更
List<Sys_unit> unitList = dao.query(Sys_unit.class, Cnd.where("unitTypeCode", "=", "1"));
unitIdNameMap = unitList.stream().collect(Collectors.toMap(Sys_unit::getId, Sys_unit::getName));
// 民族来源值为代码,更新系统用户前按现有码表换成名称。
Map<String, String> nationNames = dao.query(Sys_data_dict.class, Cnd.where("parentCode", "=", "USER_NATION"))
.stream()
.filter(dict -> StrUtil.isAllNotBlank(dict.getCode(), dict.getName()))
.collect(Collectors.toMap(Sys_data_dict::getCode, Sys_data_dict::getName, (first, second) -> first));
// 查询数据源
Cnd cnd = Cnd.where(Sys_user_source::getPullTime, "=", updateParam.getPullTime());
@@ -159,8 +165,14 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// 处理每条数据
for (Sys_user_source source : sources) {
normalizeSourceForUpdate(source, nationNames);
if (StrUtil.isBlank(source.getLoginname())) {
log.warn("数据源中存在空工号,跳过该记录");
continue;
}
Sys_user user = userMap.get(source.getLoginname());
source.setMember(null);
// 会员及福利会员由会员业务维护;来源快照中的默认 0 不参与本次人员更新。
clearMembershipFields(source);
String unitId = source.getUnitId();
if (StringUtils.isNotBlank(unitId) && unitId.length() == 5) {
source.setUnitId(unitId.substring(0, 3));
@@ -180,11 +192,6 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
} else {
// 修改现有用户
u.setId(user.getId());
/*
* aidFundMember 由基金会员业务维护,数据中心源表默认值为 0。
* 全量更新已有用户时置空,避免 updateIgnoreNull 把已迁移的基金会员标识覆盖为非会员。
*/
u.setAidFundMember(null);
u.setPersonType(null);
needDoUpdateList.add(u);
}
@@ -220,6 +227,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
updateTasks.add(insertTask);
// 异步处理公共角色分配
/* 暂停人员更新时的角色变动;新用户仅写基本信息。
if (!needInitUserList.isEmpty()) {
CompletableFuture<Void> roleTask = CompletableFuture.runAsync(() -> {
try {
@@ -248,6 +256,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
}, executorService);
// 不等待角色分配完成
}
*/
}
// 2. 更新现有用户 - 使用批量处理
@@ -352,10 +361,12 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// 6. 处理其他任务
// 6.1 更新提案的校领导角色,发送订阅
/* 暂停人员更新触发的角色刷新。
ProposalConfig config = dao.fetch(ProposalConfig.class, Cnd.where("delFlag", "=", false).desc("updatedAt"));
for (String unitId : config.getSchoolLeaderUnitIds()) {
RoleEventPublisher.broadcast(new RoleEventMsg(unitId, RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name(), RoleEventMsg.RENEW_ROLE));
}
*/
/*
* 所有异步用户更新完成后再校正待确认状态,避免主事务提前锁定 sys_user,
@@ -373,6 +384,52 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
+ " 条";
}
/**
* 更新前将来源记录的空白字符串转为 null,供 updateIgnoreNull 跳过,同时避免生成空白变更记录。
* 入参 source 为本次 sys_user_source 记录,nationNames 为 USER_NATION 的代码名称映射;
* 方法直接规范化 source,无返回值。未匹配到的非空民族代码保留原值。
*/
private void normalizeSourceForUpdate(Sys_user_source source, Map<String, String> nationNames) {
for (Field field : cn.hutool.core.util.ReflectUtil.getFields(Sys_user_source.class)) {
if (field.getType() != String.class) {
continue;
}
try {
field.setAccessible(true);
String value = (String) field.get(source);
field.set(source, StrUtil.isBlank(value) ? null : StrUtil.trim(value));
} catch (IllegalAccessException e) {
throw new IllegalStateException("规范化人员来源字段失败: " + field.getName(), e);
}
}
if ("1".equals(source.getSex())) {
source.setSex("男");
} else if ("2".equals(source.getSex())) {
source.setSex("女");
}
if (StrUtil.isNotBlank(source.getNation())) {
source.setNation(nationNames.getOrDefault(source.getNation(), source.getNation()));
}
}
/**
* 将人员来源中的会员字段置空,避免快照默认值修改系统会员和福利会员状态。
* source 是当前内存中的 sys_user_source 记录;方法不更新来源表,无返回值。
*/
private void clearMembershipFields(Sys_user_source source) {
source.setMember(null);
source.setMemberTime(null);
source.setWelfareMember(null);
source.setWantJoinLoveMutualAidAssociation(null);
source.setPreparationMemberFee(null);
source.setContractMemberFee(null);
source.setAidFundMember(null);
source.setAidFundMemberUserType(null);
source.setAidFundDeductTime(null);
source.setAidFundMemberJoinTime(null);
source.setAidFundMemberQuitTime(null);
}
/**
* 根据指定批次的完整数据源校正系统用户待确认状态。
* 已人工确认且仍未重新出现的人员保持已确认状态,避免每次全量更新重复转为待确认。
@@ -489,7 +546,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// }
// 2. 单位变更
if (!ObjectUtil.equals(user.getUnitId(), source.getUnitId())) {
if (StrUtil.isNotBlank(source.getUnitId()) && !ObjectUtil.equals(user.getUnitId(), source.getUnitId())) {
changeTypes.add(MemberChangeType.UNIT_CHANGE.name());
NutMap change = NutMap.NEW();
change.put("fieldName", "单位");
@@ -499,8 +556,9 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
changeList.add(change);
// 单位异动发送订阅消息,清空原单位的所有角色
UserChangePublisher.broadcast(new UserChangeMsg(List.of(user.getId()),
UserChangeMsg.UNIT_CHANGE_OPERATION, user.getUnitId(), source.getUnitId()));
// 单位代码和历史仍更新,角色清理由会员及权限业务另行处理。
// UserChangePublisher.broadcast(new UserChangeMsg(List.of(user.getId()),
// UserChangeMsg.UNIT_CHANGE_OPERATION, user.getUnitId(), source.getUnitId()));
}
// 如果没有任何变更,返回null
@@ -27,8 +27,11 @@ import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.random.R;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* 增量更新系统用户数据
@@ -58,6 +61,17 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
// 执行查询
List<Sys_user_source> userSources = dao.query(Sys_user_source.class, cnd);
// 新增人员也按系统显示值保存性别和民族,空白来源字段不写入新用户。
Map<String, String> nationNames = dao.query(Sys_data_dict.class, Cnd.where("parentCode", "=", "USER_NATION"))
.stream()
.filter(dict -> StrUtil.isAllNotBlank(dict.getCode(), dict.getName()))
.collect(Collectors.toMap(Sys_data_dict::getCode, Sys_data_dict::getName, (first, second) -> first));
userSources.forEach(source -> normalizeSourceForUpdate(source, nationNames));
userSources = userSources.stream().filter(source -> StrUtil.isNotBlank(source.getLoginname())).toList();
// 增量新增人员不从来源快照复制会员状态及福利会员状态。
userSources.forEach(this::clearMembershipFields);
//加入到系统用户表
List<Sys_user> sysUsers = BeanUtil.copyToList(userSources, Sys_user.class);
for (Sys_user sysUser : sysUsers) {
@@ -88,9 +102,9 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
List<CompletableFuture<List<Sys_user_history>>> splitHistoryFutures = splitHistories.stream().map(v -> CompletableFuture.supplyAsync(() -> dao.fastInsert(v))).toList();
AsyncUtil.waitAll(CompletableFuture.allOf(splitHistoryFutures.toArray(new CompletableFuture[0])));
Sys_role publicRole = sysRoleService.getByCode(RoleConstant.PUBLIC);
// Sys_role publicRole = sysRoleService.getByCode(RoleConstant.PUBLIC);
//添加公共角色
/* 暂停人员更新时的角色分配。
List<Sys_user_role> sysUserRoles = sysUsers.stream().map(user -> {
Sys_user_role sysUserRole = new Sys_user_role();
sysUserRole.setUserId(user.getId());
@@ -101,6 +115,7 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
dao.insert(sysUserRoles);
});
sysRoleService.clearCache();
*/
//比较单位数据
List<Sys_unit> sysUnits = dao.query(Sys_unit.class, Cnd.where(Sys_unit::getUnitLevel, "=", 2));
@@ -130,14 +145,62 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
});
// 6.1 更新提案的校领导角色,发送订阅
/* 暂停人员更新触发的角色刷新。
ProposalConfig config = dao.fetch(ProposalConfig.class, Cnd.where("delFlag", "=", false).desc("updatedAt"));
for (String unitId : config.getSchoolLeaderUnitIds()) {
RoleEventPublisher.broadcast(new RoleEventMsg(unitId, RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name(), RoleEventMsg.RENEW_ROLE));
}
*/
if (sysUsers.isEmpty() && insertUnits.isEmpty()) {
return "本次没有新增人员或单位。";
}
return StrUtil.format("增量更新成功,本次新增人员{}人,新增单位{}个。", sysUsers.size(), insertUnits.size());
}
/**
* 新增用户前规范化来源字段:空白字符串不入库,性别和民族代码转为现有系统名称。
* 入参 source 为 sys_user_source 记录,nationNames 为 USER_NATION 的代码名称映射;
* 方法直接修改内存中的 source,无返回值。未识别的非空编码保持原值。
*/
private void normalizeSourceForUpdate(Sys_user_source source, Map<String, String> nationNames) {
for (Field field : cn.hutool.core.util.ReflectUtil.getFields(Sys_user_source.class)) {
if (field.getType() != String.class) {
continue;
}
try {
field.setAccessible(true);
String value = (String) field.get(source);
field.set(source, StrUtil.isBlank(value) ? null : StrUtil.trim(value));
} catch (IllegalAccessException e) {
throw new IllegalStateException("规范化人员来源字段失败: " + field.getName(), e);
}
}
if ("1".equals(source.getSex())) {
source.setSex("男");
} else if ("2".equals(source.getSex())) {
source.setSex("女");
}
if (StrUtil.isNotBlank(source.getNation())) {
source.setNation(nationNames.getOrDefault(source.getNation(), source.getNation()));
}
}
/**
* 新增系统用户前排除来源快照的会员字段;新账号会员状态按系统默认值初始化。
* source 是当前内存中的 sys_user_source 记录;不修改来源表,无返回值。
*/
private void clearMembershipFields(Sys_user_source source) {
source.setMember(null);
source.setMemberTime(null);
source.setWelfareMember(null);
source.setWantJoinLoveMutualAidAssociation(null);
source.setPreparationMemberFee(null);
source.setContractMemberFee(null);
source.setAidFundMember(null);
source.setAidFundMemberUserType(null);
source.setAidFundDeductTime(null);
source.setAidFundMemberJoinTime(null);
source.setAidFundMemberQuitTime(null);
}
}
@@ -115,96 +115,81 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
@Aop(TransAop.READ_COMMITTED)
public Date pull() {
try {
// 获取数据中心的字典
List<Sys_data_dict> dataDictList = dao().query(Sys_data_dict.class, Cnd.NEW());
// 字典码表Map,key为父级编码,value为子级字典列表
Map<String, List<Sys_data_dict>> dataDictMap = dataDictList.stream().collect(Collectors.groupingBy(Sys_data_dict::getParentCode));
String accessToken = getDmpAccessToken();
List<JSONObject> rawDataList = new ArrayList<>();
rawDataList.addAll(pullDmpUsers(getDmpUrl(DMP_TEACHER_KEY), accessToken, "在岗教职工"));
rawDataList.addAll(pullDmpUsers(getDmpUrl(DMP_DISPATCH_KEY), accessToken, "在岗劳务派遣"));
rawDataList = distinctDmpUsers(rawDataList);
// 先确认本地中间表有人员,避免空表产生一个看似成功的拉取批次。
List<NutMap> rawDataList = listMap(Sqls.create("SELECT * FROM user_middle"));
if (rawDataList.isEmpty()) {
log.warn("当前未获取到人员数据");
throw new BaseException("本地人员中间表没有数据,已停止拉取");
}
// 单位必须先入库;人员快照的 DWDM 已是三位二级单位代码。
sysDataUnitPullService.updateUnits();
Date nowDate = new Date();
// 赋值
List<Sys_user_source> latestSourceList = rawDataList.stream().map(raw -> {
Sys_user_source sysUser = new Sys_user_source();
// 使用缓存的字段映射
for (FieldMapping mapping : getFieldMappings()) {
try {
// 根据字段类型设置值
if (mapping.field.getType() == String.class) {
if (StrUtil.isNotBlank(mapping.dict)) {
List<Sys_data_dict> sysDataDicts = dataDictMap.getOrDefault(mapping.dict, Collections.emptyList());
Sys_data_dict sysDataDict = sysDataDicts.stream()
.filter(v -> raw.getStr(mapping.key, "").equals(v.getCode()))
.findFirst().orElse(new Sys_data_dict());
mapping.field.set(sysUser, StrUtil.blankToDefault(sysDataDict.getName(), raw.getStr(mapping.key)));
} else {
// 特殊处理 根据身份证号获取性别和出生年月
if ("SFZJH".equals(mapping.key)) {
String idCard = raw.getStr(mapping.key);
mapping.field.set(sysUser, idCard);
// 设置出生年月
try {
sysUser.setBirthday(IdcardUtil.getBirthDate(idCard));
} catch (Exception e) {
sysUser.setBirthday(null);
}
} else if ("LXNY".equals(mapping.key)) {
mapping.field.set(sysUser, normalizeArrivalAtSchoolDate(raw.getStr(mapping.key)));
} else {
mapping.field.set(sysUser, raw.getStr(mapping.key));
}
}
} else if (mapping.field.getType() == Date.class) {
mapping.field.set(sysUser, raw.getDate(mapping.key));
}
} catch (IllegalAccessException e) {
log.error("设置字段值失败: {}", mapping.field.getName(), e);
}
Map<String, Sys_user_source> sourceMap = new LinkedHashMap<>();
for (NutMap raw : rawDataList) {
String loginname = StrUtil.trim(raw.getString("ZGH"));
if (StrUtil.isBlank(loginname)) {
throw new BaseException("人员中间表存在空工号,已停止拉取");
}
sysUser.setUnitName(raw.getStr("SZDW"));
sysUser.setUnitId(resolveSourceUnitId(raw.getStr("SZDWH")));
// 设置拉取时间
sysUser.setPullTime(nowDate);
return sysUser;
}).toList();
log.info("用户数据初始化完成,等待插入,当前数据条数{}", latestSourceList.size());
// 插入到数据库
if (CollUtil.isNotEmpty(latestSourceList)) {
dao().insert(latestSourceList);
}
// 人员的单位数据和数据库的单位数据比较,如果人员里面有单位不存在,去更新单位数据
List<String> sourceUnitIds = latestSourceList.stream().map(Sys_user_source::getUnitId).filter(StrUtil::isNotBlank).distinct().toList();
Sql sql = Sqls.create("select id from sys_unit group by id");
sql.setCallback(Sqls.callback.strList());
dao().execute(sql);
List<String> unitIds = sql.getList(String.class);
if (!new HashSet<>(unitIds).containsAll(sourceUnitIds)) {
log.info("单位需要更新,正在同步更新");
sysDataUnitPullService.updateUnits(unitIds);
sourceMap.putIfAbsent(loginname, mapMiddleUser(raw, nowDate));
}
dao().insert(new ArrayList<>(sourceMap.values()));
log.info("本地人员中间表拉取完成,原始记录数: {},去重后记录数: {}", rawDataList.size(), sourceMap.size());
return nowDate;
} catch (Exception e) {
throw new BaseException("数据拉取失败,{}", e.getMessage());
}
}
/**
* 将 user_middle 的一条人员记录映射为本次拉取快照。
* raw 包含工号 ZGH、姓名 XM、单位 DWDM/DWMC 等本地中间表字段;pullTime 是本次批次时间。
* 返回 Sys_user_source,仅用于写入数据源表,不修改 sys_user。
*/
private Sys_user_source mapMiddleUser(NutMap raw, Date pullTime) {
Sys_user_source source = new Sys_user_source();
source.setLoginname(StrUtil.trim(raw.getString("ZGH")));
source.setUsername(middleValue(raw, "XM"));
source.setUnitId(middleValue(raw, "DWDM"));
source.setUnitName(middleValue(raw, "DWMC"));
source.setSex(middleValue(raw, "XB"));
source.setNation(middleValue(raw, "MZ"));
source.setUserState(middleValue(raw, "DQZT"));
source.setPersonType(middleValue(raw, "JZGRYLB"));
source.setEducation(middleValue(raw, "XL"));
source.setMobile(middleValue(raw, "SJH"));
source.setAcademicDegree(middleValue(raw, "XW"));
source.setProfessionalTitle(middleValue(raw, "ZYJSZW"));
source.setProfessionalLevel(middleValue(raw, "ZYJSGWDJ"));
source.setPolitical(middleValue(raw, "ZZMM"));
source.setIdCard(middleValue(raw, "SFZJH"));
source.setBirthday(parseMiddleBirthDate(raw.getString("CSRQ")));
source.setPullTime(pullTime);
return source;
}
/**
* 将中间表的空字符串转为 null,避免后续正式更新时用空值覆盖系统用户字段。
*/
private String middleValue(NutMap raw, String column) {
String value = StrUtil.trim(raw.getString(column));
return StrUtil.isBlank(value) ? null : value;
}
/**
* CSRQ 当前以 yyyy/MM/dd 开头,后面可能包含时间;只取日期部分。
* 空值返回 null,异常格式抛错以阻止生成不完整的拉取批次。
*/
private Date parseMiddleBirthDate(String birthDate) {
if (StrUtil.isBlank(birthDate)) {
return null;
}
String dateText = birthDate.length() >= 10 ? birthDate.substring(0, 10).replace('/', '-') : "";
if (!dateText.matches("\\d{4}-\\d{2}-\\d{2}")) {
throw new BaseException("人员中间表存在异常出生日期,已停止拉取");
}
return DateUtil.parseDate(dateText);
}
/**
* 规范化信息中心返回的来校时间。
* 入参 arrivalAtSchoolDate 对应信息中心 LXNY 字段,允许 yyyy-MM-dd 或 yyyy-MM;
@@ -35,7 +35,6 @@ public class ProposalQueryYearReportController {
*/
private static final Map<String, String> YEAR_REPORT_ORDER_COLUMNS = Map.ofEntries(
Map.entry("code", "info.code"),
Map.entry("caseFilingCode", "info.caseFilingCode"),
Map.entry("name", "info.name"),
Map.entry("createUserName", "info.createUserName"),
Map.entry("typeName", "typeName"),
@@ -97,7 +97,7 @@ public class ProposalSeniorBasicController {
return Result.error("提案编号已存在");
}
dao.update(proposalInfo,"^code|caseFilingCode|name|typeId|brief|measures|excerpt|suggestUnits|files$");
dao.update(proposalInfo,"^code|name|typeId|brief|measures|excerpt|suggestUnits|files$");
return Result.success();
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.lang.Dict;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
@@ -11,6 +12,7 @@ import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingService;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingUnitService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -44,16 +46,24 @@ public class ProposalCommitteeFilingController {
@Inject
private ProposalCommitteeFilingService proposalCommitteeFilingService;
@Inject
private ProposalCommitteeFilingUnitService proposalCommitteeFilingUnitService;
@Inject
private ProposalCommonService proposalCommonService;
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/committeeFiling/index.html")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/committeeFilingUnit/index.html")
@SaCheckPermission("proposal.committeeFiling")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/committeeFiling/index.html")
@SaCheckPermission("h5.proposal.committeeFiling")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.committeeFiling")
@SaCheckPermission(value = {"proposal.committeeFiling", "h5.proposal.committeeFiling"}, mode = SaMode.OR)
@ApiOperation("分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval) {
Sql sql = Sqls.create("""
@@ -112,7 +122,7 @@ public class ProposalCommitteeFilingController {
cnd.desc("t.createdAt");
}
sql.setCondition(cnd);
Pagination pagination = proposalCommitteeFilingService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
Pagination pagination = proposalCommitteeFilingUnitService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
@@ -126,11 +136,11 @@ public class ProposalCommitteeFilingController {
}
@At
@SaCheckPermission("proposal.committeeFiling")
@SaCheckPermission(value = {"proposal.committeeFiling", "h5.proposal.committeeFiling"}, mode = SaMode.OR)
@ApiOperation("执行单案立案审核任务")
@Aop(TransAop.READ_COMMITTED)
public Result executeTask(@Param("data") String data) {
// data 包含流程任务ID、提交类型、立案结果及主协办单位;Service 校验负责人后执行 WF,成功返回统一 Result。
// data 包含流程任务 ID、提交类型、提案 ID、立案结果及主协办单位;Service 先保存立案数据再执行 WF,成功返回统一 Result。
Dict args = Json.fromJson(Dict.class, data);
proposalCommitteeFilingService.executeFlowTask(args);
return Result.success();
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
@@ -34,13 +35,13 @@ import javax.validation.Valid;
import java.util.List;
/**
* 团长审核
* 分工会主席审核
*/
@IocBean
@At("/platform/proposal/delegation")
@Slf4j
@Ok("json:full")
@Api(tags = "提案-办理-团长审核")
@Api(tags = "提案-办理-分工会主席审核")
public class ProposalDelegationController {
@Inject
@@ -60,12 +61,12 @@ public class ProposalDelegationController {
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/delegation/index.html")
@SaCheckPermission("proposal.delegation")
@SaCheckPermission("h5.proposal.delegation")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.delegation")
@SaCheckPermission(value = {"proposal.delegation", "h5.proposal.delegation"}, mode = SaMode.OR)
@ApiOperation("分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm,boolean approval) {
Sql sql = Sqls.create("""
@@ -116,7 +117,7 @@ public class ProposalDelegationController {
}
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
// 团长审核列表未指定有效排序时保留任务创建时间倒序。
// 分工会主席审核列表未指定有效排序时保留任务创建时间倒序。
if (!proposalCommonService.applySafeProposalListOrder(cnd, pageForm, "delegation")) {
cnd.desc("t.createdAt");
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.ExportTableColumns;
@@ -30,15 +31,15 @@ import java.util.List;
/**
* @version 1.0
* @Author FKY
* @name:ProposalPreAuditController
* @name:校工会主席审核(复用原预审核页面)
* @Date 2026/3/16 17:30
* @注释
*/
@IocBean
@At("/platform/proposal/pre/audit")
@At("/platform/proposal/schoolUnionChairApproval")
@Slf4j
@Ok("json:full")
@Api(tags = "提案管理-校工会预审核")
@Api(tags = "提案管理-校工会主席审核")
public class ProposalPreAuditController {
@Inject
@@ -48,18 +49,18 @@ public class ProposalPreAuditController {
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/preAudit/index.html")
@SaCheckPermission("proposal.preAudit")
@SaCheckPermission("proposal.schoolUnionChairApproval")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/preAudit/index.html")
@SaCheckPermission("proposal.preAudit")
@SaCheckPermission("h5.proposal.schoolUnionChairApproval")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.preAudit")
@SaCheckPermission(value = {"proposal.schoolUnionChairApproval", "h5.proposal.schoolUnionChairApproval"}, mode = SaMode.OR)
@ApiOperation("分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval) {
Sql sql = Sqls.create("""
@@ -98,7 +99,8 @@ public class ProposalPreAuditController {
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", "preAudit");
// 校工会主席沿用预审核界面,待办必须按已发布流程的实际节点编码筛选。
cnd.and("t.taskName", "=", "schoolUnionChairApproval");
if(!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())){
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
@@ -113,8 +115,8 @@ public class ProposalPreAuditController {
cnd.andEX("vu.unionId", "=", pageForm.getUnionId());
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
// 预审核列表未指定有效排序时保留任务创建时间倒序。
if (!proposalCommonService.applySafeProposalListOrder(cnd, pageForm, "preAudit")) {
// 校工会主席审核列表未指定有效排序时保留任务创建时间倒序。
if (!proposalCommonService.applySafeProposalListOrder(cnd, pageForm, "schoolUnionChairApproval")) {
cnd.desc("t.createdAt");
}
sql.setCondition(cnd);
@@ -124,10 +126,10 @@ public class ProposalPreAuditController {
@At
@Ok("void")
@SaCheckPermission("proposal.preAudit")
@SaCheckPermission("proposal.schoolUnionChairApproval")
@ApiOperation("导出列表")
public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval,
@Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) {
proposalCommonService.exportWorkflowList("preAudit", pageForm, approval, tableColumns, response);
proposalCommonService.exportWorkflowList("schoolUnionChairApproval", pageForm, approval, tableColumns, response);
}
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
@@ -13,7 +14,6 @@ import com.budwk.app.base.vo.LabelValueVO;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.service.ProcessTaskService;
@@ -70,12 +70,12 @@ public class ProposalSchoolLeaderApprovalController {
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/schoolLeaderApproval/index.html")
@SaCheckPermission("proposal.schoolLeaderApproval")
@SaCheckPermission("h5.proposal.schoolLeaderApproval")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.schoolLeaderApproval")
@SaCheckPermission(value = {"proposal.schoolLeaderApproval", "h5.proposal.schoolLeaderApproval"}, mode = SaMode.OR)
@ApiOperation("分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval) {
// List<LabelValueVO> vos = processTaskService.jumpAbleTaskNameList(280L);
@@ -121,7 +121,7 @@ public class ProposalSchoolLeaderApprovalController {
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", "schoolLeader");
cnd.and("t.taskName", "=", "school_leader");
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
@@ -163,18 +163,13 @@ public class ProposalSchoolLeaderApprovalController {
}
@At
@SaCheckPermission("proposal.schoolLeaderApproval")
@SaCheckPermission(value = {"proposal.schoolLeaderApproval", "h5.proposal.schoolLeaderApproval"}, mode = SaMode.OR)
@ApiOperation("执行任务")
@Aop(TransAop.READ_COMMITTED)
public Result executeTask(@Param("data") String data) {
Dict args = Json.fromJson(Dict.class, data);
if("back".equals(args.getStr("tf_approval"))) {
args.put("submitType", ProcessSubmitTypeEnum.JUMP.getCode());
args.put("taskName", "committee");
flowCommonService.executeTask(args);
return Result.success();
}
// 退回答复由已发布的 WF 分支按 tf_approval=back 流转到主办单位答复。
String proposalId = args.getStr("proposalId");
@@ -44,7 +44,9 @@ public class ProposalUnSubmitQueryController {
Sql sql = Sqls.create("""
SELECT
distinct
info.id,
info.id,
ins.id AS instanceId,
ins.processDefineId AS instanceProcessDefineId,
info.code as proposalCode,
info.name as proposalName,
vu.username,
@@ -55,15 +57,17 @@ public class ProposalUnSubmitQueryController {
manner.name as mannerName,
(select count(DISTINCT(seconderId))from proposal_second where proposalId = info.id) AS secondedNum,
(select count(DISTINCT(seconderId))from proposal_second where proposalId = info.id and isAgree = 1) AS secondedAgreeNum,
IF(info.stateCode <= 200 ,'未审核','已审核') AS delegationAudit
IF(EXISTS (SELECT 1 FROM wf_process_task dt WHERE dt.processInstanceId = ins.id
AND dt.taskName = 'delegation' AND dt.taskState = 20), '已审核', '未审核') AS delegationAudit
FROM
proposal_info info
left join vw_user vu on info.createUserId = vu.id
left join teacher_congress_delegation tcd on info.delegationId = tcd.id
left join proposal_type pt on info.typeId = pt.id
left join sys_dict manner on manner.`code` = info.source
left join `wf_process_instance` ins on ins.businessNo = info.id
left join wf_process_task nt on nt.processInstanceId = ins.id
left join `wf_process_instance` ins on ins.id = (
select latest.id from wf_process_instance latest
where latest.businessNo = info.id order by latest.createdAt desc, latest.id desc limit 1)
$condition
""");
Cnd cnd = Cnd.NEW();
@@ -76,6 +80,8 @@ public class ProposalUnSubmitQueryController {
SqlExpressionGroup group2 = Cnd.exps("ins.state", "=", 30);
group2.or("ins.state", "=", 45);
group2.or(new Static("ins.id not in (select processInstanceId from wf_process_task where taskName='delegation')"));
// 尚未发起 WF 实例的提案也属于未提交汇总,不能被 NOT IN 的 NULL 判断排除。
group2.orIsNull("ins.id");
cnd.and(group2);
if(searchName != null && StrUtil.isNotBlank(searchKeyword)) {
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.base.constant.RoleConstant;
@@ -70,12 +71,12 @@ public class ProposalUnderTakeReplyController {
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/unitReply/index.html")
@SaCheckPermission("proposal.unitReply")
@SaCheckPermission("h5.proposal.unitReply")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.unitReply")
@SaCheckPermission(value = {"proposal.unitReply", "h5.proposal.unitReply"}, mode = SaMode.OR)
@ApiOperation("分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval, boolean transfer) {
/*
@@ -134,8 +135,8 @@ public class ProposalUnderTakeReplyController {
$condition
""");
Cnd cnd = Cnd.NEW();
// cnd.and("t.taskName", "in", List.of("master_reply", "slave_reply", "opinion_master_reply"));
cnd.and("t.taskName", "in", List.of("unit_reply", "two_unit_reply", "opinion_unit_reply"));
// 当前已发布的 WF 版本使用主办、协办和建议办理三个答复节点。
cnd.and("t.taskName", "in", List.of("master_reply", "slave_reply", "opinion_master_reply"));
if (!SecurityUtil.getUserLoginname().equals("superadmin")) {
cnd.and("ta.actorId", "=", SecurityUtil.getUserId());
@@ -159,9 +160,9 @@ public class ProposalUnderTakeReplyController {
for (NutMap row : list) {
/*
* 承办单位答复撤回必须按“当前整组待办节点”统一判断。
* 这里不能再用 contains,只要出现过 unit_reply/two_unit_reply 就会误判可撤回;
* 这里不能再用 contains,否则出现过其他答复节点时会误判可撤回;
* 正确规则是:curTaskKey 必须精确等于某一个允许撤回的节点编码,
* 像 unit_reply,two_unit_reply 这种混合状态一律不允许撤回。
* 多个答复节点同时存在的混合状态一律不允许撤回。
*/
row.setv("canRevoke", isReplyTaskGroupRevocable(row));
}
@@ -173,9 +174,8 @@ public class ProposalUnderTakeReplyController {
/**
* 承办单位答复节点撤回需要基于整组当前节点判断。
* 只有“当前流程正在办理的节点”和“这条列表记录所属节点”完全一致时,才允许撤回;
* 这样流程已经从 unit_reply 走到 two_unit_reply 后,历史 unit_reply 记录会自动失去撤回资格。
* 主办只能撤回 unit_reply、two_unit_reply、school_leader;
* 协办只能撤回 unit_reply、two_unit_reply。
* 这样流程进入后续节点后,历史答复记录会自动失去撤回资格。
* 主办只能在主办或建议办理节点仍为当前节点时撤回;协办只能在协办节点撤回。
*/
private boolean isReplyTaskGroupRevocable(NutMap row) {
if (!ObjectUtil.equal(row.getInt("taskState"), ProcessTaskStateEnum.FINISHED.getCode())) {
@@ -187,10 +187,10 @@ public class ProposalUnderTakeReplyController {
return false;
}
if (row.getInt("underTakeIsMaster") == 1) {
return Stream.of("unit_reply", "two_unit_reply", "school_leader")
return Stream.of("master_reply", "opinion_master_reply")
.anyMatch(a -> ObjectUtil.equal(a, taskKey));
}
return Stream.of("unit_reply", "two_unit_reply")
return Stream.of("slave_reply")
.anyMatch(a -> ObjectUtil.equal(a, taskKey));
}
@@ -200,7 +200,7 @@ public class ProposalUnderTakeReplyController {
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("proposal.unitReply")
@SaCheckPermission(value = {"proposal.unitReply", "h5.proposal.unitReply"}, mode = SaMode.OR)
@ApiOperation("执行任务")
public Result executeTask(@Param("data") String data) {
Dict args = Json.fromJson(Dict.class, data);
@@ -19,8 +19,6 @@ public class ProposalQueryComprehensiveParam extends PageForm {
private String name;
@ApiModelProperty(name = "提案编号")
private String code;
@ApiModelProperty(name = "立案编号")
private String caseFilingCode;
@ApiModelProperty(name = "教代会ID")
private String sessionId;
@ApiModelProperty(name = "search session id")
@@ -63,7 +61,7 @@ public class ProposalQueryComprehensiveParam extends PageForm {
@ApiModelProperty(name = "通用关键字")
private String commonKeyword;
@ApiModelProperty(name = "提案名称或编号搜索字段,仅支持name、code、caseFilingCode")
@ApiModelProperty(name = "提案名称或编号搜索字段,仅支持name、code")
private String proposalSearchField;
@ApiModelProperty(name = "提案名称或编号搜索内容")
@@ -77,7 +75,7 @@ public class ProposalQueryComprehensiveParam extends PageForm {
* 将综合查询参数追加到查询条件中,无返回值,处理结果直接写入 cnd。
*
* @param cnd 查询条件容器,用于接收届次、名称/编号、提案人、组织及立案条件
* @param searchParam 页面查询参数;proposalSearchField 传 name、code 或 caseFilingCode,
* @param searchParam 页面查询参数;proposalSearchField 传 name 或 code,
* proposalKeyword 传对应字段的模糊搜索内容
*/
public static void buildSearch(Cnd cnd, ProposalQueryComprehensiveParam searchParam) {
@@ -93,12 +91,11 @@ public class ProposalQueryComprehensiveParam extends PageForm {
// Isolated single-session filter for pages that should not reuse sessionId semantics.
cnd.andEX("info.sessionId", "=", searchParam.getSearchSessionId());
// 名称/编号组合搜索仅允许三个固定字段,前端提交值不能直接作为SQL字段使用。
// 名称/编号组合搜索仅允许两个固定字段,前端提交值不能直接作为SQL字段使用。
if (StrUtil.isNotBlank(searchParam.getProposalKeyword())) {
String proposalSearchColumn = switch (StrUtil.blankToDefault(searchParam.getProposalSearchField(), "name")) {
case "name" -> "info.name";
case "code" -> "info.code";
case "caseFilingCode" -> "info.caseFilingCode";
default -> null;
};
if (proposalSearchColumn != null) {
@@ -114,10 +111,6 @@ public class ProposalQueryComprehensiveParam extends PageForm {
if (StrUtil.isNotBlank(searchParam.getCode())) {
cnd.where().andLike("info.code", searchParam.getCode());
}
// 立案编号由立案环节生成,支持按已生成编号进行模糊检索。
if (StrUtil.isNotBlank(searchParam.getCaseFilingCode())) {
cnd.where().andLike("info.caseFilingCode", searchParam.getCaseFilingCode());
}
//代表团ID
cnd.andEX("info.delegationId", "=", searchParam.getDelegationId());
if (StrUtil.isNotBlank(searchParam.getCreateUserName())) {
@@ -24,7 +24,7 @@ public class ProposalSearchParam extends PageForm {
private String createUserLoginName;
private String createUserKeyword;
private String caseFilingResult;
/** 名称/编号组合搜索字段,仅支持 name、code、caseFilingCode。 */
/** 名称/编号组合搜索字段,仅支持 name、code。 */
private String proposalSearchField;
/** 名称/编号组合搜索内容。 */
private String proposalKeyword;
@@ -33,7 +33,7 @@ public class ProposalSearchParam extends PageForm {
* 将提案列表参数追加到查询条件中,无返回值,处理结果直接写入 cnd。
*
* @param cnd 查询条件容器,用于接收名称/编号、届次、提案人及立案结果条件
* @param searchParam 页面查询参数;proposalSearchField 传 name、code 或 caseFilingCode,
* @param searchParam 页面查询参数;proposalSearchField 传 name 或 code,
* proposalKeyword 传对应字段的模糊搜索内容
*/
public static void buildSearch(Cnd cnd, ProposalSearchParam searchParam) {
@@ -45,7 +45,6 @@ public class ProposalSearchParam extends PageForm {
String proposalSearchColumn = switch (StrUtil.blankToDefault(searchParam.getProposalSearchField(), "name")) {
case "name" -> "info.name";
case "code" -> "info.code";
case "caseFilingCode" -> "info.caseFilingCode";
default -> null;
};
if (proposalSearchColumn != null) {
@@ -103,7 +103,6 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
/** 征集进度页面允许排序的字段,避免排序参数直接拼接到SQL。 */
private static final Map<String, String> COLLECT_PROGRESS_ORDER_COLUMNS = Map.ofEntries(
Map.entry("code", "info.code"),
Map.entry("caseFilingCode", "info.caseFilingCode"),
Map.entry("name", "info.name"),
Map.entry("createUserName", "info.createUserName"),
Map.entry("delegationName", "delegationName"),
@@ -116,7 +115,6 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
/** 征集进度页面允许自定义导出的字段及固定表头。 */
private static final Map<String, String> COLLECT_PROGRESS_EXPORT_COLUMNS = Map.ofEntries(
Map.entry("code", "提案编号"),
Map.entry("caseFilingCode", "立案编号"),
Map.entry("name", "提案名称"),
Map.entry("createUserName", "提案人"),
Map.entry("delegationName", "代表团"),
@@ -147,7 +145,6 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
/** 综合查询页面允许排序的字段,避免前端排序参数直接进入SQL。 */
private static final Map<String, String> COMPREHENSIVE_ORDER_COLUMNS = Map.ofEntries(
Map.entry("code", "info.code"),
Map.entry("caseFilingCode", "info.caseFilingCode"),
Map.entry("name", "info.name"),
Map.entry("createUserName", "info.createUserName"),
Map.entry("typeName", "typeName"),
@@ -163,7 +160,6 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
/** 综合查询页面允许自定义导出的字段及固定表头,防止请求伪造页面外字段。 */
private static final Map<String, String> COMPREHENSIVE_EXPORT_COLUMNS = Map.ofEntries(
Map.entry("code", "提案编号"),
Map.entry("caseFilingCode", "立案编号"),
Map.entry("name", "提案名称"),
Map.entry("createUserName", "提案人"),
Map.entry("typeName", "提案类别"),
@@ -402,10 +398,6 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
keywordCondition.orLike("info.createUserName", keyword);
keywordCondition.orLike("info.createUserLoginName", keyword);
}
// 立案编号由立案环节生成,选中该搜索来源时按编号进行模糊匹配。
if (ArrayUtil.contains(pageForm.getOrigins(), "caseFilingCode")) {
keywordCondition.orLike("info.caseFilingCode", keyword);
}
if (searchAnswer) {
keywordCondition.orLike("JSON_EXTRACT(replyTask.variable,'$.tf_opinion')", keyword);
}
@@ -469,7 +461,6 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
info.id,
info.name,
info.code,
info.caseFilingCode,
info.createUserName,
type.NAME AS typeName,
tcs.fullName AS sessionName,
@@ -924,19 +915,20 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
pages.put("commissioner", orderColumns("code", "info.code", "name", "info.name", "typeName", "type.name", "sessionName", "tcs.fullName", "delegationName", "tcd.name", "CONFIRM_FILING_COUNT", "CONFIRM_FILING_COUNT", "SUGGESTION_COUNT", "SUGGESTION_COUNT", "NOT_COUNT", "NOT_COUNT", "taskName", "taskName", "instanceState", "ins.state"));
pages.put("feedbackEvaluation", orderColumns("code", "info.code", "name", "info.name", "typeName", "type.name", "merge", "merge", "curTaskName", "curTaskName", "instanceState", "ins.state"));
pages.put("delegation", standardOrderColumns());
pages.put("control", mergeOrderColumns(standardOrderColumns(), orderColumns("caseFilingCode", "info.caseFilingCode")));
pages.put("control", standardOrderColumns());
pages.put("schoolLeaderApproval", orderColumns("code", "info.code", "name", "info.name", "typeName", "type.name", "undertakeUnits", "masterUnitName", "merge", "merge", "curTaskName", "curTaskName", "auditUser", "auditUser", "instanceState", "ins.state"));
pages.put("committeeFilingUnit", orderColumns("code", "info.code", "name", "info.name", "typeName", "type.name", "sessionName", "tcs.fullName", "caseFilingResult", "info.caseFilingResult", "delegationName", "tcd.name", "curTaskName", "curTaskName", "instanceState", "ins.state"));
pages.put("allFinished", orderColumns("code", "info.code", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "sessionName", "tcs.fullName", "delegationName", "tcd.name", "instanceState", "ins.state"));
pages.put("preAudit", standardOrderColumns());
pages.put("schoolUnionChairApproval", standardOrderColumns());
pages.put("committeeFiling", orderColumns("code", "info.code", "name", "info.name", "typeName", "type.name", "sessionName", "tcs.fullName", "caseFilingResult", "info.caseFilingResult", "delegationName", "tcd.name", "curTaskName", "curTaskName", "instanceState", "ins.state"));
pages.put("caseCheck", orderColumns("code", "info.code", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "sessionName", "tcs.fullName", "delegationName", "tcd.name", "curTaskName", "curTaskName", "instanceState", "ins.state", "finishTime", "t.finishTime"));
pages.put("exportComprehensive", orderColumns("code", "info.code", "caseFilingCode", "info.caseFilingCode", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "sessionName", "tcs.fullName", "delegationName", "tcd.name", "caseFilingResult", "info.caseFilingResult", "caseFilingType", "info.caseFilingType", "merge", "merge", "brief", "info.brief", "measures", "info.measures", "undertakeUnits", "masterUnitName", "curTaskName", "curTaskName", "instanceState", "ins.state"));
pages.put("exportComprehensive", orderColumns("code", "info.code", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "sessionName", "tcs.fullName", "delegationName", "tcd.name", "caseFilingResult", "info.caseFilingResult", "caseFilingType", "info.caseFilingType", "merge", "merge", "brief", "info.brief", "measures", "info.measures", "undertakeUnits", "masterUnitName", "curTaskName", "curTaskName", "instanceState", "ins.state"));
pages.put("exportSingleCustom", standardOrderColumns());
pages.put("configUnit", orderColumns("name", "t1.name", "code", "t1.code", "unitLeader", "u2.username"));
pages.put("configType", orderColumns("name", "name", "code", "code"));
pages.put("seniorBasic", seniorOrderColumns());
pages.put("expediting", orderColumns("code", "info.code", "caseFilingCode", "info.caseFilingCode", "name", "info.name", "typeName", "type.name", "delegationName", "tcd.name", "caseFilingResult", "info.caseFilingResult", "curTaskName", "curTaskName", "underTakeName", "underTakeName", "taskName", "t.displayName", "actorName", "ta.actorName", "instanceState", "ins.state"));
pages.put("expediting", orderColumns("code", "info.code", "name", "info.name", "typeName", "type.name", "delegationName", "tcd.name", "caseFilingResult", "info.caseFilingResult", "curTaskName", "curTaskName", "underTakeName", "underTakeName", "taskName", "t.displayName", "actorName", "ta.actorName", "instanceState", "ins.state"));
pages.put("seniorDelete", seniorOrderColumns());
pages.put("seniorFeedback", mergeOrderColumns(seniorOrderColumns(), orderColumns("tf_feedback", "tf_feedback")));
pages.put("seniorType", seniorOrderColumns());
@@ -950,7 +942,7 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
/** 创建高级管理列表共用的字段映射。 */
private static Map<String, String> seniorOrderColumns() {
return orderColumns("code", "info.code", "caseFilingCode", "info.caseFilingCode", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "delegationName", "tcd.name", "caseFilingResult", "info.caseFilingResult", "isConsolidation", "merge", "curTaskName", "curTaskName", "instanceState", "ins.state");
return orderColumns("code", "info.code", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "delegationName", "tcd.name", "caseFilingResult", "info.caseFilingResult", "isConsolidation", "merge", "curTaskName", "curTaskName", "instanceState", "ins.state");
}
/**
@@ -1013,7 +1005,7 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
private Sql buildWorkflowExportSql(String pageCode, ProposalSearchParam pageForm, boolean approval) {
boolean isUnitReply = "unitReply".equals(pageCode);
boolean isSchoolLeaderApproval = "schoolLeaderApproval".equals(pageCode);
boolean requiresUnionFilter = Set.of("preAudit", "committeeFiling", "committeeFilingUnit",
boolean requiresUnionFilter = Set.of("preAudit", "schoolUnionChairApproval", "committeeFiling", "committeeFilingUnit",
"schoolLeaderApproval", "caseCheck").contains(pageCode);
String taskName = getWorkflowTaskName(pageCode);
@@ -1068,7 +1060,7 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
Cnd cnd = Cnd.NEW();
if (isUnitReply) {
cnd.and("t.taskName", "in", List.of("unit_reply", "two_unit_reply", "opinion_unit_reply"));
cnd.and("t.taskName", "in", List.of("master_reply", "slave_reply", "opinion_master_reply"));
if (!"superadmin".equals(SecurityUtil.getUserLoginname())) {
cnd.and("ta.actorId", "=", SecurityUtil.getUserId());
}
@@ -1102,9 +1094,10 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
Map<String, String> taskNames = Map.of(
"delegation", "delegation",
"preAudit", "preAudit",
"schoolUnionChairApproval", "schoolUnionChairApproval",
"committeeFiling", "committee",
"committeeFilingUnit", "committeeFilingUnit",
"schoolLeaderApproval", "schoolLeader",
"schoolLeaderApproval", "school_leader",
"feedbackEvaluation", "feedback",
"caseCheck", "caseCheck"
);
@@ -1170,22 +1163,23 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
/** 初始化各办理页面可导出的列表字段白名单。 */
private static Map<String, Set<String>> createWorkflowExportColumns() {
Set<String> commonColumns = Set.of("code", "caseFilingCode", "name", "createUserName", "typeName",
Set<String> commonColumns = Set.of("code", "name", "createUserName", "typeName",
"sessionName", "delegationName", "curTaskName", "instanceState");
Map<String, Set<String>> columns = new HashMap<>();
columns.put("delegation", commonColumns);
columns.put("preAudit", commonColumns);
columns.put("caseCheck", Set.of("code", "caseFilingCode", "name", "createUserName", "typeName",
columns.put("schoolUnionChairApproval", commonColumns);
columns.put("caseCheck", Set.of("code", "name", "createUserName", "typeName",
"sessionName", "delegationName", "curTaskName", "instanceState", "finishTime"));
columns.put("committeeFiling", Set.of("code", "caseFilingCode", "name", "typeName", "sessionName",
columns.put("committeeFiling", Set.of("code", "name", "typeName", "sessionName",
"caseFilingResult", "delegationName", "curTaskName", "instanceState"));
columns.put("committeeFilingUnit", Set.of("code", "caseFilingCode", "name", "typeName", "sessionName",
columns.put("committeeFilingUnit", Set.of("code", "name", "typeName", "sessionName",
"caseFilingResult", "delegationName", "curTaskName", "instanceState"));
columns.put("schoolLeaderApproval", Set.of("code", "caseFilingCode", "name", "typeName", "undertakeUnits",
columns.put("schoolLeaderApproval", Set.of("code", "name", "typeName", "undertakeUnits",
"merge", "curTaskName", "auditUser", "instanceState"));
columns.put("unitReply", Set.of("code", "caseFilingCode", "name", "typeName", "caseFilingResult",
columns.put("unitReply", Set.of("code", "name", "typeName", "caseFilingResult",
"merge", "underTakeName", "underTakeIsMaster", "curTaskName", "auditUser", "instanceState"));
columns.put("feedbackEvaluation", Set.of("code", "caseFilingCode", "name", "typeName", "merge",
columns.put("feedbackEvaluation", Set.of("code", "name", "typeName", "merge",
"curTaskName", "instanceState"));
return Collections.unmodifiableMap(columns);
}
@@ -20,6 +20,7 @@ import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalMerge;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingService;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingUnitService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
@@ -44,6 +45,8 @@ public class ProposalCommitteeFilingServiceImpl extends BaseServiceImpl<Proposal
@Inject
private ProposalCommonService proposalCommonService;
@Inject
private ProposalCommitteeFilingUnitService proposalCommitteeFilingUnitService;
@Inject
private FlowCommonService flowCommonService;
@Inject
private ProcessTaskService processTaskService;
@@ -58,7 +61,8 @@ public class ProposalCommitteeFilingServiceImpl extends BaseServiceImpl<Proposal
@Aop(TransAop.READ_COMMITTED)
public void executeFlowTask(Dict args) {
validateSelectedUnitLeaders(args);
flowCommonService.executeTask(args);
// 立案审核复用原确认承办单位的表单,业务字段和单位记录须在同一事务内先于 WF 流转保存。
proposalCommitteeFilingUnitService.executeFlowTask(args);
}
@Override
@@ -117,7 +121,7 @@ public class ProposalCommitteeFilingServiceImpl extends BaseServiceImpl<Proposal
selectedUnitIds.addAll(getUnitIdList(args, FlowConst.TASK_FORM_DATA_PREFIX + "slaveUnitIds"));
}
if (selectedUnitIds.isEmpty()) {
return;
throw new BaseException("请选择主办或办理单位");
}
Sys_role unitLeaderRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER);
@@ -79,6 +79,21 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<Prop
if (currentTask == null) {
throw new BaseException("没有进行中的流程任务");
}
if (!"committee".equals(currentTask.getTaskName()) && !"committeeFilingUnit".equals(currentTask.getTaskName())) {
throw new BaseException("当前任务不是立案审核节点");
}
Integer submitType = args.getInt(FlowConst.SUBMIT_TYPE);
if (submitType == null) {
// 兼容未显式传提交类型的旧页面,默认按同意立案审核处理。
submitType = ProcessSubmitTypeEnum.AGREE.getCode();
args.put(FlowConst.SUBMIT_TYPE, submitType);
}
if (!ProcessSubmitTypeEnum.AGREE.getCode().equals(submitType)) {
// 退回或拒绝不形成立案结果,也不能清除上次已保存的承办单位。
flowCommonService.executeTask(args);
return;
}
String proposalId = StrUtil.trim(args.getStr("proposalId"));
if (StrUtil.isBlank(proposalId) || dao().fetch(ProposalInfo.class, proposalId) == null) {
@@ -90,7 +105,6 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<Prop
if (ObjectUtil.isEmpty(proposalIds)) {
proposalIds = List.of(proposalId);
}
generateAndNormalizeCaseFilingCode(args, proposalIds);
/*
* 先同步 proposal_info 和承办单位,再执行 WF 流转,使查看页面及下一节点读取到的都是本次最新数据。
@@ -114,129 +128,18 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<Prop
}
/**
* 按立案结果和服务器当前年份生成立案编号,并覆盖页面可能传入的编号。
* 正常提交时生成编号;退回等非同意操作不占用流水号。并案提案共用同一个编号。
*
* @param args 流程提交参数,tf_caseFilingResult 分别支持 CONFIRM_FILING、SUGGESTION、NOT
* @param proposalIds 当前办理的提案主键集合,并案时包含同组全部提案
*/
private void generateAndNormalizeCaseFilingCode(Dict args, List<String> proposalIds) {
String caseFilingResultKey = FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingResult";
String caseFilingCodeKey = FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingCode";
String caseFilingTypeKey = FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingType";
String caseFilingResult = args.getStr(caseFilingResultKey);
if (!"CONFIRM_FILING".equals(caseFilingResult)) {
// 立案类型仅适用于确定立案,切换为其他结果时同步清理历史值。
args.put(caseFilingTypeKey, "");
}
Integer submitType = args.getInt(FlowConst.SUBMIT_TYPE);
if (submitType == null) {
submitType = ProcessSubmitTypeEnum.AGREE.getCode();
args.put(FlowConst.SUBMIT_TYPE, submitType);
}
if (!ProcessSubmitTypeEnum.AGREE.getCode().equals(submitType)) {
args.put(caseFilingCodeKey, "");
return;
}
String codePrefix = resolveCaseFilingCodePrefix(caseFilingResult);
int currentYear = java.time.LocalDate.now().getYear();
String codeHead = currentYear + codePrefix;
/*
* proposal_config 当前是单行配置表,锁定该行可让所有应用实例串行生成流水号。
* 行锁一直持有到本次 WF 提交事务结束,避免两个审核请求同时读到相同最大值。
*/
Sql lockSql = Sqls.create("SELECT id FROM proposal_config ORDER BY id LIMIT 1 FOR UPDATE");
lockSql.setCallback(Sqls.callback.map());
dao().execute(lockSql);
Map<?, ?> configRow = lockSql.getObject(Map.class);
if (ObjectUtil.isEmpty(configRow)) {
throw new BaseException("提案配置不存在,无法生成立案编号");
}
String reusableCode = findReusableCaseFilingCode(proposalIds, caseFilingResult, codeHead);
if (StrUtil.isNotBlank(reusableCode)) {
args.put(caseFilingCodeKey, reusableCode);
return;
}
Sql maxSequenceSql = Sqls.create("""
SELECT COALESCE(MAX(CAST(SUBSTRING(caseFilingCode, 6) AS UNSIGNED)), 0)
FROM proposal_info
WHERE caseFilingCode REGEXP @codePattern
""");
maxSequenceSql.setParam("codePattern", "^" + codeHead + "[0-9]{3,}$");
maxSequenceSql.setCallback(Sqls.callback.integer());
dao().execute(maxSequenceSql);
int nextSequence = maxSequenceSql.getInt() + 1;
String caseFilingCode = codeHead + String.format("%03d", nextSequence);
args.put(caseFilingCodeKey, caseFilingCode);
}
/**
* 将立案结果转换成编号前缀,未知结果直接阻止流程提交,避免生成无业务含义的编号。
*
* @param caseFilingResult 立案结果编码
* @return 确定立案返回 A、工作建议返回 B、不予立案返回 C
*/
private String resolveCaseFilingCodePrefix(String caseFilingResult) {
return switch (StrUtil.nullToEmpty(caseFilingResult)) {
case "CONFIRM_FILING" -> "A";
case "SUGGESTION" -> "B";
case "NOT" -> "C";
default -> throw new BaseException("立案结果不正确,无法生成立案编号");
};
}
/**
* 流程撤回或重新提交时,复用同一批提案已有的本年同类型编号,避免流水号无意义增长。
* 只有全部提案的结果和编号完全一致且符合新编号格式时才允许复用。
*
* @param proposalIds 当前单案或并案提案主键集合
* @param caseFilingResult 本次选择的立案结果
* @param codeHead 年份和编号前缀,例如 2026A
* @return 可复用的编号;不存在时返回空字符串
*/
private String findReusableCaseFilingCode(List<String> proposalIds, String caseFilingResult, String codeHead) {
List<ProposalInfo> proposalInfos = dao().query(ProposalInfo.class,
Cnd.where(ProposalInfo::getId, "in", proposalIds));
if (proposalInfos.size() != new LinkedHashSet<>(proposalIds).size()) {
return "";
}
boolean allProposalsMatch = proposalInfos.stream().allMatch(proposalInfo ->
Objects.equals(caseFilingResult, proposalInfo.getCaseFilingResult())
&& StrUtil.isNotBlank(proposalInfo.getCaseFilingCode()));
if (!allProposalsMatch) {
return "";
}
LinkedHashSet<String> existingCodes = proposalInfos.stream()
.map(ProposalInfo::getCaseFilingCode)
.map(StrUtil::trim)
.collect(Collectors.toCollection(LinkedHashSet::new));
if (existingCodes.size() != 1) {
return "";
}
String existingCode = existingCodes.iterator().next();
return existingCode.matches(codeHead + "\\d{3,}") ? existingCode : "";
}
/**
* old-flow 页面提交后,需要把立案编号、立案结果以及主协办单位同步落业务表。
* 立案审核提交后,只同步立案结果、立案类型和主协办单位,不生成立案编号。
* 若这次改成“不予立案”等无需承办单位的结果,也要先清掉旧的承办单位记录,避免脏数据残留。
*/
private void syncCommitteeFilingUnitData(List<String> proposalIds, Dict args) {
String caseFilingCode = args.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingCode");
String caseFilingResult = args.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingResult");
String caseFilingType = args.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingType");
dao().update(ProposalInfo.class,
Chain.make("caseFilingCode", caseFilingCode)
.add("caseFilingResult", caseFilingResult)
Chain.make("caseFilingResult", caseFilingResult)
.add("caseFilingType", caseFilingType),
Cnd.where(ProposalInfo::getId, "in", proposalIds));
verifyCommitteeFilingUnitData(proposalIds, caseFilingCode, caseFilingResult, caseFilingType);
verifyCommitteeFilingUnitData(proposalIds, caseFilingResult, caseFilingType);
dao().clear(ProposalReplyUnit.class, Cnd.where(ProposalReplyUnit::getProposalId, "in", proposalIds));
if (!List.of("CONFIRM_FILING", "SUGGESTION").contains(caseFilingResult)) {
@@ -276,7 +179,7 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<Prop
/**
* 校验本次立案字段是否已同步到全部提案,避免流程变量更新成功但 info 基础信息仍保留旧值。
*/
private void verifyCommitteeFilingUnitData(List<String> proposalIds, String caseFilingCode,
private void verifyCommitteeFilingUnitData(List<String> proposalIds,
String caseFilingResult, String caseFilingType) {
List<ProposalInfo> proposalInfos = dao().query(ProposalInfo.class,
Cnd.where(ProposalInfo::getId, "in", proposalIds));
@@ -284,9 +187,7 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<Prop
throw new BaseException("部分提案信息不存在,无法同步立案结果");
}
boolean syncFailed = proposalInfos.stream().anyMatch(proposalInfo ->
!Objects.equals(StrUtil.nullToDefault(proposalInfo.getCaseFilingCode(), ""),
StrUtil.nullToDefault(caseFilingCode, ""))
|| !Objects.equals(StrUtil.nullToDefault(proposalInfo.getCaseFilingResult(), ""),
!Objects.equals(StrUtil.nullToDefault(proposalInfo.getCaseFilingResult(), ""),
StrUtil.nullToDefault(caseFilingResult, ""))
|| !Objects.equals(StrUtil.nullToDefault(proposalInfo.getCaseFilingType(), ""),
StrUtil.nullToDefault(caseFilingType, "")));
@@ -189,7 +189,6 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
info.id,
info.name,
info.code,
info.caseFilingCode,
info.caseFilingResult,
info.caseFilingType,
info.researchFindings,
@@ -259,7 +258,6 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("序号", "index", 10));
exportEntities.add(new ExcelExportEntity("提案编号", "code", 20));
exportEntities.add(new ExcelExportEntity("立案编号", "caseFilingCode", 20));
exportEntities.add(new ExcelExportEntity("立案结果", "caseFilingResultName", 15));
exportEntities.add(new ExcelExportEntity("立案类型", "caseFilingTypeName", 15));
exportEntities.add(new ExcelExportEntity("是否并案", "isConsolidationName", 12));
@@ -318,7 +316,6 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
NutMap proposalRow = proposalRows.computeIfAbsent(proposalId, key -> NutMap.NEW()
.addv("id", proposalId)
.addv("code", row.getString("code"))
.addv("caseFilingCode", row.getString("caseFilingCode"))
.addv("name", row.getString("name"))
.addv("createUserName", row.getString("createUserName"))
.addv("caseFilingResult", row.getString("caseFilingResult"))
@@ -373,7 +370,6 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
SELECT DISTINCT
info.id,
info.code,
info.caseFilingCode,
info.name,
info.createUserName,
info.caseFilingResult,
@@ -413,9 +409,8 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
LinkedHashSet<String> masterUnitNames = getUnitNameSet(proposalRow, "masterUnitNames");
LinkedHashSet<String> slaveUnitNames = getUnitNameSet(proposalRow, "slaveUnitNames");
NutMap excelRow = NutMap.NEW()
// 承办单位表统一展示为“立案编号”;未生成立案编号的工作建议回退显示提案编号。
.addv("caseFilingCode", StrUtil.blankToDefault(proposalRow.getString("caseFilingCode"),
proposalRow.getString("code")))
// 承办单位表只展示提案编号,立案结果不再生成独立编号。
.addv("code", proposalRow.getString("code"))
.addv("name", proposalRow.getString("name"))
.addv("createUserName", proposalRow.getString("createUserName"))
.addv("masterUnitNames", "")
@@ -434,7 +429,7 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
}
if (!suggestionRows.isEmpty()) {
filingRows.add(NutMap.NEW()
.addv("caseFilingCode", WORK_SUGGESTION_SECTION_MARKER)
.addv("code", WORK_SUGGESTION_SECTION_MARKER)
.addv("name", "")
.addv("createUserName", "")
.addv("masterUnitNames", "")
@@ -450,7 +445,7 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
*/
private Workbook buildUndertakeUnitWorkbook(String title, String unitName, List<NutMap> excelRows) {
List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("立案编号", "caseFilingCode", 11));
exportEntities.add(new ExcelExportEntity("提案编号", "code", 11));
exportEntities.add(new ExcelExportEntity("提案名称", "name", 43));
exportEntities.add(new ExcelExportEntity("提案人", "createUserName", 13));
exportEntities.add(new ExcelExportEntity("主办单位", "masterUnitNames", 19));
@@ -925,7 +920,6 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
info.id,
info.name,
info.code,
info.caseFilingCode,
info.caseFilingResult,
info.caseFilingType,
info.researchFindings,
@@ -336,7 +336,7 @@ public class ProposalUnderTakeReplyServiceImpl extends BaseServiceImpl<ProposalI
if (replyTask == null) {
throw new BaseException("未找到答复任务");
}
if (!List.of("unit_reply", "two_unit_reply", "opinion_master_reply").contains(replyTask.getTaskName())) {
if (!List.of("master_reply", "slave_reply", "opinion_master_reply").contains(replyTask.getTaskName())) {
throw new BaseException("当前任务不支持编辑答复内容");
}
if (!Objects.equals(ProcessTaskStateEnum.FINISHED.getCode(), replyTask.getTaskState())) {
@@ -147,9 +147,12 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
Cnd.where(WelfareProjectSubjectOption::getId, "in", toDeleteIds));
}
for (WelfareProjectSubjectOption option : project.getOptions()) {
option.setWelfareId(project.getId());
dao().insertOrUpdate(option);
// 慰问金可不配置福利选项,编辑空选项项目时无需写入关联记录。
if (project.getOptions() != null) {
for (WelfareProjectSubjectOption option : project.getOptions()) {
option.setWelfareId(project.getId());
dao().insertOrUpdate(option);
}
}
// 更新首页活动
@@ -62,7 +62,7 @@ layout("/layouts/platform.html"){
<el-card shadow="never">
<table-tool :label="pageForm.pullTime ? pageForm.pullTime + '数据' : '全部数据'">
<el-button type="primary" size="mini" :loading="pullLoading" @click="pull" icon="el-icon-download">拉取信息中心数据</el-button>
<el-button type="primary" size="mini" :loading="pullLoading" @click="pull" icon="el-icon-download">拉取本地中间表数据</el-button>
<el-button type="danger" size="mini" @click="openDelete" icon="el-icon-delete">删除本地数据源</el-button>
</table-tool>
<el-table :key="tableKey" :data="tableData" @sort-change="pageOrder" header-align="center" v-loading="tableLoading">
@@ -124,7 +124,7 @@ layout("/layouts/platform.html"){
},
methods: {
pull() {
this.$confirm("确定要拉取数据源吗?", "提示", {
this.$confirm("确定从本地中间表更新单位并拉取人员数据源吗?", "提示", {
type: "warning",
confirmButtonText: "确定",
cancelButtonText: "取消"
@@ -12,7 +12,6 @@ const PROPOSAL_INFO = {
</h3>
<el-descriptions :column="3" border>
<el-descriptions-item label="提案编号">{{ viewData.code }}</el-descriptions-item>
<el-descriptions-item label="立案编号">{{ viewData.caseFilingCode || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="提案人">{{viewData.createUserName }}</el-descriptions-item>
<el-descriptions-item label="提案时间">{{ viewData.createTime }}</el-descriptions-item>
<el-descriptions-item label="教代会届次"> {{ viewData.sessionName || '暂无' }}</el-descriptions-item>
@@ -215,7 +214,7 @@ const PROPOSAL_INFO = {
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>
<el-descriptions-item label="承办单位"
v-if="['opinion_unit_reply','unit_reply','two_unit_reply','master_reply','slave_reply'].includes(task.taskName)" :span="3">
v-if="['opinion_unit_reply','unit_reply','two_unit_reply','opinion_master_reply','master_reply','slave_reply'].includes(task.taskName)" :span="3">
{{task.ext.underTakeName}}
</el-descriptions-item>
@@ -359,7 +358,7 @@ const PROPOSAL_INFO = {
* @return 仅用于查看页面的展示数组;原始任务及每条答复内容均保存在 replyTasks 中
*/
groupReplyTasks(tasks) {
const replyTaskNames = ["opinion_unit_reply", "unit_reply", "two_unit_reply", "master_reply", "slave_reply"]
const replyTaskNames = ["opinion_unit_reply", "unit_reply", "two_unit_reply", "opinion_master_reply", "master_reply", "slave_reply"]
const replyTasks = (tasks || []).filter((task) => replyTaskNames.includes(task.taskName))
if (!replyTasks.length) {
return tasks || []
@@ -384,8 +383,7 @@ const PROPOSAL_INFO = {
/**
* 获取提案任务所属的展示阶段。
* 未单列的预审核、委员审议、确认承办单位和复核记录统一放在委员会立案阶段,
* 答复后的校领导审批通过 legacyStage 标识排在承办单位答复之后。
* 按当前 WF 节点顺序展示两级主席审核、立案、答复、校领导审批和评价。
*
* @param task 单条已办任务
* @return 数字类型的阶段顺序
@@ -397,8 +395,9 @@ const PROPOSAL_INFO = {
if (taskName === "invite") return 15
if (taskName === "second") return 20
if (taskName === "delegation") return 30
if (["opinion_unit_reply", "unit_reply", "two_unit_reply", "master_reply", "slave_reply"].includes(taskName)) return 50
if (taskName === "schoolLeader" && legacyStage === "replyLeaderAudit") return 55
if (taskName === "schoolUnionChairApproval") return 35
if (["opinion_unit_reply", "unit_reply", "two_unit_reply", "opinion_master_reply", "master_reply", "slave_reply"].includes(taskName)) return 50
if (taskName === "school_leader" || taskName === "schoolLeader" && legacyStage === "replyLeaderAudit") return 55
if (taskName === "feedback") return 60
return 40
},
@@ -27,12 +27,12 @@ layout("/layouts/platform.html"){
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
<el-table-column prop="name" label="单位名称" sortable="custom" show-overflow-tooltip></el-table-column>
<el-table-column prop="code" label="单位编码" sortable="custom" width="150px"></el-table-column>
<!-- <el-table-column prop="branchSchoolLeader" label="分管校领导及联系方式" width="180px"></el-table-column>-->
<el-table-column prop="unitLeader" label="单位领导" sortable="custom" width="180px" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="350px">
<el-table-column prop="branchSchoolLeader" label="单位分管校领导" width="180px" show-overflow-tooltip></el-table-column>
<el-table-column prop="unitLeader" label="单位负责人" sortable="custom" width="180px" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="390px">
<template slot-scope="{row}">
<!-- <el-button size="mini" type="primary" @click="$refs.userRef.onOpen(row.id,true)">设置分管校领导</el-button>-->
<el-button size="mini" type="primary" @click="$refs.userRef.onOpen(row.id,false)">设置单位领导</el-button>
<el-button size="mini" type="primary" @click="$refs.userRef.onOpen(row.id,false)">设置负责人</el-button>
<el-button size="mini" type="primary" @click="$refs.userRef.onOpen(row.id,true)">设置校领导</el-button>
<el-button size="mini" type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
@@ -1,6 +1,6 @@
const USER_COMPONENT = {
template: `
<el-dialog title="设置领导" :visible.sync="visible" width="1000px" :close-on-click-modal="false">
<el-dialog :title="isSchoolLeader ? '设置校领导' : '设置负责人'" :visible.sync="visible" width="1000px" :close-on-click-modal="false">
<el-transfer
ref="transferRef"
:titles="['可选人员', '已选人员']"
@@ -25,7 +25,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -186,7 +185,6 @@ layout("/layouts/platform.html"){
},
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name"},
{label: "提案人", prop: "createUserName"},
{label: "提案类别", prop: "typeName"},
@@ -23,7 +23,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -123,7 +122,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案人", prop: "createUserName"},
{label: "代表团", prop: "delegationName"},
@@ -265,7 +265,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案人", prop: "createUserName"},
{label: "提案类别", prop: "typeName"},
@@ -308,10 +307,6 @@ layout("/layouts/platform.html"){
{
label: '提案人',
value: 'createUserName'
},
{
label: '立案编号',
value: 'caseFilingCode'
}
]
}
@@ -23,7 +23,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -70,7 +69,6 @@ layout("/layouts/platform.html"){
<el-table v-loading="tableLoading" :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%" row-key="id">
<el-table-column label="序号" width="50" type="index" :index="indexMethod"></el-table-column>
<el-table-column label="提案编号" prop="code" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="立案编号" prop="caseFilingCode" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="提案名称" prop="name" sortable="custom" show-overflow-tooltip></el-table-column>
<el-table-column label="提案人" prop="createUserName" sortable="custom"></el-table-column>
<el-table-column label="提案类别" prop="typeName" sortable="custom"></el-table-column>
@@ -21,7 +21,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -55,7 +54,6 @@ layout("/layouts/platform.html"){
<el-table v-loading="tableLoading" :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%" row-key="id">
<el-table-column label="序号" width="50" type="index" :index="indexMethod"></el-table-column>
<el-table-column label="提案编号" prop="code" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="立案编号" prop="caseFilingCode" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="提案名称" prop="name" sortable="custom" show-overflow-tooltip></el-table-column>
<el-table-column label="提案人" prop="createUserName" sortable="custom"></el-table-column>
<el-table-column label="提案类别" prop="typeName" sortable="custom"></el-table-column>
@@ -113,11 +111,6 @@ layout("/layouts/platform.html"){
<el-input maxlength="100" placeholder="提案编号" v-model="formData.code"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="立案编号" prop="caseFilingCode">
<el-input maxlength="100" placeholder="立案编号" v-model="formData.caseFilingCode"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" type="flex">
@@ -19,7 +19,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -49,7 +48,6 @@ layout("/layouts/platform.html"){
<el-table v-loading="tableLoading" :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%" row-key="id">
<el-table-column label="序号" width="50" type="index" :index="indexMethod"></el-table-column>
<el-table-column label="提案编号" prop="code" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="立案编号" prop="caseFilingCode" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="提案名称" prop="name" sortable="custom" show-overflow-tooltip></el-table-column>
<el-table-column label="提案人" prop="createUserName" sortable="custom"></el-table-column>
<el-table-column label="提案类别" prop="typeName" sortable="custom"></el-table-column>
@@ -19,7 +19,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -132,7 +131,6 @@ layout("/layouts/platform.html"){
tableColumns: [
{prop: 'code', label: '提案编号'},
{prop: 'caseFilingCode', label: '立案编号'},
{prop: 'name', label: '提案名称'},
{prop: 'typeName', label: '提案类别'},
{prop: 'delegationName', label: '代表团'},
@@ -21,7 +21,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -55,7 +54,6 @@ layout("/layouts/platform.html"){
<el-table v-loading="tableLoading" :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%" row-key="id">
<el-table-column label="序号" width="50" type="index" :index="indexMethod"></el-table-column>
<el-table-column label="提案编号" prop="code" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="立案编号" prop="caseFilingCode" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="提案名称" prop="name" sortable="custom" show-overflow-tooltip></el-table-column>
<el-table-column label="提案人" prop="createUserName" sortable="custom"></el-table-column>
<el-table-column label="提案类别" prop="typeName" sortable="custom"></el-table-column>
@@ -19,7 +19,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -49,7 +48,6 @@ layout("/layouts/platform.html"){
<el-table v-loading="tableLoading" :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%" row-key="id">
<el-table-column label="序号" width="50" type="index" :index="indexMethod"></el-table-column>
<el-table-column label="提案编号" prop="code" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="立案编号" prop="caseFilingCode" sortable="custom" width="100px" show-overflow-tooltip></el-table-column>
<el-table-column label="提案名称" prop="name" sortable="custom" show-overflow-tooltip></el-table-column>
<el-table-column label="提案人" prop="createUserName" sortable="custom"></el-table-column>
<el-table-column label="提案类别" prop="typeName" sortable="custom"></el-table-column>
@@ -148,7 +148,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案人", prop: "createUserName", width: "80px"},
{label: "提案类别", prop: "typeName"},
@@ -204,7 +204,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{ label: "提案编号", prop: "code" },
{ label: "立案编号", prop: "caseFilingCode" },
{ label: "提案名称", prop: "name", width: "200px" },
{ label: "提案类别", prop: "typeName" },
{ label: "届次", prop: "sessionName" },
@@ -116,7 +116,7 @@ layout("/layouts/platform.html"){
</div>
<el-form :model="formData" ref="formRef" label-width="120px" :rules="formRules" label-suffix="">
<el-form-item label="立案结果" prop="tf_caseFilingResult"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
:rules="[{required:pendingSubmitType === 1,message:'必填',trigger:['change','blur']}]">
<el-radio-group v-model="formData.tf_caseFilingResult" size="small" @change="handleCaseFilingResultChange">
<el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code"
border>{{item.label}}
@@ -135,7 +135,7 @@ layout("/layouts/platform.html"){
<el-form-item
:label="formData.tf_caseFilingResult === 'SUGGESTION' ? '办理单位' : '主办单位'"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)"
:rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:formData.tf_caseFilingResult === 'SUGGESTION' ? '请选择办理单位' : '请选择主办单位',trigger:['change','blur']}]"
:rules="[{required:pendingSubmitType === 1 && ['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:formData.tf_caseFilingResult === 'SUGGESTION' ? '请选择办理单位' : '请选择主办单位',trigger:['change','blur']}]"
prop="tf_masterUnitIds"
>
<el-select v-model="formData.tf_masterUnitIds" filterable clearable multiple
@@ -204,7 +204,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案类别", prop: "typeName"},
{label: "届次", prop: "sessionName"},
@@ -221,6 +220,7 @@ layout("/layouts/platform.html"){
tf_slaveUnitIds: []
},
showApprovalForm: false,
pendingSubmitType: 1,
sessionOptions: [],
delegationOptions: [],
unionOptions: [],
@@ -341,8 +341,9 @@ layout("/layouts/platform.html"){
if (!val || !val.length) {
return
}
// 当前页面需要回显上一环节“提案委员会立案”保存的主办/协办单位或办理单位。
var data = val[val.length - 1]
// 仅回显立案审核的历史数据;校工会主席意见没有立案字段,不能清空本次表单。
var data = val.slice().reverse().find((task) => task.ext && task.ext.tf_caseFilingResult)
if (!data) return
this.$set(this.formData, "tf_caseFilingResult", data.ext.tf_caseFilingResult)
this.$set(this.formData, "tf_caseFilingType", data.ext.tf_caseFilingType)
this.$set(this.formData, "tf_masterUnitIds", this.normalizeUnitIds(data.ext.tf_masterUnitIds))
@@ -352,6 +353,7 @@ layout("/layouts/platform.html"){
openAudit(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = true
this.pendingSubmitType = 1
this.formData = {
proposalId: row.id,
processTaskId: row.taskId,
@@ -365,6 +367,9 @@ layout("/layouts/platform.html"){
},
handleTaskAction(val) {
// 退回只需要审核意见;立案结果与承办单位仅在正式提交时必填。
this.pendingSubmitType = val
this.$refs.formRef.clearValidate()
this.$refs.formRef.validate(valid => {
if (!valid) return
this.$confirm("您确定要提交吗?", "提示", {
@@ -405,7 +410,7 @@ layout("/layouts/platform.html"){
}
const loading = createLoading('提交中')
this.$axios.post("/platform/proposal/committeeFilingUnit/executeTask", {
this.$axios.post("/platform/proposal/committeeFiling/executeTask", {
data: JSON.stringify(params)
}).then((res) => {
if (res.code === 0) {
@@ -444,7 +449,7 @@ layout("/layouts/platform.html"){
cancelButtonText: "取消",
type: "info"
}).then(() => {
this.$axios.post("/platform/proposal/committeeFilingUnit/revokeTask", {
this.$axios.post("/platform/proposal/committeeFiling/revokeTask", {
taskId: row.taskId,
proposalId: row.id
}).then((res) => {
@@ -16,7 +16,6 @@ layout("/layouts/platform.html"){
<el-select slot="prepend" style="width: 110px" v-model="pageForm.proposalSearchField">
<el-option label="提案名称" value="name"></el-option>
<el-option label="提案编号" value="code"></el-option>
<el-option label="立案编号" value="caseFilingCode"></el-option>
</el-select>
</el-input>
</search-item>
@@ -93,7 +92,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "220px"},
{label: "提案人", prop: "createUserName", width: "90px"},
{label: "提案类别", prop: "typeName"},
@@ -107,7 +107,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案人", prop: "createUserName", width: "80px"},
{label: "提案类别", prop: "typeName"},
@@ -122,7 +122,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案类别", prop: "typeName"},
{label: "是否并案", prop: "merge"},
@@ -112,7 +112,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案人", prop: "createUserName", width: "80px"},
{label: "提案类别", prop: "typeName"},
@@ -155,9 +154,9 @@ layout("/layouts/platform.html"){
openAudit(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = true
this.formData = {
this.formData = {
processTaskId: row.taskId,
taskName: row.curTaskName,
taskName: row.taskName || "校工会主席审核",
taskKey: row.taskKey
}
this.$refs.proposalInfoRef.onOpen(row)
@@ -146,7 +146,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案类别", prop: "typeName"},
{label: "办理单位", prop: "undertakeUnits", width: "260px"},
@@ -71,26 +71,7 @@ layout("/layouts/platform.html"){
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #edit>
<proposal-info ref="proposalInfoRef">
<div v-if="showApprovalForm">
<div class="process-title">
{{formData.taskName}}
</div>
<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>
<el-row type="flex" justify="end">
<el-button @click="handleTaskAction(1)" size="small" type="primary">同意</el-button>
<el-button @click="handleTaskAction(2)" size="small" type="danger">不同意</el-button>
<el-button @click="handleTaskAction(6)" size="small" type="info">退回到提案人</el-button>
<el-button @click="$refs.guava.index()" size="small">返回</el-button>
</el-row>
</div>
</proposal-info>
<proposal-info ref="proposalInfoRef"></proposal-info>
</template>
</guava>
</div>
@@ -118,14 +99,13 @@ layout("/layouts/platform.html"){
{label: "提交方式", prop: "mannerName"},
{label: "附议人数量", prop: "secondedNum"},
{label: "已附议", prop: "secondedAgreeNum"},
{label: "团长审核", prop: "delegationAudit"},
{label: "分工会主席审核", prop: "delegationAudit"},
],
pageForm: {
approval: false,
searchName: "proposalName",
searchName2: "su.username",
},
showApprovalForm: false,
sessionOptions: []
}
},
@@ -133,7 +113,6 @@ layout("/layouts/platform.html"){
// 查看
openView(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = false
this.$refs.proposalInfoRef.onOpen(row)
})
},
@@ -291,7 +291,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案类别", prop: "typeName"},
{label: "立案结果", prop: "caseFilingResult"},
@@ -463,10 +462,10 @@ layout("/layouts/platform.html"){
// 加载候选人列表
loadCandidates(taskId) {
$.get("/flow/common/candidate", {taskId}).then((res) => {
this.$axios.post("/flow/common/candidate", {taskId}).then((res) => {
if (res.code === 0) {
this.candidates = res.data.candidates
this.supportCandidate = res.data.support
this.$set(this, "candidates", res.data.candidates)
this.$set(this, "supportCandidate", res.data.support)
}
})
},
@@ -88,7 +88,7 @@ layout("/layouts/platform.html"){
</template>
<template #edit>
<el-form :model="formData" :rules="formRules" label-width="150px" ref="form" v-loading="formLoading">
<el-form :model="formData" :rules="welfareFormRules" label-width="150px" ref="form" v-loading="formLoading">
<el-form-item label="沿用往期福利" v-if="!formData.id">
<el-select clearable filterable placeholder="请选择往期福利" style="width: 100%"
v-model="reuseProjectId" @change="reuseHistoryProject">
@@ -126,6 +126,15 @@ layout("/layouts/platform.html"){
<el-col :span="12">
<el-form-item prop="provideTime" label="发放时间">
<el-date-picker
v-if="formData.provideMode === 99"
style="width: 100%"
v-model="formData.provideTime"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择发放时间" @change="provideTimeChange">
</el-date-picker>
<el-date-picker
v-else
style="width: 100%"
v-model="formData.provideTime"
type="datetimerange"
@@ -196,7 +205,7 @@ layout("/layouts/platform.html"){
</el-row>
<el-form-item label="发放形式" prop="provideMode">
<el-radio-group size="medium" v-model="formData.provideMode">
<el-radio-group size="medium" v-model="formData.provideMode" @change="provideModeChange">
<el-radio-button :key="item.code" :label="item.code" v-for="item in welfareProvideMode">{{item.name}}</el-radio-button>
</el-radio-group>
</el-form-item>
@@ -275,24 +284,30 @@ layout("/layouts/platform.html"){
// { prop: "gift", label: "福利礼品" },
// { prop: "provideTimeStart", label: "发放时间" }
],
formRules: {
name: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
festival: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
provideTime: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
choiceTime: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
provideAddress: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
provideMode: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
signMode: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
noticePushMode: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
gift: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
multiSelectNum: [{ required: true, message: "必填", trigger: ["blur", "change"] }]
},
welfarePersonTypeList: [],
historyProjectOptions: [],
reuseProjectId: null,
welfareOptionKey: 0
}
},
computed: {
welfareFormRules() {
// 慰问金只要求项目名称和单个发放时间,其他发放形式沿用原有校验。
const requiredForOtherMode = this.formData.provideMode !== 99
return {
name: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
festival: [{ required: requiredForOtherMode, message: "必选", trigger: ["blur", "change"] }],
provideTime: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
choiceTime: [{ required: requiredForOtherMode, message: "必选", trigger: ["blur", "change"] }],
provideAddress: [{ required: requiredForOtherMode, message: "必选", trigger: ["blur", "change"] }],
provideMode: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
signMode: [{ required: requiredForOtherMode, message: "必选", trigger: ["blur", "change"] }],
noticePushMode: [{ required: requiredForOtherMode, message: "必选", trigger: ["blur", "change"] }],
gift: [{ required: requiredForOtherMode, message: "必填", trigger: ["blur", "change"] }],
multiSelectNum: [{ required: requiredForOtherMode, message: "必填", trigger: ["blur", "change"] }]
}
}
},
methods: {
dropdownCommand(command) {
const { type, data } = command
@@ -310,30 +325,31 @@ layout("/layouts/platform.html"){
this.sendMsg(data)
}
},
async save() {
const formValid = await this.$refs["form"].validate()
if (formValid) {
if (this.formData.flexible) {
save() {
this.$refs["form"].validate((formValid) => {
if (!formValid) {
return
}
const formData = clone(this.formData)
formData.provideAddress = "工会"
try {
this.submitLoading = true
const resp = await this.$axios.post(loc() + "/save", { data: JSON.stringify(formData) })
if (resp.code === 0) {
this.$refs.guava.index()
this.doSearch()
this.$message.success(resp.msg)
} else {
this.$message.error(resp.msg)
}
} catch (e) {
this.notifyWarning(e.status)
} finally {
this.submitLoading = false
}
}
this.submitLoading = true
this.$axios.post(loc() + "/save", { data: JSON.stringify(formData) })
.then((resp) => {
if (resp.code === 0) {
this.$refs.guava.index()
this.doSearch()
this.$message.success(resp.msg)
} else {
this.$message.error(resp.msg)
}
})
.catch((e) => {
this.notifyWarning(e.status)
})
.finally(() => {
this.submitLoading = false
})
})
},
isCheckBoxChange() {
this.formData.welfareProjectSubjects = [
@@ -354,7 +370,11 @@ layout("/layouts/platform.html"){
]
},
provideTimeChange() {
if (this.formData.provideTime && this.formData.provideTime.length) {
// 单个发放时间同时写入原有开始、结束字段,保持保存接口和项目实体不变。
if (this.formData.provideMode === 99) {
this.$set(this.formData, "provideTimeStart", this.formData.provideTime || "")
this.$set(this.formData, "provideTimeEnd", this.formData.provideTime || "")
} else if (this.formData.provideTime && this.formData.provideTime.length) {
this.formData.provideTimeStart = this.formData.provideTime[0]
this.formData.provideTimeEnd = this.formData.provideTime[1]
} else {
@@ -362,25 +382,35 @@ layout("/layouts/platform.html"){
this.formData.provideTimeEnd = ""
}
},
provideModeChange() {
// 两种日期控件的 v-model 类型不同,切换发放形式时清除旧值。
this.$set(this.formData, "provideTime", null)
this.provideTimeChange()
this.$refs.form.clearValidate()
},
choiceTimeChange() {
if (this.formData.choiceTime && this.formData.choiceTime.length) {
this.formData.choiceTimeStart = this.formData.choiceTime[0]
this.formData.choiceTimeEnd = this.formData.choiceTime[1]
this.formData.provideTimeStart = this.formData.choiceTime[0]
this.formData.provideTimeEnd = this.formData.choiceTime[1]
if (this.formData.provideMode !== 99) {
this.formData.provideTimeStart = this.formData.choiceTime[0]
this.formData.provideTimeEnd = this.formData.choiceTime[1]
}
} else {
this.formData.choiceTimeStart = ""
this.formData.choiceTimeEnd = ""
this.formData.provideTimeStart = ""
this.formData.provideTimeEnd = ""
if (this.formData.provideMode !== 99) {
this.formData.provideTimeStart = ""
this.formData.provideTimeEnd = ""
}
}
},
sendMsg(row) {},
async openAdd() {
this.$set(this, "reuseProjectId", null)
if (this.$refs.form) this.$refs.form.resetFields()
this.$set(this, "formData", {
provideMode: 99,
isPushHome: false,
signMode: 4,
isRoutine: true,
@@ -388,11 +418,11 @@ layout("/layouts/platform.html"){
defaultFlexibleGifts: 0,
flexible: false,
noticePushMode: 1,
options: [],
welfareProjectSubjects: [],
flexibleGifts: [{}]
})
this.$refs.guava.edit()
if (this.$refs.form) this.$refs.form.resetFields()
await this.loadHistoryProjectOptions()
},
// 查询可沿用的历史福利项目,用于下拉选择。
@@ -430,7 +460,8 @@ layout("/layouts/platform.html"){
const projectData = clone(resp.data)
this.clearReuseProjectIdentifiers(projectData)
this.$set(projectData, "choiceTime", [projectData.choiceTimeStart, projectData.choiceTimeEnd])
this.$set(projectData, "provideTime", [projectData.provideTimeStart, projectData.provideTimeEnd])
this.$set(projectData, "provideTime", projectData.provideMode === 99
? projectData.provideTimeStart : [projectData.provideTimeStart, projectData.provideTimeEnd])
this.$set(this, "formData", projectData)
this.$set(this, "welfareOptionKey", this.welfareOptionKey + 1)
} catch (e) {
@@ -479,7 +510,8 @@ layout("/layouts/platform.html"){
this.submitLoading = false
if (resp.code === 0) {
const data = resp.data
data.provideTime = [data.provideTimeStart, data.provideTimeEnd]
data.provideTime = data.provideMode === 99
? data.provideTimeStart : [data.provideTimeStart, data.provideTimeEnd]
data.choiceTime = [data.choiceTimeStart, data.choiceTimeEnd]
this.formData = data
this.$refs.guava.edit()
@@ -509,6 +541,10 @@ layout("/layouts/platform.html"){
this.welfareProvideMode.map((v) => {
v.code = Number(v.code)
})
// 新形式暂由本页面补充,避免依赖未纳入代码仓库的系统字典初始化数据。
if (!this.welfareProvideMode.some((v) => v.code === 99)) {
this.welfareProvideMode.push({ code: 99, name: "慰问金" })
}
this.welfareSignMode = await this.$businessTool.getDictOptions("welfareSignMode")
this.welfareSignMode.map((v) => {
v.code = Number(v.code)
@@ -9,7 +9,6 @@ const PROPOSAL_INFO = {
<van-cell-group :border="false">
<van-cell title="提案编号">{{ viewData.code || '--' }}</van-cell>
<van-cell title="提案名称">{{ viewData.name || '--' }}</van-cell>
<van-cell title="立案编号">{{ viewData.caseFilingCode || '暂无' }}</van-cell>
<van-cell title="立案结果" v-if="viewData.caseFilingResult">
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT"
:value="viewData.caseFilingResult"></dict-tag>
@@ -189,7 +188,7 @@ const PROPOSAL_INFO = {
<template v-else>
<van-cell title="办理单位"
v-if="['opinion_unit_reply', 'unit_reply', 'two_unit_reply', 'master_reply', 'slave_reply'].includes(selectedTask.taskName)">
v-if="['opinion_unit_reply', 'unit_reply', 'two_unit_reply', 'opinion_master_reply', 'master_reply', 'slave_reply'].includes(selectedTask.taskName)">
{{ selectedTask.ext.underTakeName || '--' }}
</van-cell>
<van-cell title="办理用户">
@@ -10,7 +10,7 @@ const PROPOSAL_TASK_POPUP = {
<div v-if="isApproval">
<van-form ref="formRef">
<template
v-if="kind === 'delegation' || kind === 'preAudit' || kind === 'schoolLeaderApproval'">
v-if="kind === 'delegation' || kind === 'schoolUnionChairApproval' || kind === 'schoolLeaderApproval'">
<section class="proposal-view-card">
<h2 class="proposal-view-card__title">审批意见<span
class="proposal-audit-card__required">*</span></h2>
@@ -114,6 +114,27 @@ const PROPOSAL_TASK_POPUP = {
:rules="[{required:true,message:'请填写审批意见'}]"></van-field>
</section>
</template>
<template v-if="kind === 'committeeFiling'">
<section class="proposal-view-card">
<h2 class="proposal-view-card__title">立案审核<span class="proposal-audit-card__required">*</span></h2>
<van-field :value="filingResultName" label="立案结果" readonly required is-link
placeholder="请选择立案结果" @click="openLayer('filing-result-picker')"></van-field>
<van-field v-if="formData.tf_caseFilingResult === 'CONFIRM_FILING' || formData.tf_caseFilingResult === 'SUGGESTION'"
:value="selectedFilingMasterNames"
:label="formData.tf_caseFilingResult === 'SUGGESTION' ? '办理单位' : '主办单位'"
readonly required is-link placeholder="请选择单位"
@click="openFilingUnitPicker('master')"></van-field>
<van-field v-if="formData.tf_caseFilingResult === 'CONFIRM_FILING'"
:value="selectedFilingSlaveNames" label="协办单位"
readonly is-link placeholder="可选"
@click="openFilingUnitPicker('slave')"></van-field>
<h2 class="proposal-view-card__title">审核意见<span class="proposal-audit-card__required">*</span></h2>
<van-field v-model="formData.tf_opinion" class="proposal-audit-field"
name="tf_opinion" type="textarea" rows="6" label=""
placeholder="请输入审核意见" required maxlength="500" show-word-limit
:rules="[{required:true,message:'请填写审核意见'}]"></van-field>
</section>
</template>
</van-form>
<div class="proposal-popup-form-actions" v-if="kind === 'delegation'">
<van-button type="primary" block :loading="formLoading" class="proposal-soft-button"
@@ -128,7 +149,7 @@ const PROPOSAL_TASK_POPUP = {
@click="prepareSubmit(6)">退回
</van-button>
</div>
<div class="proposal-popup-form-actions proposal-pre-audit-actions" v-else-if="kind === 'preAudit'"
<div class="proposal-popup-form-actions proposal-pre-audit-actions" v-else-if="kind === 'schoolUnionChairApproval'"
style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr))">
<van-button type="primary" :loading="formLoading"
class="proposal-soft-button proposal-pre-audit-button--primary"
@@ -148,6 +169,18 @@ const PROPOSAL_TASK_POPUP = {
</van-button>
</div>
<div class="proposal-popup-form-actions proposal-pre-audit-actions" v-else-if="kind === 'committeeFiling'"
style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr))">
<van-button type="primary" :loading="formLoading"
class="proposal-soft-button proposal-pre-audit-button--primary"
style="grid-column:1 / -1" @click="prepareSubmit(1)">提交</van-button>
<van-button type="info" :loading="formLoading"
class="proposal-soft-button proposal-pre-audit-button--info"
@click="prepareSubmit(4)">退回(不审核)</van-button>
<van-button type="info" :loading="formLoading"
class="proposal-soft-button proposal-pre-audit-button--info"
@click="prepareSubmit(6)">退回(审核)</van-button>
</div>
<div class="proposal-popup-form-actions" v-else-if="kind === 'schoolLeaderApproval'">
<van-button type="primary" block :loading="formLoading" class="proposal-soft-button"
@@ -211,11 +244,32 @@ const PROPOSAL_TASK_POPUP = {
<div class="reply-unit-empty" v-else>暂无可选办理单位</div>
</div>
</van-popup>
<van-popup v-model="filingResultPickerVisible" position="bottom" :close-on-click-overlay="false">
<van-picker show-toolbar :columns="(dict.type.PROPOSAL_CASE_FILING_RESULT || []).map((item) => ({text:item.label,value:item.code}))"
@confirm="confirmFilingResult" @cancel="closeLayer('filing-result-picker')"></van-picker>
</van-popup>
<van-popup v-model="filingUnitPickerVisible" position="bottom" round :close-on-click-overlay="false" :style="{height:'70%'}">
<div class="reply-unit-popup">
<div class="reply-unit-toolbar">
<button type="button" @click="closeLayer('filing-unit-picker')">取消</button>
<div class="reply-unit-toolbar-title">{{filingUnitType === 'master' ? '选择主办单位' : '选择协办单位'}}</div>
<button type="button" @click="confirmFilingUnits">确定</button>
</div>
<div class="reply-unit-list">
<van-checkbox-group v-model="pendingFilingUnitIds">
<van-cell v-for="item in filingUnitOptions" :key="item.id" clickable :title="item.name"
@click="toggleFilingUnit(item)">
<template #right-icon><van-checkbox :name="item.id" @click.stop></van-checkbox></template>
</van-cell>
</van-checkbox-group>
</div>
</div>
</van-popup>
<van-dialog :value="confirmVisible" title="提示" message="您确定要提交吗?" show-cancel-button
@confirm="submit" @cancel="closeLayer('submit-confirm')"></van-dialog>
</van-popup>
`,
dicts: ["PROPOSAL_FEEDBACK", "UNDERTAKE_SUGGESTION", "PROPOSAL_REPLY_IMPLEMENT"],
dicts: ["PROPOSAL_FEEDBACK", "UNDERTAKE_SUGGESTION", "PROPOSAL_REPLY_IMPLEMENT", "PROPOSAL_CASE_FILING_RESULT"],
components: {"proposal-info": PROPOSAL_INFO},
props: {kind: {type: String, required: true}, pageKey: {type: String, required: true}},
data() {
@@ -231,6 +285,11 @@ const PROPOSAL_TASK_POPUP = {
implementPickerVisible: false,
candidatePickerVisible: false,
replyUnitPickerVisible: false,
filingResultPickerVisible: false,
filingUnitPickerVisible: false,
filingUnitType: "master",
filingUnitOptions: [],
pendingFilingUnitIds: [],
confirmVisible: false,
supportCandidate: false,
candidates: [],
@@ -245,8 +304,9 @@ const PROPOSAL_TASK_POPUP = {
},
title() {
const titles = {
delegation: "团长审核",
preAudit: "校工会预审核",
delegation: "分工会主席审核",
schoolUnionChairApproval: "校工会主席审核",
committeeFiling: "提案委员立案审核",
schoolLeaderApproval: "分管校领导审批",
feedbackEvaluation: "反馈评价",
suggestion: "承办单位意见",
@@ -258,6 +318,16 @@ const PROPOSAL_TASK_POPUP = {
selectedReplyUnitNames() {
const ids = this.formData.tf_secondReplyUnitIds || [];
return this.replyUnitOptions.filter((item) => ids.indexOf(item.unitId) > -1).map((item) => item.unitName + "(" + (item.isMaster ? "主办" : "协办") + ")").join("、")
},
filingResultName() {
const item = (this.dict.type.PROPOSAL_CASE_FILING_RESULT || []).find((entry) => entry.code === this.formData.tf_caseFilingResult);
return item ? item.label : ""
},
selectedFilingMasterNames() {
return this.filingUnitOptions.filter((item) => (this.formData.tf_masterUnitIds || []).includes(item.id)).map((item) => item.name).join("、")
},
selectedFilingSlaveNames() {
return this.filingUnitOptions.filter((item) => (this.formData.tf_slaveUnitIds || []).includes(item.id)).map((item) => item.name).join("、")
}
},
methods: {
@@ -268,6 +338,8 @@ const PROPOSAL_TASK_POPUP = {
this.$set(this, "implementPickerVisible", stack.includes("implement-picker"));
this.$set(this, "candidatePickerVisible", stack.includes("candidate-picker"));
this.$set(this, "replyUnitPickerVisible", stack.includes("reply-unit-picker"));
this.$set(this, "filingResultPickerVisible", stack.includes("filing-result-picker"));
this.$set(this, "filingUnitPickerVisible", stack.includes("filing-unit-picker"));
this.$set(this, "confirmVisible", stack.includes("submit-confirm"))
},
openLayer(name) {
@@ -280,8 +352,9 @@ const PROPOSAL_TASK_POPUP = {
/** row 提供详情主键和当前 WF 任务参数,action 为 view 或 approval。 */
open(row, action) {
const defaultTaskNames = {
delegation: "团长审核",
preAudit: "校工会预审核",
delegation: "分工会主席审核",
schoolUnionChairApproval: "校工会主席审核",
committeeFiling: "提案委员立案审核",
feedbackEvaluation: "反馈评价",
unitReply: "承办单位答复",
caseCheck: "提案委员会审查"
@@ -312,6 +385,12 @@ const PROPOSAL_TASK_POPUP = {
this.$set(this.formData, "tf_secondReplyUnitIds", []);
this.loadReplyUnits(row.id)
}
if (action === "approval" && this.kind === "committeeFiling") {
this.$set(this.formData, "tf_caseFilingResult", row.caseFilingResult || "");
this.$set(this.formData, "tf_masterUnitIds", []);
this.$set(this.formData, "tf_slaveUnitIds", []);
this.loadFilingUnits()
}
this.openLayer("detail")
},
loadCandidates(taskId) {
@@ -369,6 +448,41 @@ const PROPOSAL_TASK_POPUP = {
this.$set(this.formData, "tf_secondReplyUnitIds", [...this.pendingReplyUnitIds]);
this.closeLayer("reply-unit-picker")
},
/** 立案审核的主办、协办单位均来自本项目承办单位目录。 */
loadFilingUnits() {
this.$axios.post("/platform/proposal/common/listUnderTake").then((res) => {
if (res && res.code === 0) this.$set(this, "filingUnitOptions", res.data || [])
})
},
/** value 为结果字典项;切换结果时清理不再适用的承办单位,并设置可编辑的默认意见。 */
confirmFilingResult(value) {
this.$set(this.formData, "tf_caseFilingResult", value.value);
this.$set(this.formData, "tf_masterUnitIds", []);
this.$set(this.formData, "tf_slaveUnitIds", []);
const opinions = {CONFIRM_FILING: "经提案委员会审理,该提案确定立案。", SUGGESTION: "经提案委员会审理,该提案作为意见建议。", NOT: "经提案委员会审理,该提案不予立案。"};
this.$set(this.formData, "tf_opinion", opinions[value.value] || "");
this.closeLayer("filing-result-picker")
},
/** type 为 master 或 slave,使用全局历史栈打开对应的单位选择弹层。 */
openFilingUnitPicker(type) {
this.$set(this, "filingUnitType", type);
this.$set(this, "pendingFilingUnitIds", [...(this.formData[type === "master" ? "tf_masterUnitIds" : "tf_slaveUnitIds"] || [])]);
this.openLayer("filing-unit-picker")
},
/** item 为承办单位;再次点击同一单位时取消选中。 */
toggleFilingUnit(item) {
const ids = [...this.pendingFilingUnitIds];
const index = ids.indexOf(item.id);
if (index >= 0) ids.splice(index, 1); else ids.push(item.id);
this.$set(this, "pendingFilingUnitIds", ids)
},
/** 保存当前选择,排除已在另一类承办单位中选过的记录。 */
confirmFilingUnits() {
const opposite = this.filingUnitType === "master" ? "tf_slaveUnitIds" : "tf_masterUnitIds";
const ids = this.pendingFilingUnitIds.filter((id) => !(this.formData[opposite] || []).includes(id));
this.$set(this.formData, this.filingUnitType === "master" ? "tf_masterUnitIds" : "tf_slaveUnitIds", ids);
this.closeLayer("filing-unit-picker")
},
buildCaseCheckData() {
const data = {...this.formData};
const units = data.tf_caseCheckNeedSecondReply === 1 ? this.replyUnitOptions.filter((item) => data.tf_secondReplyUnitIds.indexOf(item.unitId) > -1) : [];
@@ -395,6 +509,10 @@ const PROPOSAL_TASK_POPUP = {
this.$toast(opinionConfig.message)
return
}
if (this.kind === "committeeFiling" && value === 1) {
if (!this.formData.tf_caseFilingResult) return this.$toast("请选择立案结果");
if (["CONFIRM_FILING", "SUGGESTION"].includes(this.formData.tf_caseFilingResult) && !(this.formData.tf_masterUnitIds || []).length) return this.$toast("请选择主办或办理单位")
}
this.$refs.formRef.validate().then(() => {
this.$set(this, "pendingSubmitType", value);
this.openLayer("submit-confirm")
@@ -416,13 +534,29 @@ const PROPOSAL_TASK_POPUP = {
...this.buildCaseCheckData(),
submitType: this.pendingSubmitType
} : {...this.formData, submitType: this.pendingSubmitType}
if (this.kind === "preAudit") {
if (this.kind === "schoolUnionChairApproval") {
data.taskName = "startTask";
if (this.pendingSubmitType === 4) {
data.tf_sourceTaskKey = this.formData.taskKey;
data.tf_audit = false
}
}
if (this.kind === "committeeFiling") {
const masters = this.filingUnitOptions.filter((item) => (data.tf_masterUnitIds || []).includes(item.id));
const slaves = this.filingUnitOptions.filter((item) => (data.tf_slaveUnitIds || []).includes(item.id));
data.tf_masterUnitId = masters.length ? masters[0].id : "";
data.tf_masterUnitName = masters.length ? masters[0].name : "";
data.tf_masterUnitNames = masters.map((item) => item.name);
data.tf_masterUnitNameStr = data.tf_masterUnitNames.join(",");
data.tf_slaveUnitNames = slaves.map((item) => item.name);
data.tf_slaveUnitNameStr = data.tf_slaveUnitNames.join(",");
data.tf_helpunitreply = slaves.length ? "HAS_HELP_UNIT" : "NO_HELP_UNIT";
if (this.pendingSubmitType === 4) {
data.taskName = "startTask";
data.tf_sourceTaskKey = this.formData.taskKey;
data.tf_audit = false
}
}
if (this.kind === "unitReply") {
delete data.underTakeIsMaster;
delete data.underTakeName
@@ -432,6 +566,7 @@ const PROPOSAL_TASK_POPUP = {
data.tf_masterUnitId = res.data.tf_masterUnitId;
return this.$axios.post("/flow/common/executeTask", {data: JSON.stringify(data)})
})
else if (this.kind === "committeeFiling") request = this.$axios.post("/platform/proposal/committeeFiling/executeTask", {data: JSON.stringify(data)})
else request = this.$axios.post("/flow/common/executeTask", {data: JSON.stringify(data)})
}
request.then((res) => {
@@ -300,7 +300,6 @@ layout("/layouts/platform_h5.html"){
</div>
</template>
<template v-slot="{index,row}">
<table-column label="立案编号">{{row.caseFilingCode || '--'}}</table-column>
<table-column label="提案类别">{{row.typeName || '--'}}</table-column>
<table-column label="提案人">{{row.createUserName || '--'}}</table-column>
<table-column label="教代会">{{row.sessionName || '--'}}</table-column>
@@ -0,0 +1,415 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style>
body {
background-color: #f3f7fd;
}
.proposal-pre-audit-page {
min-height: 100vh;
padding-bottom: calc(24px + env(safe-area-inset-bottom));
box-sizing: border-box;
background-color: #f3f7fd;
}
.proposal-pre-audit-page .van-nav-bar {
background-color: #fff;
}
.proposal-pre-audit-page .van-nav-bar__title {
color: #172033;
font-size: 16px;
font-weight: 600;
}
.proposal-pre-audit-page .van-nav-bar .van-icon,
.proposal-pre-audit-page .van-nav-bar__text {
color: #1989fa;
}
.proposal-pre-audit-content {
padding: 0 12px;
}
.proposal-pre-audit-sticky {
padding: 22px 12px 1px;
box-sizing: border-box;
background-color: #f3f7fd;
}
.proposal-pre-audit-search {
margin: 0 0 8px;
padding: 0;
overflow: hidden;
border-radius: 12px;
background-color: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
}
.proposal-pre-audit-search .van-search__content {
height: 42px;
padding-left: 12px;
align-items: center;
border-radius: 12px;
background-color: #fff;
}
.proposal-pre-audit-search .van-field__control {
color: #263548;
font-size: 14px;
}
.proposal-pre-audit-search .van-field__control::placeholder {
color: #a7b0bf;
}
.proposal-pre-audit-filter {
margin: 0 0 10px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
transition: border-radius .18s ease;
}
.proposal-pre-audit-filter.is-open {
border-radius: 10px 10px 0 0;
}
.proposal-pre-audit-filter .van-dropdown-menu__bar,
.proposal-pre-audit-filter .van-dropdown-menu__item,
.proposal-pre-audit-filter .van-dropdown-menu__title {
height: 44px;
}
.proposal-pre-audit-filter .van-dropdown-menu__bar {
box-shadow: none;
}
.proposal-pre-audit-filter .van-dropdown-menu__title {
display: inline-flex;
align-items: center;
color: #66758a;
font-size: 14px;
line-height: 20px;
}
.proposal-pre-audit-filter .van-dropdown-menu__title--active,
.proposal-pre-audit-filter .van-dropdown-item__option--active,
.proposal-pre-audit-filter .van-dropdown-item__option--active .van-dropdown-item__icon {
color: #1989fa;
}
.proposal-pre-audit-filter .van-dropdown-item__content {
width: calc(100% - 24px);
margin: 0 12px;
overflow: hidden;
box-sizing: border-box;
border-radius: 0 0 12px 12px;
box-shadow: 0 10px 24px rgba(31, 65, 108, .14);
}
.proposal-pre-audit-filter .van-dropdown-item__option {
display: flex;
min-height: 48px;
padding: 0 16px;
align-items: center;
color: #46566c;
font-size: 14px;
}
.proposal-pre-audit-list .empty-state {
display: none;
}
.proposal-pre-audit-empty {
display: flex;
min-height: 280px;
padding: 46px 18px 36px;
align-items: center;
justify-content: center;
flex-direction: column;
box-sizing: border-box;
text-align: center;
}
.proposal-pre-audit-empty img {
display: block;
width: 86%;
max-width: 290px;
height: auto;
object-fit: contain;
}
.proposal-pre-audit-empty__title {
margin-top: 14px;
color: #50627a;
font-size: 15px;
line-height: 22px;
font-weight: 600;
}
.proposal-pre-audit-empty__hint {
margin-top: 5px;
color: #9aa7b8;
font-size: 12px;
line-height: 18px;
}
.proposal-pre-audit-card-header {
display: flex;
margin-bottom: 7px;
align-items: flex-start;
justify-content: space-between;
}
.proposal-pre-audit-card-heading {
min-width: 0;
padding-right: 10px;
flex: 1;
}
.proposal-pre-audit-card-title {
overflow: hidden;
color: #253247;
font-size: 15px;
line-height: 22px;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
.proposal-pre-audit-card-code {
margin-top: 1px;
color: #9ba6b6;
font-size: 12px;
line-height: 18px;
}
.proposal-pre-audit-status {
display: inline-flex;
flex: none;
}
.proposal-pre-audit-status .van-tag {
display: inline-flex;
min-height: 24px;
padding: 3px 9px;
align-items: center;
box-sizing: border-box;
border: 0;
border-radius: 12px;
font-size: 12px;
line-height: 18px;
white-space: nowrap;
}
@media (max-width: 350px) {
.proposal-pre-audit-content,
.proposal-pre-audit-sticky {
padding-right: 10px;
padding-left: 10px;
}
}
</style>
<style id="style-proposal-task-popup-h5">
<!--#include("../../common/info.css"){}#-->
<!--#include("../theme.css"){}#-->
</style>
<div id="app" v-cloak>
<div class="proposal-pre-audit-page">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="提案委员立案审核" placeholder fixed></van-nav-bar>
<van-sticky :offset-top="46">
<div class="proposal-pre-audit-sticky">
<van-search
v-model="pageForm.name"
class="proposal-pre-audit-search"
placeholder="请输入提案名称搜索"
shape="round"
clearable
@search="doSearch"
@clear="doSearch"
></van-search>
<van-dropdown-menu
class="proposal-pre-audit-filter"
:class="{'is-open': filterOpened}"
active-color="#1989fa"
>
<van-dropdown-item
v-model="pageForm.sessionId"
:options="sessionOptions"
:multiple="false"
@change="doSearch"
@open="onFilterOpen"
@close="onFilterClose"
></van-dropdown-item>
<van-dropdown-item
v-model="pageForm.approvalText"
:options="approvalOptions"
:multiple="false"
@change="approvalChange"
@open="onFilterOpen"
@close="onFilterClose"
></van-dropdown-item>
</van-dropdown-menu>
</div>
</van-sticky>
<main class="proposal-pre-audit-content">
<div class="proposal-pre-audit-list">
<!-- 列表无记录时使用与“我的提案”一致的提案专用空状态。 -->
<div v-if="pageForm.totalCount === 0" class="proposal-pre-audit-empty">
<img src="/assets/mobile/img/proposal/proposal-empty.png" alt="暂无提案记录插画">
<div class="proposal-pre-audit-empty__title">暂无提案记录</div>
<div class="proposal-pre-audit-empty__hint">当前筛选条件下暂时没有提案</div>
</div>
<table-list api="/platform/proposal/committeeFiling/pageData" :page_form.sync="pageForm" @ready="onReady"
ref="tableListRef" title="name">
<template #header="{index,row}">
<div class="proposal-pre-audit-card-header">
<div class="proposal-pre-audit-card-heading">
<div class="proposal-pre-audit-card-title">{{row.name}}</div>
<div class="proposal-pre-audit-card-code">编号:{{row.code || '--'}}</div>
</div>
<enum-tag
class="proposal-pre-audit-status"
v-if="row.instanceState"
:value="row.instanceState"
name="ProcessInstanceStateEnum"
label_key="message"
size="small"
></enum-tag>
</div>
</template>
<template v-slot="{index,row}">
<table-column label="提案类别">{{row.typeName || '--'}}</table-column>
<table-column label="提案人">{{row.createUserName || '--'}}</table-column>
<table-column label="教代会">{{row.sessionName || '--'}}</table-column>
<table-column label="代表团">{{row.delegationName || '--'}}</table-column>
<table-column label="当前节点">{{row.curTaskName || '--'}}</table-column>
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row,index)">
<van-icon class="proposal-action-icon" name="eye-o"></van-icon>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row,index)">
<van-icon class="proposal-action-icon" name="edit"></van-icon>
<span>审核</span>
</div>
<div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">
<van-icon class="proposal-action-icon" name="revoke"></van-icon>
<span>撤回</span>
</div>
</template>
</table-list>
</div>
</main>
</div>
<proposal-task-popup ref="taskPopup" kind="committeeFiling" page-key="proposal-committee-filing-task" @success="doSearch"></proposal-task-popup>
</div>
<script nonce="${cspNonce!}">
<!--#include("../../common/info.js"){}#-->
<!--#include("../../common/task-popup.js"){}#-->
new Vue({
el: "#app",
store,
components: {"proposal-task-popup": PROPOSAL_TASK_POPUP},
data() {
return {
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
pageOrderName: "",
pageOrderBy: "",
name: null,
sessionId: null,
approvalText: "0",
approval: false
},
sessionOptions: [],
filterOpened: false,
approvalOptions: [
{text: "未审核", value: "0"},
{text: "已审核", value: "1"}
],
}
},
methods: {
onReady() {
this.listSession(false)
},
onFilterOpen() {
this.filterOpened = true
},
onFilterClose() {
this.filterOpened = false
},
// 审核状态下拉使用字符串展示,查询接口使用布尔值区分已审核和未审核。
approvalChange(value) {
this.$set(this.pageForm, "approval", value === "1")
this.doSearch()
},
// 与其他提案 H5 页面一致加载全部教代会;首次进入默认选中最新一届,缓存恢复时不覆盖原筛选条件。
listSession(skipDefaultSearch) {
this.$axios.post("/platform/proposal/common/listSession").then((res) => {
if (res.code === 0) {
this.sessionOptions = [{text: "全部教代会", value: null}]
.concat(res.data.map((item) => ({text: item.fullName, value: item.id})))
if (skipDefaultSearch) return
this.$set(this.pageForm, "sessionId", res.data.length > 0 ? res.data[0].id : null)
this.doSearch()
}
})
},
onView(row, index) {
this.$refs.taskPopup.open(row, "view")
},
onApproval(row, index) {
this.$refs.taskPopup.open(row, "approval")
},
onRevoke(row) {
this.$dialog.confirm({
title: "提示",
message: "您确定要撤回吗?"
}).then(() => {
this.$axios.post("/flow/common/revokeTask", {taskId: row.taskId}).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg)
this.doSearch()
}
})
})
},
doSearch() {
this.$nextTick(() => {
this.$set(this.pageForm, "pageNumber", 1)
this.$set(this.pageForm, "totalCount", 0)
this.$refs.tableListRef.doSearch()
})
}
},
created() {
const tab = new URLSearchParams(window.location.search).get("tab")
if (tab === "done") {
this.$set(this.pageForm, "approval", true)
this.$set(this.pageForm, "approvalText", "1")
} else if (tab === "todo") {
this.$set(this.pageForm, "approval", false)
this.$set(this.pageForm, "approvalText", "0")
}
}
})
</script>
<!--#
}
#-->
@@ -235,7 +235,7 @@ layout("/layouts/platform_h5.html"){
</style>
<div id="app" v-cloak>
<div class="proposal-delegation-page">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="团长审核" placeholder fixed></van-nav-bar>
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="分工会主席审核" placeholder fixed></van-nav-bar>
<van-sticky :offset-top="46">
<div class="proposal-delegation-sticky">
<van-search
@@ -218,7 +218,7 @@ layout("/layouts/platform_h5.html"){
</style>
<div id="app" v-cloak>
<div class="proposal-pre-audit-page">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="校工会预审核" placeholder fixed></van-nav-bar>
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="校工会主席审核" placeholder fixed></van-nav-bar>
<van-sticky :offset-top="46">
<div class="proposal-pre-audit-sticky">
<van-search
@@ -263,7 +263,7 @@ layout("/layouts/platform_h5.html"){
<div class="proposal-pre-audit-empty__title">暂无提案记录</div>
<div class="proposal-pre-audit-empty__hint">当前筛选条件下暂时没有提案</div>
</div>
<table-list api="/platform/proposal/pre/audit/pageData" :page_form.sync="pageForm" @ready="onReady"
<table-list api="/platform/proposal/schoolUnionChairApproval/pageData" :page_form.sync="pageForm" @ready="onReady"
ref="tableListRef" title="name">
<template #header="{index,row}">
<div class="proposal-pre-audit-card-header">
@@ -282,7 +282,6 @@ layout("/layouts/platform_h5.html"){
</div>
</template>
<template v-slot="{index,row}">
<table-column label="立案编号">{{row.caseFilingCode || '--'}}</table-column>
<table-column label="提案类别">{{row.typeName || '--'}}</table-column>
<table-column label="提案人">{{row.createUserName || '--'}}</table-column>
<table-column label="教代会">{{row.sessionName || '--'}}</table-column>
@@ -307,7 +306,7 @@ layout("/layouts/platform_h5.html"){
</div>
</main>
</div>
<proposal-task-popup ref="taskPopup" kind="preAudit" page-key="proposal-pre-audit-task" @success="doSearch"></proposal-task-popup>
<proposal-task-popup ref="taskPopup" kind="schoolUnionChairApproval" page-key="proposal-school-union-chair-task" @success="doSearch"></proposal-task-popup>
</div>
<script nonce="${cspNonce!}">
<!--#include("../../common/info.js"){}#-->
@@ -285,7 +285,7 @@ const todo = {
this.$toast("当前流程暂不支持手机端审核,请前往PC端页面审核!");
return;
}
this.$pjaxReplace(h5FormKey + "?taskId=" + taskId + "bizId=" + businessNo + "taskKey=" + taskKey+ "&tab=" + this.activeTab)
this.$pjaxReplace(h5FormKey + "?taskId=" + taskId + "&bizId=" + businessNo + "&taskKey=" + taskKey + "&tab=" + this.activeTab)
},
// 根据当前列表类型统一展示流程状态,发起列表保留流程实例的实际状态。