This commit is contained in:
=
2026-07-20 14:49:41 +08:00
parent 64dbcba3c6
commit 7ae31b9103
84 changed files with 6615 additions and 71 deletions
@@ -12,6 +12,7 @@ import com.budwk.app.web.commons.auth.satoken.StpInterfaceImpl;
import com.budwk.app.web.commons.base.Globals;
import com.budwk.app.web.commons.ext.pubsub.WebPubSub;
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service.impl.DifficultSubsidySchemaInitializer;
import lombok.extern.slf4j.Slf4j;
import org.beetl.core.GroupTemplate;
import org.nutz.boot.NbApp;
@@ -61,6 +62,8 @@ public class MainLauncher {
private SysTaskService sysTaskService;
@Inject
private Dao dao;
@Inject
private DifficultSubsidySchemaInitializer difficultSubsidySchemaInitializer;//确保特困补助实体迁移随应用启动执行
public static void main(String[] args) throws Exception {
NbApp nb = new NbApp().setArgs(args).setPrintProcDoc(true);
@@ -19,25 +19,28 @@ import org.nutz.lang.Lang;
import java.util.List;
/**
* 获取申请人所在分工会的分工会主席
* 获取申请人所在分工会的分工会主席和管理员
*/
public class FlowFghzxAssignmentHandler implements AssignmentHandler {
@Override
public List<String> assign(TaskModel model, Execution execution) {
Dao dao = ServiceContext.find(Dao.class);
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.BRANCH_UNION_CHAIRMAN);
Sys_role chairmanRole = sysRoleService.getByCode(RoleConstant.BRANCH_UNION_CHAIRMAN);
Sys_role administratorRole = sysRoleService.getByCode(RoleConstant.BRANCH_UNION_ADMIN);
String unionId = StrUtil.blankToDefault(execution.getArgs().getStr(FlowConst.INITIATOR_UNIT_UNION_ID), SecurityUtil.getUnionId());
List<Sys_user_role> user_roles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()).and(Sys_user_role::getUnionId, "=", unionId));
// 同一分工会的主席和管理员均可处理该节点,重复配置的人员只返回一次。
List<Sys_user_role> user_roles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "in", List.of(chairmanRole.getId(), administratorRole.getId()))
.and(Sys_user_role::getUnionId, "=", unionId));
if (Lang.isEmpty(user_roles)) {
throw new BaseException("分工会主席没有设置,请联系校工会!", sysRole.getCode());
throw new BaseException("提案人所属分工会主席和管理员均未设置,请联系校工会!", chairmanRole.getCode());
}
return user_roles.stream().map(Sys_user_role::getUserId).distinct().toList();
}
@Override
public String getMessage() {
return "当前用户所属分工会主席";
return "提案人所属分工会主席和管理员";
}
@Override
@@ -2,6 +2,7 @@ package com.budwk.app.flow.service;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.event.flow.jumptofirsttask.FlowJumpToFirstTaskNodePublisher;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.constant.FlowConst;
@@ -9,9 +10,11 @@ import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.flow.engine.event.ProcessEvent;
import com.budwk.app.flow.engine.event.ProcessPublisher;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessDefine;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.*;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.sys.models.Sys_user_signature;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
@@ -22,10 +25,17 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import java.util.List;
import java.util.Set;
@IocBean
public class FlowCommonService {
/** 教代会提案中从二级工会审核开始,可留存当前办理人的电子签名快照。 */
private static final Set<String> PROPOSAL_SIGNATURE_TASK_NAMES = Set.of(
"secondaryUnionAudit", "partyOrganizationAudit", "committee", "committeeFilingUnit",
"unit_reply", "schoolLeader", "feedback"
);
@Inject
private FlowEngine flowEngine;
@@ -39,6 +49,9 @@ public class FlowCommonService {
public void executeTask(Dict args){
Long processTaskId = args.getLong(FlowConst.PROCESS_TASK_ID_KEY);
ProcessTask processTask = flowEngine.processTaskService().getById(processTaskId);
appendProposalSignature(processTask, args);
String operator = SecurityUtil.getUserId();
if (args.containsKey(FlowConst.EXT_ENABLE_OPERATOR)&&args.getBool(FlowConst.EXT_ENABLE_OPERATOR)){
operator = FlowConst.ADMIN_ID;
@@ -49,7 +62,6 @@ public class FlowCommonService {
submitType = ProcessSubmitTypeEnum.AGREE.getCode();
}
ProcessTask processTask = flowEngine.processTaskService().getById(processTaskId);
ProcessTask parentTask = flowEngine.processTaskService().getById(processTask.getTaskParentId());
if(parentTask != null) {
Dict taskArgs = Json.fromJson(Dict.class, parentTask.getVariable());
@@ -98,6 +110,34 @@ public class FlowCommonService {
}
}
/**
* 为教代会提案审核任务保存签字 URL 快照。
* 仅处理 JDHTA_NC 流程,避免电子签名快照影响系统其他工作流;用户已维护签名时才归档,
* 未维护签名也允许正常提交。快照随任务变量归档,后续用户更新个人签名不会改变已办任务的导出结果。
*
* @param processTask 当前待办任务
* @param args 即将写入任务变量的表单参数
*/
private void appendProposalSignature(ProcessTask processTask, Dict args) {
if (processTask == null || !PROPOSAL_SIGNATURE_TASK_NAMES.contains(processTask.getTaskName())) {
return;
}
ProcessInstance processInstance = dao.fetch(ProcessInstance.class, processTask.getProcessInstanceId());
if (processInstance == null) {
return;
}
ProcessDefine processDefine = dao.fetch(ProcessDefine.class, processInstance.getProcessDefineId());
if (processDefine == null || !"JDHTA_NC".equals(processDefine.getName())) {
return;
}
Sys_user_signature userSignature = dao.fetch(Sys_user_signature.class,
Cnd.where(Sys_user_signature::getUserId, "=", SecurityUtil.getUserId()));
if (userSignature == null || StrUtil.isBlank(userSignature.getSignature())) {
return;
}
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "signature", userSignature.getSignature());
}
@Aop(TransAop.READ_COMMITTED)
public Result revokeTask(Long taskId) {
// 自己任务
@@ -120,6 +120,8 @@ public class ProcessTaskServiceImpl extends BaseServiceImpl<ProcessTask> impleme
public List<ProcessTask> getDoneTaskList(Long processInstanceId, String[] taskNames) {
Cnd cnd = Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId).and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode());
cnd.andEX(ProcessTask::getTaskName, "", taskNames);
// 已办记录必须按实际办结时间返回,避免补迁历史任务因写入时间较晚而排在流程末尾。
cnd.asc(ProcessTask::getFinishTime).asc(ProcessTask::getId);
List<ProcessTask> processTaskList = query(cnd);
return processTaskList;
}
@@ -131,7 +131,9 @@ public class SysSignatureController {
@Aop(TransAop.READ_COMMITTED)
public Result update(@Param("file") TempFile tempFile) {
String url = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), tempFile);
Sys_user_signature sysUserSignature = dao.fetch(Sys_user_signature.class);
// 每位用户维护独立的电子签名,不能取任意一条记录后覆盖其归属用户。
Sys_user_signature sysUserSignature = dao.fetch(Sys_user_signature.class,
Cnd.where(Sys_user_signature::getUserId, "=", SecurityUtil.getUserId()));
if (ObjectUtil.isNull(sysUserSignature)) {
sysUserSignature = new Sys_user_signature();
}
@@ -259,11 +259,10 @@ public class SysUnionController {
@At
@SaCheckPermission("sys.manager.union")
public Result branchUnionUserPageData(PageForm pageForm, String unionId, @Param("j") String j) {
List<Sys_dict> branchUnionRoles = sysDictService.getSubListByCode("BRANCH_UNION_ROLES");
if (Lang.isEmpty(branchUnionRoles)) {
List<String> branchUnionRoleCodes = getBranchUnionRoleCodes();
if (Lang.isEmpty(branchUnionRoleCodes)) {
return Result.success();
}
List<String> branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList();
Sql sql = Sqls.create("""
SELECT
@@ -334,11 +333,10 @@ public class SysUnionController {
@At
@SaCheckPermission("sys.manager.union")
public Result branchUnionUserUsedJData(String unionId) {
List<Sys_dict> branchUnionRoles = sysDictService.getSubListByCode("BRANCH_UNION_ROLES");
if (Lang.isEmpty(branchUnionRoles)) {
List<String> branchUnionRoleCodes = getBranchUnionRoleCodes();
if (Lang.isEmpty(branchUnionRoleCodes)) {
return Result.success(List.of());
}
List<String> branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList();
Sql sql = Sqls.create("""
SELECT DISTINCT
@@ -371,16 +369,46 @@ public class SysUnionController {
return Result.success(usedJCodes);
}
/**
* 返回分工会干部页面可维护的角色,审核角色不依赖字典重复配置,
* 直接使用系统角色编码,避免单位党委书记在多个字典目录中出现同码数据。
*/
@At
@SaCheckPermission("sys.manager.union")
@ApiOperation("查询分工会干部可选角色")
public Result branchUnionRoleOptions() {
List<NutMap> options = getBranchUnionRoleCodes().stream()
.map(sysRoleService::getByCode)
.filter(role -> role != null)
.map(role -> NutMap.NEW().addv("code", role.getCode()).addv("name", role.getName()))
.toList();
return Result.success(options);
}
/**
* 汇总分工会干部和提案审核所需角色,后续列表、历史届次和新增校验统一使用同一范围。
*/
private List<String> getBranchUnionRoleCodes() {
List<String> roleCodes = new ArrayList<>(sysDictService.getSubListByCode("BRANCH_UNION_ROLES")
.stream().map(Sys_dict::getCode).toList());
roleCodes.add(RoleConstant.BRANCH_UNION_ADMIN.name());
roleCodes.add(RoleConstant.UNIT_PARTY_SECRETARY.name());
return roleCodes.stream().filter(StrUtil::isNotBlank).distinct().toList();
}
@At
@SaCheckPermission("sys.manager.union.branchOfficer")
@ApiOperation("添加分工会人员角色")
@Aop(TransAop.READ_COMMITTED)
public Result insertBranchUnionUserRole(String userId, String roleCode, String unionId, String j) {
if (!getBranchUnionRoleCodes().contains(roleCode)) {
return Result.error("该角色不允许在分工会干部中维护");
}
Sys_role role = sysRoleService.fetch(Cnd.where("code", "=", roleCode));
if (Lang.isEmpty(role)) {
return Result.success("无法找到" + roleCode + "对应编码的角色");
return Result.error("无法找到" + roleCode + "对应编码的角色");
}
int count = dao.count(Sys_user_role.class, Cnd.where("roleId", "=", role.getId()).and("userId", "=", userId));
int count = dao.count(Sys_user_role.class, Cnd.where("roleId", "=", role.getId()).and("userId", "=", userId).and("unionId", "=", unionId));
if (count > 0) {
return Result.error("请勿重复添加");
}
@@ -404,6 +432,9 @@ public class SysUnionController {
// 构建表结构存储
View_user user = dao.fetch(View_user.class, Cnd.where("id", "=", userId));
if (user == null) {
return Result.error("未找到人员信息");
}
Sys_union_cadre unionCadre = new Sys_union_cadre();
unionCadre.setUserId(userId);
@@ -50,6 +50,8 @@ import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
;
@@ -305,6 +307,7 @@ public class SysUnitController {
Cnd cnd = Cnd.NEW();
cnd.asc("unitcode");
List<Sys_unit> list = sysUnitService.query(cnd);
Set<String> unitIds = list.stream().map(Sys_unit::getId).collect(Collectors.toSet());
List<TreeNode<String>> nodeList = CollUtil.newArrayList();
for (int i = 0; i < list.size(); i++) {
@@ -312,8 +315,12 @@ public class SysUnitController {
/*
* 单位根节点存在 id 与 parentId 都为 0 的自引用数据。
* 构建树时把当前查询根挂到虚拟根下,避免“中国地质大学”和 parentId=0 的学院被构造成同级。
* 同时兼容迁移数据中根节点 parentId 为空或父级不存在的情况,确保这些节点可被树组件展示。
*/
String parentId = rootId.equals(unit.getId()) ? virtualRootId : unit.getParentId();
String parentId = unit.getParentId();
if (rootId.equals(unit.getId()) || StrUtil.isBlank(parentId) || !unitIds.contains(parentId)) {
parentId = virtualRootId;
}
nodeList.add(new TreeNode<>(unit.getId(), parentId, unit.getName(), i)
.setExtra(
Map.of(
@@ -420,7 +420,7 @@ public class UnionReimburseMineController {
@At
private void exportAsPDF(String templateName, HashMap<String, Object> docData,
UnionReimburse unionReimburse, HttpServletResponse response) throws Exception {
String fileName = "中国地质大学(武汉)工会经费日常报销_" + unionReimburse.getUserName() + "_" +
String fileName = "西南财经大学工会经费日常报销_" + unionReimburse.getUserName() + "_" +
DateUtil.format(unionReimburse.getCreateTime(), "yyyyMMdd") + ".pdf";
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
@@ -446,7 +446,7 @@ public class UnionReimburseMineController {
@At
private void exportAsWord(String templateName, HashMap<String, Object> docData,
UnionReimburse unionReimburse, HttpServletResponse response) throws Exception {
String fileName = "中国地质大学(武汉)工会经费日常报销_" + unionReimburse.getUserName() + "_" +
String fileName = "西南财经大学工会经费日常报销_" + unionReimburse.getUserName() + "_" +
DateUtil.format(unionReimburse.getCreateTime(), "yyyyMMdd") + ".docx";
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
@@ -148,6 +148,14 @@ public class ProposalExportComprehensiveController {
proposalExportService.exportProposalStatisticsAsDocx(pageForm, response);
}
@At
@Ok("void")
@ApiOperation("按综合导出模板导出单条提案")
@SaCheckPermission("proposal.query.comprehensive")
public void exportProposalAsDocx(@Valid String id, HttpServletResponse response) {
proposalExportService.exportProposalAsDocx(id, response);
}
@At
@Ok("void")
@ApiOperation("导出全部提案反馈表ZIP")
@@ -52,6 +52,16 @@ public class ProposalCommitteeFilingController {
public void index() {
}
/**
* 提案委员会立案 H5 办理页
* H5 权限与 PC 权限独立配置避免手机端通过 PC 菜单权限访问审核页面
*/
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/committeeFiling/index.html")
@SaCheckPermission("h5.proposal.committeeFiling")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.committeeFiling")
@ApiOperation("分页列表")
@@ -116,6 +126,17 @@ public class ProposalCommitteeFilingController {
return Result.success(pagination);
}
/**
* 提案委员会立案 H5 列表
* 列表数据与 PC 保持同一任务参与人和状态口径仅切换为 H5 菜单权限
*/
@At("/h5/pageData")
@SaCheckPermission("h5.proposal.committeeFiling")
@ApiOperation("提案委员会立案 H5 分页列表")
public Result h5PageData(@Valid ProposalSearchParam pageForm, boolean approval) {
return pageData(pageForm, approval);
}
@At
@Ok("void")
@SaCheckPermission("proposal.committeeFiling")
@@ -65,6 +65,16 @@ public class ProposalCommitteeFilingUnitController {
public void index() {
}
/**
* 提案委员会确认承办单位 H5 办理页
* H5 权限与 PC 权限独立配置避免手机端通过 PC 菜单权限访问审核页面
*/
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/committeeFilingUnit/index.html")
@SaCheckPermission("h5.proposal.committeeFilingUnit")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.committeeFilingUnit")
@ApiOperation("分页列表")
@@ -129,6 +139,17 @@ public class ProposalCommitteeFilingUnitController {
return Result.success(pagination);
}
/**
* 提案委员会确认承办单位 H5 列表
* 列表数据与 PC 保持同一任务参与人和状态口径仅切换为 H5 菜单权限
*/
@At("/h5/pageData")
@SaCheckPermission("h5.proposal.committeeFilingUnit")
@ApiOperation("提案委员会确认承办单位 H5 分页列表")
public Result h5PageData(@Valid ProposalSearchParam pageForm, boolean approval) {
return pageData(pageForm, approval);
}
@At
@Ok("void")
@SaCheckPermission("proposal.committeeFilingUnit")
@@ -148,6 +169,20 @@ public class ProposalCommitteeFilingUnitController {
return Result.success();
}
/**
* 确认承办单位 H5 提交
* 复用原有服务保证并案提案承办单位和立案结果的同步规则与 PC 一致
*/
@At("/h5/executeTask")
@SaCheckPermission("h5.proposal.committeeFilingUnit")
@ApiOperation("提案委员会确认承办单位 H5 执行任务")
@Aop(TransAop.READ_COMMITTED)
public Result h5ExecuteTask(@Param("data") String data) {
Dict args = Json.fromJson(Dict.class, data);
proposalCommitteeFilingUnitService.executeFlowTask(args);
return Result.success();
}
@At
@SaCheckLogin
@@ -26,6 +26,7 @@ import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalSecond;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalMessageService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -94,6 +95,8 @@ public class ProposalMineController {
private ProcessTaskService processTaskService;
@Inject
private FlowCommonService flowCommonService;
@Inject
private ProposalMessageService proposalMessageService;
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/mine/index.html")
@@ -384,6 +387,8 @@ public class ProposalMineController {
return seconded;
}).toList();
dao.insert(secondeds);
// V3 在邀请附议人保存成功后逐个发送业务短信实际下发仍由 SmsService 配置开关控制
proposalMessageService.sendInviteSeconderMessage(proposalId, userIds);
return Result.success();
}
@@ -0,0 +1,65 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.ExportTableColumns;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
/**
* 提案院级党组织审核
*/
@IocBean
@At("/platform/proposal/partyOrganizationAudit")
@Ok("json:full")
@Api(tags = "提案办理-院级党组织审核")
public class ProposalPartyOrganizationAuditController {
@Inject
private ProposalCommonService proposalCommonService;
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/delegation/index.html")
@SaCheckPermission("proposal.partyOrganizationAudit")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/delegation/index.html")
@SaCheckPermission("h5.proposal.partyOrganizationAudit")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.partyOrganizationAudit")
@ApiOperation("院级党组织审核分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval) {
return Result.success(proposalCommonService.getWorkflowAuditPage("partyOrganizationAudit", pageForm, approval));
}
@At("/h5/pageData")
@SaCheckPermission("h5.proposal.partyOrganizationAudit")
@ApiOperation("院级党组织审核手机端分页列表")
public Result h5PageData(@Valid ProposalSearchParam pageForm, boolean approval) {
return Result.success(proposalCommonService.getWorkflowAuditPage("partyOrganizationAudit", pageForm, approval));
}
@At
@Ok("void")
@SaCheckPermission("proposal.partyOrganizationAudit")
@ApiOperation("导出院级党组织审核列表")
public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval,
@Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) {
proposalCommonService.exportWorkflowList("partyOrganizationAudit", pageForm, approval, tableColumns, response);
}
}
@@ -0,0 +1,65 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.ExportTableColumns;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
/**
* 提案二级工会审核
*/
@IocBean
@At("/platform/proposal/secondaryUnionAudit")
@Ok("json:full")
@Api(tags = "提案办理-二级工会审核")
public class ProposalSecondaryUnionAuditController {
@Inject
private ProposalCommonService proposalCommonService;
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/delegation/index.html")
@SaCheckPermission("proposal.secondaryUnionAudit")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/delegation/index.html")
@SaCheckPermission("h5.proposal.secondaryUnionAudit")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.secondaryUnionAudit")
@ApiOperation("二级工会审核分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval) {
return Result.success(proposalCommonService.getWorkflowAuditPage("secondaryUnionAudit", pageForm, approval));
}
@At("/h5/pageData")
@SaCheckPermission("h5.proposal.secondaryUnionAudit")
@ApiOperation("二级工会审核手机端分页列表")
public Result h5PageData(@Valid ProposalSearchParam pageForm, boolean approval) {
return Result.success(proposalCommonService.getWorkflowAuditPage("secondaryUnionAudit", pageForm, approval));
}
@At
@Ok("void")
@SaCheckPermission("proposal.secondaryUnionAudit")
@ApiOperation("导出二级工会审核列表")
public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval,
@Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) {
proposalCommonService.exportWorkflowList("secondaryUnionAudit", pageForm, approval, tableColumns, response);
}
}
@@ -198,19 +198,9 @@ public class ProposalSecondedController {
}
// 更新 proposal_second记录当前附议人的最终附议结果附议意见和附议时间
ProposalSecond proposalSecond = dao.fetch(
ProposalSecond.class,
Cnd.where(ProposalSecond::getProposalId, "=", proposalId)
.and(ProposalSecond::getSeconderId, "=", taskActorUserId)
);
if (proposalSecond == null) {
if (!proposalSecondedService.updateSecondRecord(proposalId, taskActorUserId, opinion, submitType)) {
return Result.error("未找到附议记录");
}
proposalSecond.setIsAgree(ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.AGREE.getCode()));
proposalSecond.setOpinion(opinion);
proposalSecond.setIsAgree(submitType == 1);
proposalSecond.setSecondedTime(DateUtil.date());
dao.update(proposalSecond);
// 仅更新本次附议对应的 wf_process_task不推动实例也不创建后续任务
View_user user = dao.fetch(View_user.class, Cnd.where(View_user::getId, "=", taskActorUserId));
@@ -269,7 +259,9 @@ public class ProposalSecondedController {
return Result.error("参数错误");
}
flowCommonService.executeTask(args);
proposalSecondedService.updateSecondRecord(proposalId, taskActorUserId, opinion, submitType);
if (!proposalSecondedService.updateSecondRecord(proposalId, taskActorUserId, opinion, submitType)) {
return Result.error("未找到附议记录");
}
return Result.success();
}
@@ -0,0 +1,43 @@
package com.budwk.app.zhgh.democratic.proposal.handler;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.flow.constant.FlowConst;
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 com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import org.nutz.json.Json;
import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* 提案院级党组织审核参与人
*/
public class ProposalPartyOrganizationAssignmentHandler implements AssignmentHandler {
@Override
public List<String> assign(TaskModel model, Execution execution) {
NutMap variable = Json.fromJson(NutMap.class, execution.getProcessInstance().getVariable());
ProposalInfo proposalInfo = variable.getAs(FlowConst.FORM_DATA, ProposalInfo.class);
List<String> userIds = ServiceContext.find(ProposalCommonService.class).getProposalUnionRoleUserIds(proposalInfo,
RoleConstant.UNIT_PARTY_SECRETARY.name());
if (userIds.isEmpty()) {
throw new BaseException("提案人所属分工会未设置党委书记,无法流转到院级党组织审核");
}
return userIds;
}
@Override
public String getMessage() {
return "提案人所属分工会党委书记";
}
@Override
public int getOrder() {
return 240;
}
}
@@ -0,0 +1,43 @@
package com.budwk.app.zhgh.democratic.proposal.handler;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.flow.constant.FlowConst;
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 com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import org.nutz.json.Json;
import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* 提案二级工会审核参与人
*/
public class ProposalSecondaryUnionAssignmentHandler implements AssignmentHandler {
@Override
public List<String> assign(TaskModel model, Execution execution) {
NutMap variable = Json.fromJson(NutMap.class, execution.getProcessInstance().getVariable());
ProposalInfo proposalInfo = variable.getAs(FlowConst.FORM_DATA, ProposalInfo.class);
List<String> userIds = ServiceContext.find(ProposalCommonService.class).getProposalUnionRoleUserIds(proposalInfo,
RoleConstant.BRANCH_UNION_CHAIRMAN.name(), RoleConstant.BRANCH_UNION_VICE_CHAIRMAN.name());
if (userIds.isEmpty()) {
throw new BaseException("提案人所属分工会未设置主席或副主席,无法流转到二级工会审核");
}
return userIds;
}
@Override
public String getMessage() {
return "提案人所属分工会主席或副主席";
}
@Override
public int getOrder() {
return 230;
}
}
@@ -18,7 +18,8 @@ import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* 提案委员会主任
* 提案委员会办理人
* 优先由届次内配置的提案委员会主任办理历史届次未配置主任时由副主任兜底办理
*/
public class ProposalWyhAssignmentHandler implements AssignmentHandler {
@Override
@@ -29,19 +30,26 @@ public class ProposalWyhAssignmentHandler implements AssignmentHandler {
String sessionId = info.getSessionId();
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
Sys_role role = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DIRECTOR);
Sys_role directorRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DIRECTOR);
Sys_user_role user_role = ServiceContext.find(Dao.class).fetch(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", role.getId()).and(Sys_user_role::getTcSessionId, "=", sessionId));
if (user_role == null) {
throw new RuntimeException("没有设置提案委员会主任,无法流转到下一步,请联系校工会进行设置。");
Sys_user_role userRole = ServiceContext.find(Dao.class).fetch(Sys_user_role.class,
Cnd.where(Sys_user_role::getRoleId, "=", directorRole.getId()).and(Sys_user_role::getTcSessionId, "=", sessionId));
if (userRole == null) {
Sys_role deputyDirectorRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DEPUTY_DIRECTOR);
// 历史届次可能仅配置副主任确保迁入提案可继续完成立案和承办单位确认
userRole = ServiceContext.find(Dao.class).fetch(Sys_user_role.class,
Cnd.where(Sys_user_role::getRoleId, "=", deputyDirectorRole.getId()).and(Sys_user_role::getTcSessionId, "=", sessionId));
}
return List.of(user_role.getUserId());
if (userRole == null) {
throw new RuntimeException("没有设置提案委员会主任或副主任,无法流转到下一步,请联系校工会进行设置。");
}
return List.of(userRole.getUserId());
}
@Override
public String getMessage() {
return "教代会提案委员会主任";
return "教代会提案委员会主任(未配置时由副主任办理)";
}
@Override
@@ -66,6 +66,23 @@ public interface ProposalExportService extends BaseService<ProposalInfo> {
*/
void exportProposalStatisticsAsDocx(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response);
/**
* 按综合导出专用提案模板导出单条提案
* 该模板保留提案人及审核环节的电子签名不影响其他页面使用的通用提案导出模板
*
* @param proposalId 提案ID
* @param response HTTP 响应
*/
void exportProposalAsDocx(String proposalId, HttpServletResponse response);
/**
* 按综合导出专用提案模板写入单条 DOCX用于 ZIP 与汇集文档导出
*
* @param proposalId 提案ID
* @param outputStream DOCX 输出流
*/
void exportProposalAsDocx(String proposalId, ByteArrayOutputStream outputStream);
/**
* 导出反馈表
*
@@ -0,0 +1,26 @@
package com.budwk.app.zhgh.democratic.proposal.service;
import java.util.List;
/**
* 提案业务短信服务
*
* 短信是否实际下发由统一的 SmsService 配置开关控制本服务仅负责还原提案业务的收件人和文案
*/
public interface ProposalMessageService {
/**
* 向本次新增的附议人发送邀请附议短信
*
* @param proposalId 提案ID
* @param seconderUserIds 本次新增附议人的用户ID列表
*/
void sendInviteSeconderMessage(String proposalId, List<String> seconderUserIds);
/**
* 向提案人所属分工会的主席副主席发送附议人数达标短信
*
* @param proposalId 提案ID
*/
void sendSecondaryUnionMessage(String proposalId);
}
@@ -13,5 +13,5 @@ public interface ProposalSecondedService extends BaseService<ProposalSecond> {
* 3. opinion附议意见写入附议记录的 opinion 字段
* 4. submitType附议结果1 表示同意20 表示不同意
*/
void updateSecondRecord(String proposalId, String taskActorUserId, String opinion, Integer submitType);
boolean updateSecondRecord(String proposalId, String taskActorUserId, String opinion, Integer submitType);
}
@@ -1,5 +1,6 @@
package com.budwk.app.zhgh.democratic.proposal.service.common;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.param.ExportTableColumns;
@@ -37,6 +38,25 @@ public interface ProposalCommonService extends BaseService<ProposalInfo> {
*/
boolean applySafeProposalListOrder(Cnd cnd, PageForm pageForm, String pageCode);
/**
* 查询提案流程审核页面的数据确保页面任务范围参与人范围和排序规则保持一致
*
* @param pageCode 审核页面代码
* @param pageForm 页面筛选条件
* @param approval 是否查询已审核记录
* @return 审核任务分页数据
*/
Pagination getWorkflowAuditPage(String pageCode, ProposalSearchParam pageForm, boolean approval);
/**
* 根据提案发起人所属分工会查询指定角色的有效用户
*
* @param proposalInfo 提案信息
* @param roleCodes 参与人角色
* @return 可参与流程的用户 ID
*/
List<String> getProposalUnionRoleUserIds(ProposalInfo proposalInfo, String... roleCodes);
/**
* 按白名单字段对已汇总的统计结果进行后端排序
*
@@ -13,6 +13,7 @@ import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.ExportTableColumns;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
@@ -34,6 +35,7 @@ import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysDictService;
import com.budwk.app.sys.services.SysRoleService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.web.commons.base.Globals;
@@ -147,6 +149,114 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
return applySafePageOrder(cnd, pageForm, PROPOSAL_LIST_ORDER_COLUMNS.get(pageCode));
}
/**
* 查询二级工会院级党组织审核页面的任务数据页面代码由控制器固定传入
* 防止请求参数篡改任务节点范围
*/
@Override
public Pagination getWorkflowAuditPage(String pageCode, ProposalSearchParam pageForm, boolean approval) {
if (!Set.of("secondaryUnionAudit", "partyOrganizationAudit").contains(pageCode)) {
throw new BaseException("不支持的提案审核页面");
}
Sql sql = Sqls.create("""
SELECT
info.*,
type.name AS typeName,
tcs.fullName AS sessionName,
tcd.`name` AS delegationName,
ins.id AS instanceId,
ins.businessNo,
ins.state AS instanceState,
ins.variable AS instanceVariable,
ins.processDefineId AS instanceProcessDefineId,
t.id AS taskId,
t.taskName AS taskKey,
t.displayName AS taskName,
t.taskType,
t.performType AS taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable AS taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName), '结束') AS curTaskName,
IF(rt.id IS NOT NULL, 1, 0) AS canRevoke
FROM wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_type type ON type.id = info.typeId
LEFT JOIN teacher_congress_session tcs ON tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd ON tcd.id = info.delegationId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", getWorkflowTaskName(pageCode));
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("ta.actorId", "=", SecurityUtil.getUserId());
}
if (approval) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
if (!applySafeProposalListOrder(cnd, pageForm, pageCode)) {
cnd.desc("t.createdAt");
}
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
/**
* 提案未保存分工会字段流程分派时以提案人当前所属分工会为准
* 并过滤已删除已禁用或不存在的人员避免把无效角色关系写入流程任务
*/
@Override
public List<String> getProposalUnionRoleUserIds(ProposalInfo proposalInfo, String... roleCodes) {
if (proposalInfo == null || StrUtil.isBlank(proposalInfo.getCreateUserId())) {
throw new BaseException("提案缺少提案人,无法确定审核人员");
}
if (roleCodes == null || roleCodes.length == 0) {
throw new BaseException("未配置审核人员角色");
}
View_user proposalUser = dao().fetch(View_user.class,
Cnd.where(View_user::getId, "=", proposalInfo.getCreateUserId()));
if (proposalUser == null || StrUtil.isBlank(proposalUser.getUnionId())) {
throw new BaseException("提案人未设置所属分工会,无法确定审核人员");
}
List<String> roleIds = Arrays.stream(roleCodes)
.filter(StrUtil::isNotBlank)
.map(sysRoleService::getByCode)
.filter(Objects::nonNull)
.map(Sys_role::getId)
.toList();
if (roleIds.isEmpty()) {
throw new BaseException("审核人员角色不存在,请联系校工会进行设置");
}
List<Sys_user_role> userRoles = dao().query(Sys_user_role.class,
Cnd.where(Sys_user_role::getRoleId, "in", roleIds)
.and(Sys_user_role::getUnionId, "=", proposalUser.getUnionId()));
return userRoles.stream()
.map(Sys_user_role::getUserId)
.filter(StrUtil::isNotBlank)
.distinct()
.filter(this::isEnabledUser)
.toList();
}
/** 校验流程参与人仍是可用系统用户。 */
private boolean isEnabledUser(String userId) {
Sys_user user = dao().fetch(Sys_user.class, userId);
return user != null && !Boolean.TRUE.equals(user.getDelFlag()) && !user.isDisabled();
}
/**
* 创建各列表页独立排序白名单显示字段与真实SQL字段或固定查询别名一一对应
*
@@ -163,6 +273,8 @@ 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("secondaryUnionAudit", standardOrderColumns());
pages.put("partyOrganizationAudit", standardOrderColumns());
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"));
@@ -252,7 +364,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", "secondaryUnionAudit", "partyOrganizationAudit", "committeeFiling", "committeeFilingUnit",
"schoolLeaderApproval", "caseCheck").contains(pageCode);
String taskName = getWorkflowTaskName(pageCode);
@@ -341,6 +453,8 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
Map<String, String> taskNames = Map.of(
"delegation", "delegation",
"preAudit", "preAudit",
"secondaryUnionAudit", "secondaryUnionAudit",
"partyOrganizationAudit", "partyOrganizationAudit",
"committeeFiling", "committee",
"committeeFilingUnit", "committeeFilingUnit",
"schoolLeaderApproval", "schoolLeader",
@@ -414,6 +528,8 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> imp
Map<String, Set<String>> columns = new HashMap<>();
columns.put("delegation", commonColumns);
columns.put("preAudit", commonColumns);
columns.put("secondaryUnionAudit", commonColumns);
columns.put("partyOrganizationAudit", commonColumns);
columns.put("caseCheck", Set.of("code", "caseFilingCode", "name", "createUserName", "typeName",
"sessionName", "delegationName", "curTaskName", "instanceState", "finishTime"));
columns.put("committeeFiling", Set.of("code", "caseFilingCode", "name", "typeName", "sessionName",
@@ -5,6 +5,8 @@ import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import com.budwk.app.base.exception.BaseException;
@@ -580,7 +582,7 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
ZipOutputStream zipOutputStream = new ZipOutputStream(bos);
for (NutMap proposalRow : list) {
try (ByteArrayOutputStream docxByteArrayOutputStream = new ByteArrayOutputStream()) {
proposalCommonService.exportProposalAsDocx(proposalRow.getString("id"), docxByteArrayOutputStream);
exportProposalAsDocx(proposalRow.getString("id"), docxByteArrayOutputStream);
ZipEntry zipEntry = new ZipEntry(proposalRow.getString("code") + "-" + sanitizeZipFileName(proposalRow.getString("name")) + ".docx");
zipOutputStream.putNextEntry(zipEntry);
docxByteArrayOutputStream.writeTo(zipOutputStream);
@@ -611,7 +613,7 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
for (NutMap proposalRow : list) {
try (ByteArrayOutputStream oneDocOut = new ByteArrayOutputStream()) {
proposalCommonService.exportProposalAsDocx(proposalRow.getString("id"), oneDocOut);
exportProposalAsDocx(proposalRow.getString("id"), oneDocOut);
if (mergedDoc == null) {
mergedDoc = new XWPFDocument(new ByteArrayInputStream(oneDocOut.toByteArray()));
} else {
@@ -642,6 +644,182 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
}
}
@Override
public void exportProposalAsDocx(String proposalId, HttpServletResponse response) {
ProposalInfo proposalInfo = fetch(proposalId);
if (proposalInfo == null) {
throw new BaseException("提案信息不存在");
}
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
exportProposalAsDocx(proposalId, outputStream);
CommonDownloadUtil.download(proposalInfo.getCode() + "-" + proposalInfo.getName() + ".docx",
outputStream.toByteArray(), response);
} catch (IOException e) {
log.error("综合导出单条提案失败,proposalId={}", proposalId, e);
throw new BaseException("导出失败");
}
}
@Override
public void exportProposalAsDocx(String proposalId, ByteArrayOutputStream outputStream) {
NutMap proposal = queryProposalTemplateData(proposalId);
if (proposal == null) {
throw new BaseException("提案信息不存在");
}
proposal.put("sign", sysOfficeTemplateUtil.createPictureRenderData(proposal.getString("signature")));
proposal.put("secondedUserName", queryAgreedSecondedUserNames(proposalId));
proposal.put("brief", sysOfficeTemplateUtil.convertRichTextToDocText(proposal.getString("brief")));
proposal.put("measures", sysOfficeTemplateUtil.convertRichTextToDocText(proposal.getString("measures")));
HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();
htmlRenderPolicy.getConfig().setShowDefaultTableBorderInTableCell(true);
Configure config = Configure.builder()
.bind("proposal.brief", htmlRenderPolicy)
.bind("proposal.measures", htmlRenderPolicy)
.bind("opinion", htmlRenderPolicy)
.build();
Map<String, Object> renderData = new HashMap<>();
renderData.put("schoolName", Globals.AppName);
renderData.put("proposal", proposal);
renderData.put("secondaryUnitAuditList", queryLatestProposalAudit(proposalId, "secondaryUnionAudit", proposal));
renderData.put("partyOrganizationAuditList", queryLatestProposalAudit(proposalId, "partyOrganizationAudit", proposal));
renderData.put("caseUnitRecordList", queryLatestProposalAudit(proposalId, "committee", proposal));
try {
XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("proposal"), config)
.render(renderData)
.writeAndClose(outputStream);
} catch (IOException e) {
log.error("综合导出提案模板渲染失败,proposalId={}", proposalId, e);
throw new BaseException("导出失败");
}
}
/**
* 查询模板使用的提案基础信息字段名称与 SWUFE 提案模板保持一致
* 使模板替换后无需改动业务代码中的占位符命名
*/
private NutMap queryProposalTemplateData(String proposalId) {
Sql sql = Sqls.create("""
SELECT
info.code AS proposalCode,
info.caseFilingCode,
info.name AS proposalName,
info.createUserName AS username,
info.unitName,
COALESCE(NULLIF(info.mobile, ''), user.mobile) AS mobile,
user.email,
info.signature,
info.caseFilingResult,
info.brief,
info.measures,
DATE_FORMAT(info.createTime, '%Y年%m月%d日') AS createTime
FROM proposal_info info
LEFT JOIN sys_user user ON user.id = info.createUserId
WHERE info.id = @proposalId
""");
sql.setParam("proposalId", proposalId);
sql.setCallback(Sqls.callback.map());
execute(sql);
return (NutMap) sql.getResult();
}
/** 查询已同意的附议人姓名,按附议完成时间保持稳定排序。 */
private String queryAgreedSecondedUserNames(String proposalId) {
Sql sql = Sqls.create("""
SELECT GROUP_CONCAT(userName ORDER BY secondedTime SEPARATOR '、') AS secondedUserName
FROM proposal_second
WHERE proposalId = @proposalId AND isAgree = 1
""");
sql.setParam("proposalId", proposalId);
sql.setCallback(Sqls.callback.str());
execute(sql);
return StrUtil.blankToDefault(sql.getString(), "");
}
/**
* 读取指定审核节点最后一次已办记录并从任务变量中获取办理人意见和签字快照
* 历史退回后重新审核时模板只展示最终有效的一次审核结果避免重复打印旧意见
*/
private List<NutMap> queryLatestProposalAudit(String proposalId, String taskName, NutMap proposal) {
Sql sql = Sqls.create("""
SELECT task.variable, task.finishTime
FROM wf_process_task task
INNER JOIN wf_process_instance instance ON instance.id = task.processInstanceId
WHERE instance.businessNo = @proposalId
AND task.taskName = @taskName
AND task.taskState = @finishedState
ORDER BY task.finishTime DESC, task.id DESC
LIMIT 1
""");
sql.setParam("proposalId", proposalId);
sql.setParam("taskName", taskName);
sql.setParam("finishedState", ProcessTaskStateEnum.FINISHED.getCode());
sql.setCallback(Sqls.callback.map());
execute(sql);
NutMap task = (NutMap) sql.getResult();
if (task == null) {
return List.of();
}
Dict variable = parseTaskVariable(task.getString("variable"));
NutMap audit = new NutMap();
audit.put("username", getTaskVariableString(variable, "userName"));
audit.put("opinion", getTaskVariableString(variable, "opinion"));
audit.put("auditTime", task.getTime("finishTime") == null ? "" : DateUtil.formatDate(task.getTime("finishTime")));
audit.put("sign", sysOfficeTemplateUtil.createPictureRenderData(getTaskVariableString(variable, "signature")));
if ("committee".equals(taskName)) {
audit.put("resultAudit", buildCommitteeResultAudit(variable, proposal));
}
return List.of(audit);
}
/** 兼容当前流程变量与已迁移 V3 历史任务变量的字段前缀。 */
private String getTaskVariableString(Dict variable, String fieldName) {
String taskFormValue = variable.getStr(FlowConst.TASK_FORM_DATA_PREFIX + fieldName);
if (StrUtil.isNotBlank(taskFormValue)) {
return taskFormValue;
}
return StrUtil.blankToDefault(variable.getStr("f_" + fieldName), "");
}
/** 流程变量损坏或为空时按空字典处理,确保单条导出不会因历史脏数据中断。 */
private Dict parseTaskVariable(String variableJson) {
if (StrUtil.isBlank(variableJson)) {
return Dict.create();
}
try {
return Json.fromJson(Dict.class, variableJson);
} catch (Exception e) {
log.warn("提案审核任务变量解析失败,将按空变量导出");
return Dict.create();
}
}
/** 构建提案委员会立案结论,沿用 V3 模板中“结论 + 审核意见”的展示方式。 */
private String buildCommitteeResultAudit(Dict variable, NutMap proposal) {
String caseFilingResult = StrUtil.blankToDefault(getTaskVariableString(variable, "caseFilingResult"),
proposal.getString("caseFilingResult"));
if ("CONFIRM_FILING".equals(caseFilingResult)) {
String masterUnitName = getTaskVariableString(variable, "masterUnitNameStr");
String slaveUnitName = getTaskVariableString(variable, "slaveUnitNameStr");
String result = "经提案工作委员会研究,符合提案立案条件,予以立案";
if (StrUtil.isNotBlank(masterUnitName)) {
result += ",建议由" + masterUnitName + "办理";
}
if (StrUtil.isNotBlank(slaveUnitName)) {
result += "" + slaveUnitName + "协办";
}
return result + "";
}
if ("SUGGESTION".equals(caseFilingResult)) {
return "作为意见建议转送参考。";
}
if ("NOT".equals(caseFilingResult)) {
return "该提案不予立案。";
}
return "";
}
private List<NutMap> queryProposalBaseList(ProposalQueryComprehensiveParam pageForm) {
Sql sql = Sqls.create("""
SELECT
@@ -0,0 +1,116 @@
package com.budwk.app.zhgh.democratic.proposal.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalMessageService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.List;
/**
* 提案业务短信实现
*
* 发送前不重复判断短信平台开关统一交由 SmsService 按单点登录系统参数和平台开关处理
* 避免提案模块和公共短信模块出现两套配置规则
*/
@IocBean(args = {"refer:dao"})
@Slf4j
public class ProposalMessageServiceImpl implements ProposalMessageService {
private static final String INVITE_SECONDER_TEMPLATE = "{}代表,您好,{}代表的提案《{}》,邀请您作为附议人,请您登陆学校智慧工会系统进行附议。";
private static final String SECONDARY_UNION_TEMPLATE = "{}您好,{}代表的提案《{}》,已经完成附议,请您登陆学校智慧工会系统进行审核。";
private final Dao dao;
@Inject
private SmsService smsService;
@Inject
private ProposalCommonService proposalCommonService;
public ProposalMessageServiceImpl(Dao dao) {
this.dao = dao;
}
@Override
public void sendInviteSeconderMessage(String proposalId, List<String> seconderUserIds) {
ProposalInfo proposalInfo = getProposal(proposalId);
if (proposalInfo == null || seconderUserIds == null || seconderUserIds.isEmpty()) {
return;
}
Sys_user proposalCreator = dao.fetch(Sys_user.class, proposalInfo.getCreateUserId());
if (proposalCreator == null) {
log.warn("提案邀请附议短信未发送,未找到提案人,proposalId={}", proposalId);
return;
}
List<Sys_user> seconders = dao.query(Sys_user.class,
Cnd.where(Sys_user::getId, "in", seconderUserIds));
for (Sys_user seconder : seconders) {
String content = StrUtil.format(INVITE_SECONDER_TEMPLATE, seconder.getUsername(),
proposalCreator.getUsername(), proposalInfo.getName());
sendSms(seconder, content, proposalId, "邀请附议人");
}
}
@Override
public void sendSecondaryUnionMessage(String proposalId) {
ProposalInfo proposalInfo = getProposal(proposalId);
if (proposalInfo == null) {
return;
}
Sys_user proposalCreator = dao.fetch(Sys_user.class, proposalInfo.getCreateUserId());
if (proposalCreator == null) {
log.warn("提案附议达标短信未发送,未找到提案人,proposalId={}", proposalId);
return;
}
try {
List<String> reviewerIds = proposalCommonService.getProposalUnionRoleUserIds(proposalInfo,
RoleConstant.BRANCH_UNION_CHAIRMAN.name(), RoleConstant.BRANCH_UNION_VICE_CHAIRMAN.name());
if (reviewerIds.isEmpty()) {
log.warn("提案附议达标短信未发送,提案人所属分工会未设置主席或副主席,proposalId={}", proposalId);
return;
}
List<Sys_user> reviewers = dao.query(Sys_user.class, Cnd.where(Sys_user::getId, "in", reviewerIds));
for (Sys_user reviewer : reviewers) {
String content = StrUtil.format(SECONDARY_UNION_TEMPLATE, reviewer.getUsername(),
proposalCreator.getUsername(), proposalInfo.getName());
sendSms(reviewer, content, proposalId, "附议人数达标");
}
} catch (RuntimeException e) {
// 短信通知失败不能影响已完成的附议业务和流程流转
log.error("提案附议达标短信发送失败,proposalId={}", proposalId, e);
}
}
/**
* 发送单个提案业务短信
*
* SmsService 会按运行配置决定是否真实下发没有工号的人员不能被短信平台识别因此只记录并跳过
*/
private void sendSms(Sys_user user, String content, String proposalId, String scene) {
if (StrUtil.isBlank(user.getLoginname())) {
log.warn("提案{}短信未发送,收件人未配置工号,proposalId={}userId={}", scene, proposalId, user.getId());
return;
}
try {
smsService.sendSmMsg(user.getLoginname(), content);
} catch (RuntimeException e) {
// 短信平台异常不能影响提案业务提交异常留在日志中供后续排查
log.error("提案{}短信发送失败,proposalId={}userId={}", scene, proposalId, user.getId(), e);
}
}
private ProposalInfo getProposal(String proposalId) {
if (StrUtil.isBlank(proposalId)) {
return null;
}
return dao.fetch(ProposalInfo.class, proposalId);
}
}
@@ -4,34 +4,67 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalSecond;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalMessageService;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalSecondedService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
@IocBean(args = {"refer:dao"})
public class ProposalSecondedServiceImpl extends BaseServiceImpl<ProposalSecond> implements ProposalSecondedService {
@Inject
private ProposalMessageService proposalMessageService;
public ProposalSecondedServiceImpl(Dao dao) {
super(dao);
}
@Override
public void updateSecondRecord(String proposalId, String taskActorUserId, String opinion, Integer submitType) {
public boolean updateSecondRecord(String proposalId, String taskActorUserId, String opinion, Integer submitType) {
if (Strings.isBlank(proposalId) || Strings.isBlank(taskActorUserId) || submitType == null) {
return;
return false;
}
// 仅更新当前提案当前附议人的附议结果不处理流程任务表
ProposalSecond proposalSecond = this.fetch(Cnd.where(ProposalSecond::getProposalId, "=", proposalId)
.and(ProposalSecond::getSeconderId, "=", taskActorUserId));
if (proposalSecond == null) {
return;
return false;
}
proposalSecond.setIsAgree(ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.AGREE.getCode()));
boolean wasAgree = Boolean.TRUE.equals(proposalSecond.getIsAgree());
boolean isAgree = ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.AGREE.getCode());
int agreedCountBefore = 0;
if (isAgree && !wasAgree) {
agreedCountBefore = this.count(Cnd.where(ProposalSecond::getProposalId, "=", proposalId)
.and(ProposalSecond::getIsAgree, "=", true));
}
proposalSecond.setIsAgree(isAgree);
proposalSecond.setOpinion(opinion);
proposalSecond.setSecondedTime(DateUtil.date());
this.update(proposalSecond);
notifySecondaryUnionWhenThresholdReached(proposalId, isAgree, wasAgree, agreedCountBefore);
return true;
}
/**
* 仅在同意附议人数首次达到配置数量时发送二级工会审核短信防止重复提交附议结果导致重复通知
*/
private void notifySecondaryUnionWhenThresholdReached(String proposalId, boolean isAgree, boolean wasAgree,
int agreedCountBefore) {
if (!isAgree || wasAgree) {
return;
}
ProposalConfig proposalConfig = this.dao().fetch(ProposalConfig.class, Cnd.NEW());
if (proposalConfig == null || proposalConfig.getSeconderNum() == null || proposalConfig.getSeconderNum() <= 0) {
return;
}
if (agreedCountBefore < proposalConfig.getSeconderNum()
&& agreedCountBefore + 1 >= proposalConfig.getSeconderNum()) {
proposalMessageService.sendSecondaryUnionMessage(proposalId);
}
}
}
@@ -0,0 +1,38 @@
package com.budwk.app.zhgh.huazhu.constant;
public class HuaZhuConstant {
/**
* 华住接口请求超时单位毫秒
*/
public static final int TIMEOUT = 20000;
public static final String BASEURL = "https://htravelserver.huazhu.com";
public static final String OAUTH2_URL = "https://oauth2-api.huazhu.com";
/**
* 公司卡号
*/
public static final String CARD_ID = "VCENTCRM1169099135";
/**
* 公司密钥
*/
public static final String CARD_PASSWORD = "47CD8C9C-A4CD-4CBD-A30F-41C41BDF7E99";
/**
* AES密钥
*/
public static final String AES_KEY = "JAE4YZJLXZW5HGBU";
/**
* 客户端ID
*/
public static final String CLIENT_ID = "5e8651d8-a5a8-4358-9986-91f426e191c8";
/**
* 客户端密钥
*/
public static final String CLIENT_SECRET = "2d20a7f3125b52b8e90936c3b30fb4b62b443a52716a090e55e302cf40f32b66d592ef1e98c1cf0a8e5ec3f9c444a9b4f53a337b1be57263d4fbb71044f67f12";
}
@@ -0,0 +1,64 @@
package com.budwk.app.zhgh.huazhu.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.huazhu.param.HuaZhuEmployeePageForm;
import com.budwk.app.zhgh.huazhu.service.HuaZhuEmployeeService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.validation.Valid;
import java.util.Arrays;
import java.util.Collections;
/**
* PC 端华住员工同步管理
*/
@IocBean
@Ok("json:full")
@At("/platform/huazhu")
public class HuaZhuManageController {
@Inject
private HuaZhuEmployeeService huaZhuEmployeeService;
@At("")
@Ok("beetl:/platform/zhgh/huazhu/index.html")
@SaCheckPermission("huazhu.manage")
public void index() {
}
@At
@SaCheckPermission("huazhu.manage")
public Result pageData(@Valid HuaZhuEmployeePageForm pageForm) {
return Result.success(huaZhuEmployeeService.pageUsers(pageForm));
}
@At
@SaCheckPermission("huazhu.manage")
public Result syncUsers(@Param("userIds") String[] userIds) {
if (userIds == null || userIds.length == 0) {
return Result.error("请先选择需要同步的员工");
}
return Result.success(huaZhuEmployeeService.syncUsers(Arrays.asList(userIds)));
}
@At
@SaCheckPermission("huazhu.manage")
public Result queryStatus(@Param("userId") String userId) {
if (StrUtil.isBlank(userId)) {
return Result.error("用户不能为空");
}
try {
return Result.success(huaZhuEmployeeService.queryStatus(userId));
} catch (IllegalArgumentException | IllegalStateException e) {
return Result.error(e.getMessage());
} catch (Exception e) {
return Result.error("查询华住状态失败,请稍后重试");
}
}
}
@@ -0,0 +1,37 @@
package com.budwk.app.zhgh.huazhu.h5Controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.huazhu.service.HuaZhuLoginService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* H5 幸福工荟中的华住免密登录入口
*/
@IocBean
@At("/platform/h5/huazhu")
public class H5HuaZhuController {
@Inject
private HuaZhuLoginService huaZhuLoginService;
/**
* 校验幸福工荟访问权限后跳转至华住企业登录页面
*
* @param response HTTP 响应用于返回 302 跳转
* @throws IOException 响应输出异常
*/
@At("/basicLogin")
@Ok("void")
@SaCheckPermission("h5.inclusive.benefit")
public void basicLogin(HttpServletResponse response) throws IOException {
response.setHeader("Cache-Control", "no-store");
response.sendRedirect(huaZhuLoginService.buildBasicLoginUrl(SecurityUtil.getUserLoginname()));
}
}
@@ -0,0 +1,14 @@
package com.budwk.app.zhgh.huazhu.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 华住员工管理分页参数
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class HuaZhuEmployeePageForm extends PageForm {
private String searchKeyword;
}
@@ -0,0 +1,39 @@
package com.budwk.app.zhgh.huazhu.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.zhgh.huazhu.param.HuaZhuEmployeePageForm;
import com.budwk.app.zhgh.huazhu.vo.HuaZhuEmployeeStatus;
import com.budwk.app.zhgh.huazhu.vo.HuaZhuSyncResult;
import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* 华住员工同步与状态查询服务
*/
public interface HuaZhuEmployeeService {
/**
* 分页查询可同步的本项目员工
*
* @param pageForm 查询参数
* @return 员工分页数据
*/
Pagination<NutMap> pageUsers(HuaZhuEmployeePageForm pageForm);
/**
* 将选中的本项目员工同步到华住
*
* @param userIds 本项目用户主键列表
* @return 每位员工的同步结果
*/
List<HuaZhuSyncResult> syncUsers(List<String> userIds);
/**
* 查询指定本项目员工在华住的实时状态
*
* @param userId 本项目用户主键
* @return 华住员工状态
*/
HuaZhuEmployeeStatus queryStatus(String userId);
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.huazhu.service;
/**
* 华住免密登录服务
*/
public interface HuaZhuLoginService {
/**
* 按华住协议为当前职工工号生成免密登录跳转地址
*
* @param loginName 当前登录职工的工号
* @return 华住免密登录地址
*/
String buildBasicLoginUrl(String loginName);
}
@@ -0,0 +1,232 @@
package com.budwk.app.zhgh.huazhu.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.huazhu.constant.HuaZhuConstant;
import com.budwk.app.zhgh.huazhu.param.HuaZhuEmployeePageForm;
import com.budwk.app.zhgh.huazhu.service.HuaZhuEmployeeService;
import com.budwk.app.zhgh.huazhu.vo.HuaZhuEmployeeStatus;
import com.budwk.app.zhgh.huazhu.vo.HuaZhuSyncResult;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.integration.jedis.RedisService;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
/**
* 华住员工同步与状态查询实现
*/
@IocBean(args = {"refer:dao"})
public class HuaZhuEmployeeServiceImpl implements HuaZhuEmployeeService {
private static final String TOKEN_CACHE_KEY = "zhgh:huazhu:oauth-token";
private final Dao dao;
@Inject
private RedisService redisService;
public HuaZhuEmployeeServiceImpl(Dao dao) {
this.dao = dao;
}
@Override
public Pagination<NutMap> pageUsers(HuaZhuEmployeePageForm pageForm) {
String keyword = StrUtil.trim(pageForm.getSearchKeyword());
String condition = " WHERE u.delFlag = 0 ";
if (StrUtil.isNotBlank(keyword)) {
condition += " AND (u.loginname LIKE @keyword OR u.username LIKE @keyword) ";
}
Sql countSql = Sqls.create("SELECT COUNT(1) FROM sys_user u" + condition);
Sql listSql = Sqls.create("SELECT u.id, u.loginname, u.username, unit.name AS unitName "
+ "FROM sys_user u LEFT JOIN sys_unit unit ON unit.id = u.unitId " + condition
+ "ORDER BY u.createdAt DESC");
if (StrUtil.isNotBlank(keyword)) {
String likeKeyword = "%" + keyword + "%";
countSql.setParam("keyword", likeKeyword);
listSql.setParam("keyword", likeKeyword);
}
countSql.setCallback(Sqls.callback.integer());
dao.execute(countSql);
int totalCount = countSql.getInt();
listSql.setPager(dao.createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
listSql.setCallback(Sqls.callback.maps());
dao.execute(listSql);
return new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), totalCount, listSql.getList(NutMap.class));
}
@Override
public List<HuaZhuSyncResult> syncUsers(List<String> userIds) {
if (userIds == null || userIds.isEmpty()) {
return Collections.emptyList();
}
List<HuaZhuSyncResult> results = new ArrayList<>();
for (String userId : userIds) {
Sys_user user = findAvailableUser(userId);
HuaZhuSyncResult result = new HuaZhuSyncResult()
.setUserId(user.getId())
.setLoginName(user.getLoginname())
.setUserName(user.getUsername());
try {
request("/userInfo/addUser", buildEmployeeRequest(user.getLoginname()));
result.setSuccess(true).setMessage("同步成功");
} catch (Exception e) {
result.setSuccess(false).setMessage("同步失败:" + safeMessage(e));
}
results.add(result);
}
return results;
}
@Override
public HuaZhuEmployeeStatus queryStatus(String userId) {
Sys_user user = findAvailableUser(userId);
String body = request("/userInfo/queryUser", buildEmployeeRequest(user.getLoginname()));
JSONObject payload = extractQueryPayload(body);
try {
return JSONUtil.toBean(payload, HuaZhuEmployeeStatus.class);
} catch (Exception e) {
throw new IllegalStateException("华住查询接口返回格式无法识别", e);
}
}
/**
* 兼容华住接口直接返回员工对象以及以 data result 包装员工对象的两种响应格式
* 响应中不包含员工状态时只返回经筛选后的业务提示避免透出第三方完整响应
*
* @param body 华住查询接口响应正文
* @return 可转换为员工状态的 JSON 对象
*/
private JSONObject extractQueryPayload(String body) {
JSONObject response;
try {
response = JSONUtil.parseObj(body);
} catch (Exception e) {
throw new IllegalStateException("华住查询接口未返回有效数据", e);
}
JSONObject payload = extractObject(response, "data");
if (payload == null) {
payload = extractObject(response, "result");
}
if (payload != null) {
return payload;
}
if (response.containsKey("statusCode") || response.containsKey("userNumber") || response.containsKey("userName")) {
return response;
}
String message = StrUtil.blankToDefault(response.getStr("message"), response.getStr("msg"));
throw new IllegalStateException(StrUtil.isBlank(message) ? "华住未返回该员工状态" : "华住查询失败:" + message);
}
/**
* 从包装响应中提取对象部分网关会将 data/result 再序列化为 JSON 字符串
*
* @param response 华住响应对象
* @param key 包装字段名
* @return 包装内的 JSON 对象缺失或格式不匹配时返回 null
*/
private JSONObject extractObject(JSONObject response, String key) {
Object value = response.get(key);
if (value instanceof JSONObject) {
return (JSONObject) value;
}
if (value instanceof CharSequence && StrUtil.isNotBlank(value.toString())) {
try {
return JSONUtil.parseObj(value.toString());
} catch (Exception ignored) {
return null;
}
}
return null;
}
/**
* 调用华住员工接口并统一处理 OAuth Token 与非成功状态
*
* @param path 华住接口路径
* @param requestBody 请求体
* @return 华住响应正文
*/
private String request(String path, NutMap requestBody) {
HttpResponse response = HttpRequest.post(trimTrailingSlash(HuaZhuConstant.BASEURL) + path)
.header("Authorization", getAccessToken())
.header("accept", "application/json")
.contentType("application/json; charset=UTF-8")
.body(JSONUtil.toJsonStr(requestBody))
.timeout(HuaZhuConstant.TIMEOUT)
.execute();
if (!response.isOk()) {
throw new IllegalStateException("华住接口调用失败,状态码:" + response.getStatus());
}
return response.body();
}
/**
* 获取华住 OAuth Token优先使用 Redis 缓存以避免频繁请求第三方认证接口
*
* @return HTTP Authorization 头的 Bearer Token
*/
private String getAccessToken() {
String cachedToken = redisService.get(TOKEN_CACHE_KEY);
if (StrUtil.isNotBlank(cachedToken)) {
return cachedToken;
}
String credential = Base64.getEncoder().encodeToString((HuaZhuConstant.CLIENT_ID + ":" + HuaZhuConstant.CLIENT_SECRET).getBytes(StandardCharsets.UTF_8));
HttpResponse response = HttpRequest.post(trimTrailingSlash(HuaZhuConstant.OAUTH2_URL)
+ "/oauth/token?scope=ALL&grant_type=client_credentials")
.header("Authorization", "Basic " + credential)
.timeout(HuaZhuConstant.TIMEOUT)
.execute();
if (!response.isOk()) {
throw new IllegalStateException("获取华住认证 Token 失败,状态码:" + response.getStatus());
}
JSONObject tokenData = JSONUtil.parseObj(response.body());
String accessToken = tokenData.getStr("access_token");
String tokenType = StrUtil.blankToDefault(tokenData.getStr("token_type"), "Bearer");
if (StrUtil.isBlank(accessToken)) {
throw new IllegalStateException("华住认证接口未返回 Token");
}
String authorization = tokenType + " " + accessToken;
redisService.setex(TOKEN_CACHE_KEY, 60 * 58, authorization);
return authorization;
}
private NutMap buildEmployeeRequest(String loginName) {
return NutMap.NEW().addv("cardId", HuaZhuConstant.CARD_ID)
.addv("cardPassWord", HuaZhuConstant.CARD_PASSWORD)
.addv("userNumber", loginName);
}
private Sys_user findAvailableUser(String userId) {
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", userId).and("delFlag", "=", false));
if (user == null) {
throw new IllegalArgumentException("用户不存在或已删除");
}
if (StrUtil.isBlank(user.getLoginname())) {
throw new IllegalArgumentException("用户缺少工号,无法同步华住");
}
return user;
}
private String trimTrailingSlash(String value) {
return value.endsWith("/") ? value.substring(0, value.length() - 1) : value;
}
private String safeMessage(Exception e) {
return StrUtil.isBlank(e.getMessage()) ? "请检查华住配置和网络连接" : e.getMessage();
}
}
@@ -0,0 +1,68 @@
package com.budwk.app.zhgh.huazhu.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.huazhu.constant.HuaZhuConstant;
import com.budwk.app.zhgh.huazhu.service.HuaZhuLoginService;
import org.nutz.ioc.loader.annotation.IocBean;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* 华住免密登录服务实现
*/
@IocBean
public class HuaZhuLoginServiceImpl implements HuaZhuLoginService {
private static final String TRAVEL_TYPE = "YS";
@Override
public String buildBasicLoginUrl(String loginName) {
if (StrUtil.isBlank(loginName)) {
throw new IllegalArgumentException("当前登录用户缺少工号,无法进入华住");
}
// 华住约定使用 AES/ECB/PKCS5Padding 对公司卡号工号和当前时间生成签名
String authorizeSign = encrypt(HuaZhuConstant.CARD_ID + "&" + loginName + "&" + DateUtil.now(), HuaZhuConstant.AES_KEY);
return trimTrailingSlash(HuaZhuConstant.BASEURL) + "/authentication/basicLogin?cardId=" + encode(HuaZhuConstant.CARD_ID)
+ "&travelType=" + TRAVEL_TYPE
+ "&userNumber=" + encode(loginName)
+ "&invoiceNo="
+ "&authorizeSign=" + encode(authorizeSign);
}
/**
* 按华住协议生成 AES 签名不记录签名内容或密钥
*
* @param source 待签名内容
* @param aesKey 华住提供的 16 字节 AES 密钥
* @return Base64 编码后的签名
*/
private String encrypt(String source, String aesKey) {
try {
byte[] keyBytes = aesKey.getBytes(StandardCharsets.UTF_8);
if (keyBytes.length != 16) {
throw new IllegalStateException("华住 AES 密钥必须为 16 字节");
}
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"));
return Base64.getEncoder().encodeToString(cipher.doFinal(source.getBytes(StandardCharsets.UTF_8)));
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
throw new IllegalStateException("生成华住登录签名失败", e);
}
}
private String encode(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
private String trimTrailingSlash(String value) {
return value.endsWith("/") ? value.substring(0, value.length() - 1) : value;
}
}
@@ -0,0 +1,32 @@
package com.budwk.app.zhgh.huazhu.vo;
import lombok.Data;
import java.util.List;
/**
* 华住查询员工接口的展示结果
*/
@Data
public class HuaZhuEmployeeStatus {
private String statusCode;
private String userNumber;
private String userName;
private String userDep;
private String costCenter;
private String rank;
private String leader;
private String mobile;
private String email;
private List<Permission> permissions;
/**
* 华住返回的预订权限
*/
@Data
public static class Permission {
private Boolean hasPermission;
private String permissionCode;
private String permissionValue;
}
}
@@ -0,0 +1,17 @@
package com.budwk.app.zhgh.huazhu.vo;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 单个员工同步到华住的执行结果
*/
@Data
@Accessors(chain = true)
public class HuaZhuSyncResult {
private String userId;
private String loginName;
private String userName;
private Boolean success;
private String message;
}
@@ -0,0 +1,77 @@
package com.budwk.app.zhgh.inclusive.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.inclusive.model.InclusiveBenefit;
import com.budwk.app.zhgh.inclusive.param.InclusiveBenefitPageForm;
import com.budwk.app.zhgh.inclusive.service.InclusiveBenefitService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.validation.Valid;
/**
* PC 端南航专惠管理
*/
@IocBean
@Ok("json:full")
@At("/platform/inclusive")
public class InclusiveBenefitController {
@Inject
private InclusiveBenefitService inclusiveBenefitService;
@At("")
@Ok("beetl:/platform/zhgh/inclusive/index.html")
@SaCheckPermission("inclusive.benefit")
public void index() {
}
@At
@SaCheckPermission("inclusive.benefit")
public Result pageData(@Valid InclusiveBenefitPageForm pageForm) {
return Result.success(inclusiveBenefitService.pageData(pageForm));
}
@At
@SaCheckPermission("inclusive.benefit")
public Result findOne(@Param("id") String id) {
return Result.success(inclusiveBenefitService.findVO(id, false));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("inclusive.benefit")
public Result save(InclusiveBenefit benefit, @Param("status") Integer status) {
if (benefit == null || StrUtil.isBlank(benefit.getTitle())) {
return Result.error("标题不能为空");
}
if (StrUtil.isBlank(benefit.getTagType())) {
return Result.error("标签类型不能为空");
}
inclusiveBenefitService.saveBenefit(benefit, status);
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("inclusive.benefit")
public Result updateStatus(@Param("id") String id, @Param("status") Integer status) {
inclusiveBenefitService.updateStatus(id, status);
return Result.success();
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("inclusive.benefit")
public Result delete(@Param("id") String id) {
inclusiveBenefitService.deleteBenefit(id);
return Result.success();
}
}
@@ -0,0 +1,55 @@
package com.budwk.app.zhgh.inclusive.h5Controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.inclusive.param.InclusiveBenefitPageForm;
import com.budwk.app.zhgh.inclusive.service.InclusiveBenefitService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.validation.Valid;
/**
* H5 端南航专惠浏览
*/
@IocBean
@Ok("json:full")
@At("/platform/h5/inclusive")
public class H5InclusiveBenefitController {
@Inject
private InclusiveBenefitService inclusiveBenefitService;
@At("")
@Ok("beetl:/platform/zhghh5/inclusive/index.html")
@SaCheckPermission("h5.inclusive.benefit")
public void index() {
}
@At("/detail")
@Ok("beetl:/platform/zhghh5/inclusive/detail.html")
@SaCheckPermission("h5.inclusive.benefit")
public void detail() {
}
@At
@SaCheckPermission("h5.inclusive.benefit")
public Result pageData(@Valid InclusiveBenefitPageForm pageForm) {
return Result.success(inclusiveBenefitService.pageData(pageForm));
}
@At
@SaCheckPermission("h5.inclusive.benefit")
public Result findOne(@Param("id") String id) {
return Result.success(inclusiveBenefitService.findVO(id, true));
}
@At
@SaCheckPermission("h5.inclusive.benefit")
public Result tagOptions() {
return Result.success(inclusiveBenefitService.tagOptions());
}
}
@@ -0,0 +1,82 @@
package com.budwk.app.zhgh.inclusive.model;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.EL;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* 南航专惠内容
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("inclusive_benefit")
@Comment("南航专惠")
public class InclusiveBenefit extends BaseModel implements Serializable {
@Name
@Column
@Comment("主键")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(els = @EL("uuid()"))
private String id;
@Column
@Comment("年度")
@ColDefine(type = ColType.INT)
private Integer year;
@Column
@Comment("标题")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String title;
@Column
@Comment("摘要")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String summary;
@Column
@Comment("标签类型字典编码")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String tagType;
@Column
@Comment("富文本内容")
@ColDefine(type = ColType.TEXT)
private String content;
@Column
@Comment("性别限制:0全部开放,1男性,2女性")
@ColDefine(type = ColType.INT)
private Integer sexLimit;
@Column
@Comment("封面图")
@ColDefine(type = ColType.VARCHAR, width = 255)
private String cover;
@Column
@Comment("外链地址")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String linkUrl;
@Column
@Comment("发布状态:0草稿,1已发布")
@ColDefine(type = ColType.INT)
private Integer status;
@Column
@Comment("发布时间")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String publishTime;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.inclusive.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 南航专惠分页查询参数
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class InclusiveBenefitPageForm extends PageForm {
private Integer year;
private String title;
private String tagType;
}
@@ -0,0 +1,28 @@
package com.budwk.app.zhgh.inclusive.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.inclusive.model.InclusiveBenefit;
import com.budwk.app.zhgh.inclusive.param.InclusiveBenefitPageForm;
import com.budwk.app.zhgh.inclusive.vo.InclusiveBenefitVO;
import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* 南航专惠业务服务
*/
public interface InclusiveBenefitService extends BaseService<InclusiveBenefit> {
Pagination<InclusiveBenefitVO> pageData(InclusiveBenefitPageForm pageForm);
InclusiveBenefitVO findVO(String id, boolean onlyPublished);
List<NutMap> tagOptions();
void saveBenefit(InclusiveBenefit benefit, Integer status);
void updateStatus(String id, Integer status);
void deleteBenefit(String id);
}
@@ -0,0 +1,170 @@
package com.budwk.app.zhgh.inclusive.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_dict;
import com.budwk.app.zhgh.inclusive.model.InclusiveBenefit;
import com.budwk.app.zhgh.inclusive.param.InclusiveBenefitPageForm;
import com.budwk.app.zhgh.inclusive.service.InclusiveBenefitService;
import com.budwk.app.zhgh.inclusive.vo.InclusiveBenefitVO;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 南航专惠业务实现
*/
@IocBean(args = {"refer:dao"})
public class InclusiveBenefitServiceImpl extends BaseServiceImpl<InclusiveBenefit> implements InclusiveBenefitService {
private static final String TAG_DICT_CODE = "INCLUSIVE_BENEFIT_TYPE";
public InclusiveBenefitServiceImpl(Dao dao) {
super(dao);
}
@Override
public Pagination<InclusiveBenefitVO> pageData(InclusiveBenefitPageForm pageForm) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (pageForm.getYear() != null) {
cnd.and("year", "=", pageForm.getYear());
}
if (Strings.isNotBlank(pageForm.getTitle())) {
cnd.and(Cnd.likeEX("title", pageForm.getTitle()));
}
if (Strings.isNotBlank(pageForm.getTagType())) {
cnd.and("tagType", "=", pageForm.getTagType());
}
cnd.desc("publishTime").desc("createdAt");
Pagination<InclusiveBenefit> pagination = listPage(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
Map<String, String> tagNameMap = getTagNameMap();
List<InclusiveBenefitVO> list = pagination.getList().stream()
.map(benefit -> toVO(benefit, tagNameMap))
.collect(Collectors.toList());
return new Pagination<>(pagination.getPageNo(), pagination.getPageSize(), pagination.getTotalCount(), list);
}
@Override
public InclusiveBenefitVO findVO(String id, boolean onlyPublished) {
Cnd cnd = Cnd.where("id", "=", id).and("delFlag", "=", false);
if (onlyPublished) {
cnd.and("status", "=", 1);
}
return toVO(fetch(cnd), getTagNameMap());
}
@Override
public List<NutMap> tagOptions() {
Sys_dict parent = dao().fetch(Sys_dict.class, Cnd.where("code", "=", TAG_DICT_CODE).and("delFlag", "=", false));
if (parent == null) {
return Collections.emptyList();
}
return dao().query(Sys_dict.class, Cnd.where("parentId", "=", parent.getId())
.and("disabled", "=", false).and("delFlag", "=", false).asc("location"))
.stream()
.map(dict -> NutMap.NEW().addv("text", dict.getName()).addv("value", dict.getCode()))
.collect(Collectors.toList());
}
@Override
public void saveBenefit(InclusiveBenefit benefit, Integer status) {
InclusiveBenefit target;
if (StrUtil.isBlank(benefit.getId())) {
target = new InclusiveBenefit();
target.setYear(benefit.getYear() == null ? DateUtil.year(DateUtil.date()) : benefit.getYear());
} else {
target = fetch(Cnd.where("id", "=", benefit.getId()).and("delFlag", "=", false));
if (target == null) {
throw new IllegalArgumentException("南航专惠不存在或已删除");
}
target.setYear(benefit.getYear() == null ? target.getYear() : benefit.getYear());
}
target.setTitle(benefit.getTitle());
target.setSummary(benefit.getSummary());
target.setTagType(benefit.getTagType());
target.setContent(benefit.getContent());
target.setSexLimit(benefit.getSexLimit() == null ? 0 : benefit.getSexLimit());
target.setCover(benefit.getCover());
target.setLinkUrl(benefit.getLinkUrl());
target.setStatus(status == null ? 0 : status);
if (Integer.valueOf(1).equals(target.getStatus())) {
target.setPublishTime(DateUtil.now());
}
if (StrUtil.isBlank(target.getId())) {
insert(target);
} else {
update(target);
}
}
@Override
public void updateStatus(String id, Integer status) {
InclusiveBenefit benefit = fetch(Cnd.where("id", "=", id).and("delFlag", "=", false));
if (benefit == null) {
throw new IllegalArgumentException("南航专惠不存在或已删除");
}
Integer targetStatus = status == null ? 0 : status;
Chain chain = Chain.make("status", targetStatus);
if (Integer.valueOf(1).equals(targetStatus)) {
chain.add("publishTime", DateUtil.now());
}
update(chain, Cnd.where("id", "=", id));
}
@Override
public void deleteBenefit(String id) {
InclusiveBenefit benefit = fetch(Cnd.where("id", "=", id).and("delFlag", "=", false));
if (benefit == null) {
throw new IllegalArgumentException("南航专惠不存在或已删除");
}
vDelete(id);
}
private InclusiveBenefitVO toVO(InclusiveBenefit benefit, Map<String, String> tagNameMap) {
if (benefit == null) {
return null;
}
return new InclusiveBenefitVO()
.setId(benefit.getId())
.setYear(benefit.getYear())
.setTitle(benefit.getTitle())
.setSummary(benefit.getSummary())
.setTagType(benefit.getTagType())
.setTagTypeName(tagNameMap.get(benefit.getTagType()))
.setContent(benefit.getContent())
.setSexLimit(benefit.getSexLimit())
.setSexLimitName(getSexLimitName(benefit.getSexLimit()))
.setCover(benefit.getCover())
.setLinkUrl(benefit.getLinkUrl())
.setStatus(benefit.getStatus())
.setStatusName(Integer.valueOf(1).equals(benefit.getStatus()) ? "已发布" : "草稿")
.setCreatedAt(benefit.getCreatedAt())
.setUpdatedAt(benefit.getUpdatedAt())
.setPublishTime(benefit.getPublishTime());
}
private Map<String, String> getTagNameMap() {
List<NutMap> options = tagOptions();
return options.stream().collect(Collectors.toMap(option -> option.getString("value"), option -> option.getString("text")));
}
private String getSexLimitName(Integer sexLimit) {
if (Integer.valueOf(1).equals(sexLimit)) {
return "男性";
}
if (Integer.valueOf(2).equals(sexLimit)) {
return "女性";
}
return "全部开放";
}
}
@@ -0,0 +1,28 @@
package com.budwk.app.zhgh.inclusive.vo;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 南航专惠列表和详情展示对象
*/
@Data
@Accessors(chain = true)
public class InclusiveBenefitVO {
private String id;
private Integer year;
private String title;
private String summary;
private String tagType;
private String tagTypeName;
private String content;
private Integer sexLimit;
private String sexLimitName;
private String cover;
private String linkUrl;
private Integer status;
private String statusName;
private Long createdAt;
private Long updatedAt;
private String publishTime;
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.constants;
/**
* 困难补助申请的业务状态
*
* 仅用于草稿及流程结果的业务快照审批中的真实状态以 TKBZ 工作流实例为准
*/
public interface DifficultSubsidyState {
int TO_BE_SUBMITTED = 7000;
int IN_PROCESS = 7010;
int SCHOOL_REFUSE = 7050;
int SCHOOL_BACK = 7060;
int PASS = 7070;
}
@@ -0,0 +1,42 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service.DifficultSubsidyService;
import io.swagger.annotations.ApiOperation;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
/**
* 困难补助的公共查询接口
*/
@IocBean
@At("/platform/difficultSubsidy/common")
@Ok("json")
public class DifficultSubsidyCommonController {
@Inject
private DifficultSubsidyService difficultSubsidyService;
@At
@ApiOperation("查询申请详情及审核记录")
@SaCheckPermission(value = {"difficultSubsidy.apply", "difficultSubsidy.mine",
"difficultSubsidy.schoolAudit", "difficultSubsidy.query"}, mode = SaMode.OR)
public Result findOne(String id) {
return Result.success(difficultSubsidyService.findDetail(id));
}
@At
@ApiOperation("查询补助项目及疾病类型")
@SaCheckPermission(value = {"difficultSubsidy.apply", "difficultSubsidy.mine",
"difficultSubsidy.schoolAudit", "difficultSubsidy.query"}, mode = SaMode.OR)
public Result projectInfo() {
java.util.Map<String, Object> data = new java.util.HashMap<>();
data.put("projectList", difficultSubsidyService.projectList());
data.put("diseaseList", difficultSubsidyService.diseaseList(null, null));
return Result.success(data);
}
}
@@ -0,0 +1,70 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.controller.basicSetting;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyDiseaseType;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyProject;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service.DifficultSubsidyService;
import io.swagger.annotations.ApiOperation;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
/**
* 困难补助项目与疾病类型配置
*/
@IocBean
@At("/platform/difficultSubsidy/basicSetting/project")
@Ok("json")
public class DifficultSubsidyProjectController {
@Inject
private DifficultSubsidyService difficultSubsidyService;
@At("")
@SaCheckPermission("difficultSubsidy.basicSetting.project")
@Ok("beetl:/platform/zhgh/staffbenefit/difficultSubsidy/basicSetting/project/index.html")
public void index() {
}
@At
@ApiOperation("补助项目列表")
@SaCheckPermission("difficultSubsidy.basicSetting.project")
public Result projectList() {
return Result.success(difficultSubsidyService.projectList());
}
@At
@SaCheckPermission("difficultSubsidy.basicSetting.project")
public Result saveProject(@Param("data") DifficultSubsidyProject project) {
return Result.success(difficultSubsidyService.saveProject(project));
}
@At
@SaCheckPermission("difficultSubsidy.basicSetting.project")
public Result deleteProject(Integer id) {
difficultSubsidyService.deleteProject(id);
return Result.success();
}
@At
@SaCheckPermission("difficultSubsidy.basicSetting.project")
public Result diseaseList(Integer projectId, String keyword) {
return Result.success(difficultSubsidyService.diseaseList(projectId, keyword));
}
@At
@SaCheckPermission("difficultSubsidy.basicSetting.project")
public Result saveDisease(@Param("data") DifficultSubsidyDiseaseType diseaseType) {
return Result.success(difficultSubsidyService.saveDisease(diseaseType));
}
@At
@SaCheckPermission("difficultSubsidy.basicSetting.project")
public Result deleteDisease(Integer id) {
difficultSubsidyService.deleteDisease(id);
return Result.success();
}
}
@@ -0,0 +1,67 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.controller.process;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyApply;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service.DifficultSubsidyService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
/**
* 特困补助申请表
*/
@IocBean
@At("/platform/difficultSubsidy/apply")
@Ok("json")
public class DifficultSubsidyApplyController {
@Inject
private DifficultSubsidyService difficultSubsidyService;
@At("")
@SaCheckPermission("difficultSubsidy.apply")
@Ok("beetl:/platform/zhgh/staffbenefit/difficultSubsidy/process/apply/index.html")
public void index() {
}
/**
* 保留旧的表单子路径避免已有链接失效正式菜单入口仍为 /apply
*/
@At("/form")
@SaCheckPermission("difficultSubsidy.apply")
@Ok("beetl:/platform/zhgh/staffbenefit/difficultSubsidy/process/apply/index.html")
public void form() {
}
@At
@Ok("json")
@SaCheckPermission("difficultSubsidy.apply")
public Result save(@Param("data") DifficultSubsidyApply apply) {
return Result.success(difficultSubsidyService.saveDraft(apply));
}
@At
@Ok("json")
@SaCheckPermission("difficultSubsidy.apply")
public Result submit(@Param("data") DifficultSubsidyApply apply) {
return Result.success(difficultSubsidyService.submit(apply));
}
/**
* 查询当前登录人有权选择的补助申请人
*/
@At
@SaCheckPermission("difficultSubsidy.apply")
public Result queryRecipients(String key) {
return Result.success(difficultSubsidyService.listRecipients(key));
}
@At
@SaCheckPermission("difficultSubsidy.apply")
public Result findOne(String id) {
return Result.success(difficultSubsidyService.findDetail(id));
}
}
@@ -0,0 +1,53 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.controller.process;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service.DifficultSubsidyService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
/**
* 当前登录人的特困补助申请记录
*/
@IocBean
@At("/platform/difficultSubsidy/mine")
@Ok("json")
public class DifficultSubsidyMineController {
@Inject
private DifficultSubsidyService difficultSubsidyService;
@At("")
@SaCheckPermission("difficultSubsidy.mine")
@Ok("beetl:/platform/zhgh/staffbenefit/difficultSubsidy/process/mine/index.html")
public void index() {
}
@At
@SaCheckPermission("difficultSubsidy.mine")
public Result pageData(Integer pageNumber, Integer pageSize, Integer year) {
return Result.success(difficultSubsidyService.pageMine(
pageNumber, pageSize, SecurityUtil.getUserId(), year));
}
@At
@SaCheckPermission("difficultSubsidy.mine")
public Result delete(String id) {
difficultSubsidyService.deleteMineApplication(id, SecurityUtil.getUserId());
return Result.success();
}
@At
@Ok("void")
@SaCheckPermission("difficultSubsidy.mine")
public void exportApplyInfo(String id, HttpServletResponse response) {
CommonDownloadUtil.download("特困补助申请登记表.docx",
difficultSubsidyService.exportApplyWord(id), response);
}
}
@@ -0,0 +1,57 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.controller.process;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service.DifficultSubsidyService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
/**
* 已通过特困补助综合查询与汇总导出
*/
@IocBean
@At("/platform/difficultSubsidy/query")
@Ok("json")
public class DifficultSubsidyQueryController {
@Inject
private DifficultSubsidyService difficultSubsidyService;
@At("")
@SaCheckPermission("difficultSubsidy.query")
@Ok("beetl:/platform/zhgh/staffbenefit/difficultSubsidy/query/index.html")
public void index() {
}
@At
@SaCheckPermission("difficultSubsidy.query")
public Result pageData(Integer pageNumber, Integer pageSize,
Integer year, String startMonth, String endMonth,
String searchKeyword, String unionId, String unitId) {
return Result.success(difficultSubsidyService.pageQuery(pageNumber, pageSize,
year, startMonth, endMonth, searchKeyword, unionId, unitId));
}
@At
@Ok("void")
@SaCheckPermission("difficultSubsidy.query")
public void exportSummaryExcel(Integer year, String startMonth, String endMonth,
HttpServletResponse response) {
CommonDownloadUtil.download("特困补助人员名单.xlsx",
difficultSubsidyService.exportSummaryExcel(year, startMonth, endMonth), response);
}
@At
@Ok("void")
@SaCheckPermission("difficultSubsidy.query")
public void exportSummary(Integer year, String startMonth, String endMonth,
HttpServletResponse response) {
CommonDownloadUtil.download("特困补助汇总表.docx",
difficultSubsidyService.exportSummaryWord(year, startMonth, endMonth), response);
}
}
@@ -0,0 +1,100 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.controller.process;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyApply;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service.DifficultSubsidyService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse;
/**
* 特困补助校工会审核
*/
@IocBean
@At("/platform/difficultSubsidy/schoolAudit")
@Ok("json")
public class DifficultSubsidySchoolAuditController {
@Inject
private DifficultSubsidyService difficultSubsidyService;
@At("")
@SaCheckPermission("difficultSubsidy.schoolAudit")
@Ok("beetl:/platform/zhgh/staffbenefit/difficultSubsidy/process/schoolAudit/index.html")
public void index() {
}
/**
* 复用申请表页面展示校工会修改表单实际保存使用校工会专用接口
*/
@At("/edit")
@SaCheckPermission("difficultSubsidy.schoolAudit")
@Ok("beetl:/platform/zhgh/staffbenefit/difficultSubsidy/process/apply/index.html")
public void edit() {
}
@At
@SaCheckPermission("difficultSubsidy.schoolAudit")
public Result pageData(Integer pageNumber, Integer pageSize,
Integer year, String startMonth, String endMonth,
String searchKeyword, String unionId, String unitId, Integer isAudit) {
return Result.success(difficultSubsidyService.pageSchoolAudit(pageNumber, pageSize,
SecurityUtil.getUserId(), year, startMonth, endMonth,
searchKeyword, unionId, unitId, isAudit));
}
@At
@SaCheckPermission("difficultSubsidy.schoolAudit")
public Result calculateSubsidy(String id) {
return Result.success(difficultSubsidyService.calculateSubsidy(id));
}
/**
* 修改校工会待审核申请的基础表单信息不推进工作流任务
*/
@At
@SaCheckPermission("difficultSubsidy.schoolAudit")
public Result modifyApply(Long processTaskId, @Param("data") DifficultSubsidyApply apply) {
difficultSubsidyService.modifySchoolAuditApply(processTaskId, apply);
return Result.success();
}
@At
@SaCheckPermission("difficultSubsidy.schoolAudit")
public Result executeTask(Long processTaskId, Integer submitType, String opinion,
Float finalSubsidyAmount, String hospitalRecords) {
difficultSubsidyService.executeSchoolAudit(processTaskId, submitType, opinion,
finalSubsidyAmount, hospitalRecords);
return Result.success();
}
@At
@SaCheckPermission("difficultSubsidy.schoolAudit")
public Result revoke(Long processTaskId) {
difficultSubsidyService.revokeSchoolAudit(processTaskId, SecurityUtil.getUserId());
return Result.success();
}
@At
@SaCheckPermission("difficultSubsidy.schoolAudit")
public Result modifyFinalSubsidyAmount(String id, Float finalSubsidyAmount) {
difficultSubsidyService.modifyFinalSubsidyAmount(id, finalSubsidyAmount);
return Result.success();
}
@At
@Ok("void")
@SaCheckPermission("difficultSubsidy.schoolAudit")
public void exportZip(Integer year, String startMonth, String endMonth,
HttpServletResponse response) {
CommonDownloadUtil.download("特困补助审核材料.zip",
difficultSubsidyService.exportApprovedZip(year, startMonth, endMonth), response);
}
}
@@ -0,0 +1,148 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models;
import com.budwk.app.base.model.BaseModel;
import cn.hutool.json.JSONObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import java.util.Date;
import java.util.List;
/**
* 困难补助申请申请提交后通过特困补助工作流处理流程实例以本记录主键作为业务编号关联
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("difficult_subsidy_apply")
@TableMeta("{'mysql-charset':'utf8mb4'}")
public class DifficultSubsidyApply extends BaseModel {
@Name
@PrevInsert(uu32 = true)
@Comment("主键")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String id;
@Comment("申请人用户ID")
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String applyUserId;
@Comment("申请人姓名")
@Column
@ColDefine(type = ColType.VARCHAR, width = 100)
private String applyUserName;
@Comment("申请人工号")
@Column
@ColDefine(type = ColType.VARCHAR, width = 100)
private String applyLoginName;
@Comment("受助人用户ID")
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Comment("受助人姓名")
@Column
@ColDefine(type = ColType.VARCHAR, width = 100)
private String userName;
@Comment("受助人工号")
@Column
@ColDefine(type = ColType.VARCHAR, width = 100)
private String loginName;
@Comment("性别")
@Column
@ColDefine(type = ColType.VARCHAR, width = 10)
private String sex;
@Comment("年龄")
@Column
@ColDefine(type = ColType.INT)
private Integer age;
@Comment("单位ID")
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unitId;
@Comment("单位名称")
@Column
@ColDefine(type = ColType.VARCHAR, width = 500)
private String unitName;
@Comment("分工会ID")
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unionId;
@Comment("分工会名称")
@Column
@ColDefine(type = ColType.VARCHAR, width = 500)
private String unionName;
@Comment("家庭住址")
@Column
@ColDefine(type = ColType.VARCHAR, width = 500)
private String homeAddress;
@Comment("联系电话")
@Column
@ColDefine(type = ColType.VARCHAR, width = 50)
private String mobile;
@Comment("补助项目ID集合")
@Column
@ColDefine(type = ColType.MYSQL_JSON)
private List<Integer> subsidyType;
@Comment("申请补助原因")
@Column
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String reason;
@Comment("证明材料")
@Column
@ColDefine(type = ColType.MYSQL_JSON)
private List<JSONObject> files;
@Comment("纸质申请表")
@Column
@ColDefine(type = ColType.MYSQL_JSON)
private List<JSONObject> applyFile;
@Comment("住院记录")
@Column
@ColDefine(type = ColType.MYSQL_JSON)
private List<JSONObject> hospitalRecords;
@Comment("申请时间")
@Column
@ColDefine(type = ColType.DATETIME)
private Date applyDate;
@Comment("审核状态")
@Column
@ColDefine(type = ColType.INT)
private Integer stateId;
@Comment("建议补助金额")
@Column
@ColDefine(type = ColType.FLOAT, width = 10)
private Float subsidyAmount;
@Comment("最终补助金额")
@Column
@ColDefine(type = ColType.FLOAT, width = 10)
private Float finalSubsidyAmount;
}
@@ -0,0 +1,42 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
/**
* 困难补助项目下的疾病及金额上限配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("difficult_subsidy_disease_type")
@TableMeta("{'mysql-charset':'utf8mb4'}")
public class DifficultSubsidyDiseaseType extends BaseModel {
@Id(auto = true)
@Comment("主键")
@ColDefine(type = ColType.INT)
private Integer id;
@Comment("补助项目ID")
@Column
@ColDefine(type = ColType.INT)
private Integer projectId;
@Comment("疾病名称")
@Column
@ColDefine(type = ColType.VARCHAR, width = 200)
private String diseaseName;
@Comment("补助金额上限")
@Column
@ColDefine(type = ColType.VARCHAR, width = 50)
private String subsidyAmountUpperLimit;
}
@@ -0,0 +1,41 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Many;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import java.util.List;
/**
* 困难补助项目配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("difficult_subsidy_project")
@TableMeta("{'mysql-charset':'utf8mb4'}")
public class DifficultSubsidyProject extends BaseModel {
@Id(auto = true)
@Comment("主键")
@ColDefine(type = ColType.INT)
private Integer id;
@Comment("补助项目名称")
@Column
@ColDefine(type = ColType.VARCHAR, width = 100)
private String projectName;
/**
* 当前补助范围下的疾病或事项说明仅用于关联查询不单独落入项目表
*/
@Many(target = DifficultSubsidyDiseaseType.class, field = "projectId", key = "id")
private List<DifficultSubsidyDiseaseType> diseaseTypeList;
}
@@ -0,0 +1,105 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyApply;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyDiseaseType;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyProject;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* 特困补助申请审核查询配置和导出公共业务服务
*/
public interface DifficultSubsidyService extends BaseService<DifficultSubsidyApply> {
DifficultSubsidyApply saveDraft(DifficultSubsidyApply apply);
DifficultSubsidyApply submit(DifficultSubsidyApply apply);
/**
* 按当前用户的数据权限查询可选择的补助申请人
*
* @param keyword 姓名或工号关键字
* @return 最多十条申请人基础信息
*/
List<NutMap> listRecipients(String keyword);
/**
* 查询当前登录人的申请记录及最新工作流状态
*/
Pagination<NutMap> pageMine(Integer pageNumber, Integer pageSize, String applyUserId, Integer year);
/**
* 查询当前审核人的校工会待办或已办记录申请年份和月份范围共同约束申请时间
*/
Pagination<NutMap> pageSchoolAudit(Integer pageNumber, Integer pageSize, String auditorId,
Integer year, String startMonth, String endMonth,
String searchKeyword, String unionId,
String unitId, Integer isAudit);
/**
* 查询指定申请年份月份范围内最终通过的补助申请
*/
Pagination<DifficultSubsidyApply> pageQuery(Integer pageNumber, Integer pageSize, Integer year,
String startMonth, String endMonth,
String searchKeyword, String unionId, String unitId);
/**
* 返回申请项目配置和工作流审核记录组成的完整查看数据
*/
NutMap findDetail(String id);
List<DifficultSubsidyProject> projectList();
List<DifficultSubsidyDiseaseType> diseaseList(Integer projectId, String keyword);
DifficultSubsidyProject saveProject(DifficultSubsidyProject project);
DifficultSubsidyDiseaseType saveDisease(DifficultSubsidyDiseaseType diseaseType);
void deleteProject(Integer id);
void deleteDisease(Integer id);
/**
* 删除当前登录人的特困补助申请并同步清理该申请对应的流程实例任务及待办办理人
*
* @param id 申请记录ID
* @param applyUserId 当前登录用户ID用于校验申请归属
*/
void deleteMineApplication(String id, String applyUserId);
/**
* 校工会审核期间修改申请基础表单信息不变更申请人受助人身份或流程状态
*
* @param processTaskId 当前校工会审核待办任务ID
* @param apply 页面提交的申请基础表单数据
*/
void modifySchoolAuditApply(Long processTaskId, DifficultSubsidyApply apply);
/**
* 保存逐条补助项目审核结果与金额并执行校工会工作流任务
*/
void executeSchoolAudit(Long processTaskId, Integer submitType, String opinion,
Float finalSubsidyAmount, String hospitalRecords);
/**
* 撤销本人已经完成的校工会审核任务并重新激活该节点
*/
void revokeSchoolAudit(Long processTaskId, String auditorId);
void modifyFinalSubsidyAmount(String id, Float finalSubsidyAmount);
NutMap calculateSubsidy(String id);
Workbook exportSummaryExcel(Integer year, String startMonth, String endMonth);
byte[] exportSummaryWord(Integer year, String startMonth, String endMonth);
byte[] exportApplyWord(String id);
byte[] exportApprovedZip(Integer year, String startMonth, String endMonth);
}
@@ -0,0 +1,83 @@
package com.budwk.app.zhgh.staffbenefit.difficultSubsidy.service.impl;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyApply;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyDiseaseType;
import com.budwk.app.zhgh.staffbenefit.difficultSubsidy.models.DifficultSubsidyProject;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.util.Daos;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* 特困补助模块表结构与基础补助范围初始化
*
* 项目公共启动器只在 DEBUG 日志级别下执行实体迁移因此本模块单独按实体补齐自己的三张表
* 不修改公共表也不依赖额外 SQL 文件基础数据仅在对应表为空时写入避免覆盖管理员后续维护的数据
*/
@IocBean(create = "init")
public class DifficultSubsidySchemaInitializer {
@Inject
private Dao dao;
/**
* 根据实体补齐模块字段并在全新配置表中写入参考项目的默认补助范围
*/
public void init() {
Daos.migration(dao, DifficultSubsidyApply.class, true, false);
Daos.migration(dao, DifficultSubsidyProject.class, true, false);
Daos.migration(dao, DifficultSubsidyDiseaseType.class, true, false);
initDefaultProjects();
}
/**
* 仅当项目和说明表都为空时初始化默认数据显式保留编号以兼容参考项目的补助测算规则
*/
private void initDefaultProjects() {
if (dao.count(DifficultSubsidyProject.class) > 0 || dao.count(DifficultSubsidyDiseaseType.class) > 0) {
return;
}
insertProject(1, "重大疾病补助");
insertProject(2, "门诊第二类特殊疾病补助");
insertProject(3, "重大变故补助");
insertProject(4, "住院护理补助");
insertDisease(1, 1, "重大器官移植术或造血干细胞移植术");
insertDisease(2, 1, "恶性肿瘤");
insertDisease(3, 1, "重型再生障碍性贫血");
insertDisease(4, 1, "严重系统性红斑狼疮性肾病");
insertDisease(5, 1, "持续植物人状态");
insertDisease(6, 1, "其他自生病起本年度内自费超过5万的重大疾病");
insertDisease(7, 2, "恶性肿瘤病人的补充放化疗及手术后门诊支持性治疗");
insertDisease(8, 2, "慢性白血病");
insertDisease(9, 2, "红斑狼疮");
insertDisease(10, 2, "慢性肾功能衰竭的透析治疗");
insertDisease(11, 2, "重大器官移植术后抗免疫排斥药物治疗");
insertDisease(12, 2, "口服靶向药物治疗");
insertDisease(13, 3, "本年度内,教职工家庭遭受地震、泥石流、火灾等自然灾害(财产损失价值20000元及以上),造成生活困难");
insertDisease(14, 3, "本年度内,教职工本人或直系亲属(教职工子女、配偶和教职工本人父母)遭受突发性意外伤害(工伤等除外),个人承担治疗费用较大(减除保险、医保及责任方赔付以外20000元及以上),造成生活困难");
insertDisease(15, 3, "本年度内,教职工本人或直系亲属(教职工子女、配偶和教职工本人父母)患严重疾病,教职工个人承担治疗费用较大(减除保险、医保以外20000元及以上),造成生活困难");
insertDisease(18, 4, "普通疾病");
insertDisease(19, 4, "重大疾病");
}
private void insertProject(Integer id, String name) {
if (dao.fetch(DifficultSubsidyProject.class, Cnd.where("id", "=", id)) == null) {
DifficultSubsidyProject project = new DifficultSubsidyProject();
project.setId(id);
project.setProjectName(name);
dao.insert(project);
}
}
private void insertDisease(Integer id, Integer projectId, String name) {
if (dao.fetch(DifficultSubsidyDiseaseType.class, Cnd.where("id", "=", id)) == null) {
DifficultSubsidyDiseaseType disease = new DifficultSubsidyDiseaseType();
disease.setId(id);
disease.setProjectId(projectId);
disease.setDiseaseName(name);
dao.insert(disease);
}
}
}
@@ -299,7 +299,7 @@ public class DifficultHelpReadingController {
docData.put("bankCardNum", record.getString("bankCardNum"));
docData.put("reason", record.getString("reason"));
docData.put("applyTime", DateUtil.format(DateUtil.parse(record.getString("applyTime")), "yyyy年MM月dd日"));
docData.put("schoolname", "中国地质大学(武汉)");
docData.put("schoolname", "西南财经大学");
docData.put("year", DateUtil.format(DateUtil.parse(record.getString("applyTime")), "yyyy"));
String familyListStr = record.getString("familyList");
@@ -350,7 +350,7 @@ public class DifficultHelpReadingController {
docData.put("bankCardNum", difficultHelp.getBankCardNum());
docData.put("reason", difficultHelp.getReason());
docData.put("applyTime", DateUtil.format(difficultHelp.getApplyTime(), "yyyy-MM-dd"));
docData.put("schoolname","中国地质大学(武汉)");
docData.put("schoolname","西南财经大学");
docData.put("year",DateUtil.format(difficultHelp.getApplyTime(), "yyyy"));
docData.put("familyList", difficultHelp.getFamilyList() != null ? difficultHelp.getFamilyList() : new ArrayList<>());
@@ -371,7 +371,7 @@ public class DifficultHelpReadingController {
@At
private void exportAsPDF(String templateName, HashMap<String, Object> docData,Configure config,
DifficultHelpInfo difficultHelp, HttpServletResponse response) throws Exception {
String fileName = "中国地质大学(武汉)困难慰问申请表_" + difficultHelp.getUserName() + "_" +
String fileName = "西南财经大学困难慰问申请表_" + difficultHelp.getUserName() + "_" +
DateUtil.format(difficultHelp.getApplyTime(), "yyyyMMdd") + ".pdf";
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
@@ -396,7 +396,7 @@ public class DifficultHelpReadingController {
@At
private void exportAsWord(String templateName, HashMap<String, Object> docData,Configure config,
DifficultHelpInfo difficultHelp, HttpServletResponse response) throws Exception {
String fileName = "中国地质大学(武汉)困难慰问申请表_" + difficultHelp.getUserName() + "_" +
String fileName = "西南财经大学困难慰问申请表_" + difficultHelp.getUserName() + "_" +
DateUtil.format(difficultHelp.getApplyTime(), "yyyyMMdd") + ".docx";
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
@@ -0,0 +1,192 @@
<template>
<div v-loading="loading">
<div class="process-title">申请信息</div>
<el-descriptions :column="3" border class="difficult-subsidy-info flow-task-form">
<el-descriptions-item label="申请人姓名/工号">
{{ detail.applyUserName }}{{ detail.applyLoginName }}
</el-descriptions-item>
<el-descriptions-item label="受助人姓名">{{ detail.userName }}</el-descriptions-item>
<el-descriptions-item label="受助人工号">{{ detail.loginName }}</el-descriptions-item>
<el-descriptions-item label="性别">{{ detail.sex }}</el-descriptions-item>
<el-descriptions-item label="年龄">{{ detail.age }}</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ detail.mobile }}</el-descriptions-item>
<el-descriptions-item label="所在部门">{{ detail.unitName }}</el-descriptions-item>
<el-descriptions-item label="所属工会">{{ detail.unionName }}</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ detail.applyDate }}</el-descriptions-item>
<el-descriptions-item label="申请补助类别" :span="3">
{{ subsidyTypeNames(detail.subsidyType) }}
</el-descriptions-item>
<el-descriptions-item label="住院及补助事项记录" :span="3">
<el-table :data="detail.hospitalRecords || []" border style="width: 100%">
<el-table-column label="就诊医院" prop="hospitalName" min-width="130"></el-table-column>
<el-table-column label="入院时间" prop="startDate" width="110"></el-table-column>
<el-table-column label="出院时间" prop="endDate" width="110"></el-table-column>
<el-table-column label="自费金额" prop="selfFundedAmount" width="100"></el-table-column>
<el-table-column label="补助类型" min-width="150">
<template slot-scope="scope">{{ projectName(scope.row.subsidyType) }}</template>
</el-table-column>
<el-table-column label="疾病详情" min-width="220">
<template slot-scope="scope">{{ diseaseNames(scope.row.diseaseDetail) }}</template>
</el-table-column>
<el-table-column label="备注" prop="notes" min-width="120"></el-table-column>
</el-table>
</el-descriptions-item>
<el-descriptions-item label="申请补助原因" :span="3">
<div class="pre-line">{{ detail.reason }}</div>
</el-descriptions-item>
<el-descriptions-item label="纸质申请表" :span="3">
<file-preview :files="detail.applyFile" complete_result></file-preview>
</el-descriptions-item>
<el-descriptions-item label="证明材料" :span="3">
<file-preview :files="detail.files" complete_result></file-preview>
</el-descriptions-item>
<el-descriptions-item label="建议补助金额">{{ detail.subsidyAmount }}</el-descriptions-item>
<el-descriptions-item label="最终补助金额" :span="2">{{ detail.finalSubsidyAmount }}</el-descriptions-item>
</el-descriptions>
<div v-for="task in detail.approvalRecords || []" :key="task.id" class="mt10">
<div class="process-title">{{ task.displayName }}</div>
<el-descriptions :column="3" border class="difficult-subsidy-info flow-task-form">
<el-descriptions-item label="办理用户">{{ auditUserName(task) }}</el-descriptions-item>
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
<el-descriptions-item label="办理结果">{{ auditResultName(task) }}</el-descriptions-item>
<el-descriptions-item v-if="taskOpinion(task)" label="办理意见" :span="3">
<div class="pre-line">{{ taskOpinion(task) }}</div>
</el-descriptions-item>
</el-descriptions>
</div>
<slot></slot>
</div>
</template>
<script>
module.exports = {
name: "DifficultSubsidyProcessInfo",
data: function () {
return {
loading: false,
detail: {},
projectList: [],
diseaseList: []
};
},
methods: {
open: function (id) {
var self = this;
this.$set(this, "loading", true);
this.$set(this, "detail", {});
return Promise.all([
this.$axios.post("/platform/difficultSubsidy/common/findOne", {id: id}),
this.$axios.post("/platform/difficultSubsidy/common/projectInfo")
]).then(function (responses) {
if (responses[0].code === 0) {
var detail = responses[0].data || {};
// v3 response.data
self.$set(detail, "applyFile", self.normalizeLegacyFiles(detail.applyFile));
self.$set(detail, "files", self.normalizeLegacyFiles(detail.files));
self.$set(self, "detail", detail);
}
if (responses[1].code === 0) {
self.$set(self, "projectList", responses[1].data.projectList || []);
self.$set(self, "diseaseList", responses[1].data.diseaseList || []);
}
self.$set(self, "loading", false);
}).catch(function () {
self.$set(self, "loading", false);
});
},
normalizeLegacyFiles: function (files) {
var self = this;
return (files || []).map(function (item) {
if (!item.response || !item.response.data) {
self.$set(item, "name", item.name || item.filename || "历史附件");
self.$set(item, "response", {data: item.id || ""});
}
return item;
});
},
auditUserName: function (row) {
var formData = row.taskFormData || {};
// 使
if (row.taskName === "startTask") {
return this.formatUserName(this.detail.applyUserName, this.detail.applyLoginName);
}
if (formData.userName) {
return this.formatUserName(formData.userName, formData.loginName);
}
// operator ID ID
return "—";
},
formatUserName: function (userName, loginName) {
if (!userName) {
return "—";
}
return loginName ? userName + "" + loginName + "" : userName;
},
auditResultName: function (row) {
var ext = row.ext || {};
var resultNames = {
0: "发起申请",
1: "同意",
2: "拒绝",
3: "退回上一步",
5: "重新提交",
6: "退回申请人",
20: "会签不同意"
};
return resultNames[ext.submitType] || "已办理";
},
taskOpinion: function (row) {
var formData = row.taskFormData || {};
return formData.approvalComment || formData.opinion || "";
},
projectName: function (id) {
var item = this.projectList.find(function (project) {
return project.id === id;
});
return item ? item.projectName : "";
},
subsidyTypeNames: function (ids) {
var self = this;
var idList = this.normalizeIdList(ids);
return idList.map(function (id) {
return self.projectName(id);
}).filter(function (name) {
return name;
}).join("、");
},
normalizeIdList: function (ids) {
if (Array.isArray(ids)) {
return ids;
}
if (typeof ids === "string" && ids) {
try {
var parsedIds = JSON.parse(ids);
return Array.isArray(parsedIds) ? parsedIds : [];
} catch (e) {
return [];
}
}
return [];
},
diseaseNames: function (ids) {
var diseaseIds = ids || [];
return this.diseaseList.filter(function (item) {
return diseaseIds.indexOf(item.id) >= 0;
}).map(function (item) {
return item.diseaseName;
}).join("、");
}
}
};
</script>
<style scoped>
.difficult-subsidy-info .el-descriptions-item__label {
min-width: 130px;
}
.pre-line {
white-space: pre-line;
}
</style>
@@ -75,7 +75,14 @@ const branchUnionUserManage = {
<el-dialog title="添加" :visible.sync="dialogFormVisible" width="600px" :close-on-click-modal="false">
<el-form :model="formData" ref="form" size="small" label-width="80px">
<el-form-item prop="roleCode" label="角色">
<dict-select v-model="formData.roleCode" code="BRANCH_UNION_ROLES" placeholder="请选择角色"></dict-select>
<el-select v-model="formData.roleCode" placeholder="请选择角色">
<el-option
v-for="item in roleOptions"
:key="item.code"
:label="item.name"
:value="item.code"
></el-option>
</el-select>
</el-form-item>
<el-form-item prop="j" label="届数">
<dict-select v-model="formData.j" code="TEACHER_CONGRESS_J" placeholder="请选择届数"></dict-select>
@@ -143,6 +150,7 @@ const branchUnionUserManage = {
id: "",
leaveDate: ""
},
roleOptions: [],
jOptions: [],
usedJCodes: []
}
@@ -154,6 +162,13 @@ const branchUnionUserManage = {
}
},
methods: {
loadRoleOptions() {
return $.get("/platform/sys/union/branchUnionRoleOptions").then((res) => {
if (res.code === 0) {
this.roleOptions = res.data || []
}
})
},
loadJOptions() {
return $.get("/open/common/dictOptions", { code: "TEACHER_CONGRESS_J" }).then((res) => {
this.jOptions = res.data || []
@@ -279,6 +294,7 @@ const branchUnionUserManage = {
}
},
created() {
this.loadRoleOptions()
this.initJSearch()
},
style: /*language=CSS*/ `
@@ -56,7 +56,7 @@ layout("/layouts/platform.html"){
<el-card class="mt10" shadow="never">
<table-tool
:label="pageForm.status==='2'?('中国地质大学(武汉)'+pageForm.year+'年教职工运动会获奖个数汇总表'):'奖品名单'">
:label="pageForm.status==='2'?('西南财经大学'+pageForm.year+'年教职工运动会获奖个数汇总表'):'奖品名单'">
<el-tag
:effect="pageForm.status===item.code?'dark':'plain'"
:key="item.code"
@@ -76,7 +76,7 @@ layout("/layouts/platform.html"){
<tr>
<td class="text-center" colspan="11">
<span v-if="activityList.find(v=>v.id === pageForm.activityId)">
中国地质大学(武汉){{ activityList.find(v=>v.id === pageForm.activityId).name }}
西南财经大学{{ activityList.find(v=>v.id === pageForm.activityId).name }}
{{unionName}})获奖名单
</span>
</td>
@@ -149,7 +149,9 @@ const PROPOSAL_INFO = {
<el-table-column label="姓名" prop="userName"></el-table-column>
<el-table-column label="工号" prop="loginName"></el-table-column>
<el-table-column label="性别" prop="sex"></el-table-column>
<el-table-column label="联系方式" prop="mobile"></el-table-column>
<el-table-column label="联系方式" prop="mobile">
<template slot-scope="{row}">{{ row.mobile || '' }}</template>
</el-table-column>
<el-table-column label="单位" prop="unitName"></el-table-column>
<el-table-column label="分工会" prop="unionName"></el-table-column>
<el-table-column label="代表团" prop="delegationName"></el-table-column>
@@ -271,9 +273,37 @@ const PROPOSAL_INFO = {
this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => {
if (res.code === 0) {
const tasks = res.data || []
this.doneTasks = tasks
// 提案历史按定义节点顺序展示,迁入任务不受数据库补写时间影响。
const taskOrders = {
startTask: 10,
invite: 20,
second: 30,
secondaryUnionAudit: 40,
partyOrganizationAudit: 50,
committee: 60,
committeeFiling: 60,
committeeFilingUnit: 70,
unit_reply: 80,
unitReply: 80,
schoolLeader: 90,
feedback: 100,
feedbackEvaluation: 100
}
tasks.sort((first, second) => {
const orderDifference = (taskOrders[first.taskName] || 999) - (taskOrders[second.taskName] || 999)
if (orderDifference !== 0) {
return orderDifference
}
const firstTime = first.finishTime || ''
const secondTime = second.finishTime || ''
if (firstTime !== secondTime) {
return firstTime.localeCompare(secondTime)
}
return first.id - second.id
})
this.$set(this, 'doneTasks', tasks)
// 审核记录加载完成后默认全部折叠,由用户按需展开查看。
this.activeTaskId = null
this.$set(this, 'activeTaskId', null)
// 通知外层页面已办节点数据已加载完成,便于当前环节做表单回显。
this.$emit("done-tasks", tasks)
}
@@ -285,7 +285,7 @@ layout("/layouts/platform.html"){
})
},
exportDocx(id) {
this.$downLoad("/platform/proposal/common/exportProposalAsDocx", {id})
this.$downLoad("/platform/proposal/export/comprehensive/exportProposalAsDocx", {id})
},
exportSummaryAsExcel() {
this.$downLoad("/platform/proposal/export/comprehensive/exportSummaryAsExcel", {
@@ -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"},
@@ -201,7 +201,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{ label: "提案编号", prop: "code" },
{ label: "立案编号", prop: "caseFilingCode" },
{ label: "提案名称", prop: "name", width: "200px" },
{ label: "提案类别", prop: "typeName" },
{ label: "届次", prop: "sessionName" },
@@ -209,7 +209,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案类别", prop: "typeName"},
{label: "届次", prop: "sessionName"},
@@ -108,7 +108,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"},
@@ -120,7 +120,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"},
@@ -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"},
@@ -289,7 +289,6 @@ layout("/layouts/platform.html"){
return {
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "立案编号", prop: "caseFilingCode"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案类别", prop: "typeName"},
{label: "立案结果", prop: "caseFilingResult"},
@@ -0,0 +1,82 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<template>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="员工信息">
<el-input @keyup.enter.native="doSearch" clearable placeholder="请输入工号或姓名" v-model="pageForm.searchKeyword"></el-input>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool :app="this" label="华住员工管理">
<el-button @click="syncSelected" icon="el-icon-refresh" size="small" type="primary">同步选中员工</el-button>
</table-tool>
<el-table :data="tableData" :size="tableSize" @selection-change="selectionChange" class="vi-table" v-loading="tableLoading">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column :index="indexMethod" label="序号" type="index" width="80"></el-table-column>
<el-table-column label="工号" min-width="140" prop="loginname"></el-table-column>
<el-table-column label="姓名" min-width="120" prop="username"></el-table-column>
<el-table-column label="单位" min-width="180" prop="unitName" show-overflow-tooltip></el-table-column>
<el-table-column fixed="right" label="操作" width="130">
<template slot-scope="{row}"><el-button @click="queryStatus(row)" size="mini" type="primary">查询状态</el-button></template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog :visible.sync="statusDialogVisible" title="华住同步状态" width="520px">
<el-descriptions :column="1" border size="small" v-if="statusData">
<el-descriptions-item label="状态码">{{statusData.statusCode || "--"}}</el-descriptions-item>
<el-descriptions-item label="工号">{{statusData.userNumber || "--"}}</el-descriptions-item>
<el-descriptions-item label="姓名">{{statusData.userName || "--"}}</el-descriptions-item>
<el-descriptions-item label="部门">{{statusData.userDep || "--"}}</el-descriptions-item>
<el-descriptions-item label="成本中心">{{statusData.costCenter || "--"}}</el-descriptions-item>
<el-descriptions-item label="职级">{{statusData.rank || "--"}}</el-descriptions-item>
<el-descriptions-item label="直属领导">{{statusData.leader || "--"}}</el-descriptions-item>
<el-descriptions-item label="邮箱">{{statusData.email || "--"}}</el-descriptions-item>
</el-descriptions>
</el-dialog>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data: function () { return {pageForm: {searchKeyword: "", pageNumber: 1, pageSize: 10, totalCount: 0}, selectedRows: [], statusDialogVisible: false, statusData: null} },
methods: {
selectionChange: function (rows) { this.$set(this, "selectedRows", rows) },
syncSelected: function () {
var self = this;
if (!this.selectedRows.length) { this.$message.warning("请先选择需要同步的员工"); return }
this.$confirm("确定同步选中的 " + this.selectedRows.length + " 名员工到华住吗?", "提示", {type: "warning"}).then(function () {
self.$set(self, "tableLoading", true);
return $.post(loc() + "/syncUsers", {userIds: self.selectedRows.map(function (row) { return row.id })})
}).then(function (res) {
if (res && res.code === 0) {
var failed = (res.data || []).filter(function (item) { return !item.success });
self.$message.success("同步完成,成功 " + ((res.data || []).length - failed.length) + " 人,失败 " + failed.length + " 人");
if (failed.length) { self.$alert(failed.map(function (item) { return item.userName + "" + item.loginName + "):" + item.message }).join("\n"), "失败明细") }
} else if (res) { self.$message.warning(res.msg || "同步失败") }
}).catch(function () {}).then(function () { self.$set(self, "tableLoading", false) })
},
queryStatus: function (row) {
var self = this;
this.$set(this, "tableLoading", true);
$.post(loc() + "/queryStatus", {userId: row.id}).then(function (res) {
if (res.code === 0) { self.$set(self, "statusData", res.data); self.$set(self, "statusDialogVisible", true) } else { self.$message.warning(res.msg || "查询失败") }
}).catch(function (xhr) {
var message = xhr && xhr.responseJSON && xhr.responseJSON.msg;
self.$message.error(message || "查询华住状态失败,请稍后重试");
}).then(function () { self.$set(self, "tableLoading", false) })
}
},
created: function () { this.pageData() }
})
</script>
<!--#
}
#-->
@@ -0,0 +1,136 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<template>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker :clearable="true" placeholder="请选择年度" style="width: 100%" type="year" v-model="pageForm.year" value-format="yyyy"></el-date-picker>
</search-item>
<search-item label="标题">
<el-input @keyup.enter.native="doSearch" clearable placeholder="请输入标题" v-model="pageForm.title"></el-input>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool :app="this">
<el-button @click="openAdd" icon="el-icon-plus" size="small" type="primary">新增</el-button>
</table-tool>
<el-table :data="tableData" :size="tableSize" class="vi-table" v-loading="tableLoading">
<el-table-column :index="indexMethod" label="序号" type="index" width="80"></el-table-column>
<el-table-column label="标题" min-width="180" prop="title" show-overflow-tooltip></el-table-column>
<el-table-column label="标签类型" prop="tagTypeName" width="130">
<template slot-scope="{row}"><el-tag v-if="row.tagTypeName" size="small">{{row.tagTypeName}}</el-tag><span v-else>--</span></template>
</el-table-column>
<el-table-column label="摘要" min-width="180" prop="summary" show-overflow-tooltip></el-table-column>
<el-table-column label="封面图" width="110">
<template slot-scope="{row}"><el-image v-if="row.cover" :preview-src-list="[row.cover]" :src="row.cover" fit="cover" style="height: 36px; width: 72px"></el-image><span v-else>--</span></template>
</el-table-column>
<el-table-column label="创建时间" width="170">
<template slot-scope="{row}">{{formatDate(row.createdAt)}}</template>
</el-table-column>
<el-table-column label="发布状态" width="100">
<template slot-scope="{row}"><span :class="row.status === 1 ? 'text-success' : 'text-muted'">{{row.statusName}}</span></template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="220">
<template slot-scope="{row}">
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
<el-button @click="changeStatus(row, row.status === 1 ? 0 : 1)" size="mini">{{row.status === 1 ? '下架' : '发布'}}</el-button>
<el-button @click="deleteRow(row)" size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</template>
<template #edit>
<el-form :model="formData" :rules="formRules" label-width="90px" ref="form" v-loading="formLoading">
<el-form-item label="标题" prop="title"><el-input maxlength="100" placeholder="请输入标题" v-model="formData.title"></el-input></el-form-item>
<el-form-item label="摘要" prop="summary"><el-input maxlength="500" placeholder="请输入摘要" rows="3" type="textarea" v-model="formData.summary"></el-input></el-form-item>
<el-form-item label="标签类型" prop="tagType">
<el-select placeholder="请先维护标签类型字典" style="width: 100%" v-model="formData.tagType"><el-option :key="item.code" :label="item.label" :value="item.code" v-for="item in dict.type.INCLUSIVE_BENEFIT_TYPE || []"></el-option></el-select>
</el-form-item>
<el-form-item label="内容" prop="content"><text-editor v-model="formData.content"></text-editor></el-form-item>
<el-form-item label="性别限制" prop="sexLimit"><el-radio-group v-model="formData.sexLimit"><el-radio :label="0">全部开放</el-radio><el-radio :label="1">男性</el-radio><el-radio :label="2">女性</el-radio></el-radio-group></el-form-item>
<el-form-item label="封面图" prop="cover">
<file-upload :upload_number="1" :value.sync="formData.cover" upload_mode="image" upload_result_category="interval" upload_result_type="url"></file-upload>
</el-form-item>
<el-form-item label="链接" prop="linkUrl"><el-input maxlength="500" placeholder="请输入外链地址" v-model="formData.linkUrl"></el-input></el-form-item>
<el-form-item><el-button @click="back">取消</el-button><el-button :loading="formLoading" @click="saveForm(0)" type="primary">保存草稿</el-button><el-button :loading="formLoading" @click="saveForm(1)" type="primary">发布</el-button></el-form-item>
</el-form>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
dicts: ["INCLUSIVE_BENEFIT_TYPE"],
mixins: [initTableMixins],
data: function () {
return {
pageForm: {year: "", title: "", pageNumber: 1, pageSize: 10, totalCount: 0},
formData: {},
formLoading: false,
formRules: {
title: [{required: true, message: "请输入标题", trigger: "blur"}],
summary: [{required: true, message: "请输入摘要", trigger: "blur"}],
tagType: [{required: true, message: "请选择标签类型", trigger: "change"}],
content: [{required: true, message: "请输入内容", trigger: "change"}],
cover: [{required: true, message: "请上传封面图", trigger: "change"}]
}
}
},
methods: {
emptyForm: function () { return {id: "", year: this.pageForm.year || new Date().getFullYear() + "", title: "", summary: "", tagType: "", content: "", sexLimit: 0, cover: "", linkUrl: ""} },
formatDate: function (value) { return value ? moment(value).format("YYYY-MM-DD HH:mm") : "--" },
back: function () { this.$refs.guava.index() },
openAdd: function () { this.$set(this, "formData", this.emptyForm()); this.$refs.guava.edit() },
openEdit: function (row) {
var self = this;
this.$set(this, "formLoading", true);
$.post(loc() + "/findOne", {id: row.id}).then(function (res) {
if (res.code === 0 && res.data) {
var formData = self.emptyForm();
Object.keys(res.data).forEach(function (key) {
self.$set(formData, key, res.data[key]);
});
if (formData.year !== null && formData.year !== undefined) {
self.$set(formData, "year", formData.year + "");
}
self.$set(self, "formData", formData);
self.$refs.guava.edit();
} else { self.$message.warning(res.msg || "未查询到数据") }
}).always(function () { self.$set(self, "formLoading", false) })
},
saveForm: function (status) {
var self = this;
this.$refs.form.validate(function (valid) {
if (!valid) { return }
self.$set(self, "formLoading", true);
self.$set(self.formData, "status", status);
$.post(loc() + "/save", self.formData).then(function (res) {
if (res.code === 0) { self.$message.success(res.msg || "保存成功"); self.$refs.guava.index(); self.doSearch() } else { self.$message.warning(res.msg) }
}).always(function () { self.$set(self, "formLoading", false) })
})
},
changeStatus: function (row, status) {
var self = this;
this.$confirm("确定要" + (status === 1 ? "发布" : "下架") + "该幸福工荟吗?", "提示", {type: "warning"}).then(function () {
return $.post(loc() + "/updateStatus", {id: row.id, status: status})
}).then(function (res) { if (res && res.code === 0) { self.$message.success(res.msg || "操作成功"); self.doSearch() } else if (res) { self.$message.warning(res.msg) } }).catch(function () {})
},
deleteRow: function (row) {
var self = this;
this.$confirm("确定要删除该幸福工荟吗?", "提示", {type: "warning"}).then(function () { return $.post(loc() + "/delete", {id: row.id}) }).then(function (res) {
if (res && res.code === 0) { self.$message.success(res.msg || "删除成功"); self.doSearch() } else if (res) { self.$message.warning(res.msg) }
}).catch(function () {})
}
},
created: function () { this.pageData() }
})
</script>
<!--#
}
#-->
@@ -0,0 +1,240 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.difficult-project-page { height: calc(100vh - 90px); }
.difficult-project-page .el-row, .difficult-project-page .el-col, .difficult-project-page .el-card { height: 100%; }
.difficult-project-page .project-list { margin-top: 10px; }
.difficult-project-page .project-item { display:flex; align-items:center; justify-content:space-between; padding:10px 12px; cursor:pointer; }
.difficult-project-page .project-item:hover, .difficult-project-page .project-item.active { background:#eef6ff; color:#1867b0; }
.difficult-project-page .project-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1; }
</style>
<div id="app" class="difficult-project-page" v-cloak>
<el-row :gutter="10">
<el-col :span="5">
<el-card shadow="never">
<el-input v-model="projectKeyword" clearable placeholder="请输入内容">
<el-button slot="append" @click="openProjectDialog()">新增补助范围</el-button>
</el-input>
<div class="project-list">
<div v-for="item in filteredProjects" :key="item.id"
:class="['project-item', selectedProject && selectedProject.id === item.id ? 'active' : '']"
@click="selectProject(item)">
<span class="project-name"><i class="el-icon-s-home"></i> {{ item.projectName }}</span>
<span>
<el-button type="text" size="mini" @click.stop="openProjectDialog(item)">编辑</el-button>
<el-button type="text" size="mini" @click.stop="deleteProject(item)">删除</el-button>
</span>
</div>
</div>
</el-card>
</el-col>
<el-col :span="19">
<el-card shadow="never">
<el-row style="height:auto">
<el-col :span="18" style="height:auto;display:flex">
<el-input v-model="diseaseKeyword" clearable placeholder="请输入名称"></el-input>
<el-button type="primary" icon="el-icon-search" style="margin-left:10px"
@click="loadDisease">查询</el-button>
</el-col>
<el-col :span="6" style="height:auto;text-align:right">
<el-button type="primary" icon="el-icon-circle-plus-outline"
:disabled="!selectedProject" @click="openDiseaseDialog()">新增子类</el-button>
</el-col>
</el-row>
<el-table :data="diseaseList" border style="width:100%;margin-top:10px" height="calc(100vh - 180px)">
<el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
<el-table-column prop="diseaseName" label="说明" align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="220" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="openDiseaseDialog(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="deleteDisease(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
<el-dialog title="补助范围" :visible.sync="projectDialogVisible" width="440px" :close-on-click-modal="false">
<el-form ref="projectFormRef" :model="projectForm" label-width="100px">
<el-form-item label="补助项目" prop="projectName"
:rules="[{required:true,message:'请输入补助项目',trigger:'blur'}]">
<el-input v-model="projectForm.projectName" maxlength="100" clearable></el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="closeProjectDialog">取消</el-button>
<el-button type="primary" @click="saveProject">确定</el-button>
</div>
</el-dialog>
<el-dialog :title="diseaseForm.id ? '编辑' : '新增'" :visible.sync="diseaseDialogVisible"
width="60%" :close-on-click-modal="false">
<el-form ref="diseaseFormRef" :model="diseaseForm" label-width="100px">
<el-form-item label="说明" prop="diseaseName"
:rules="[{required:true,message:'请输入说明',trigger:'blur'}]">
<el-input v-model="diseaseForm.diseaseName" type="textarea" :rows="4"
maxlength="1000" show-word-limit></el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="closeDiseaseDialog">取消</el-button>
<el-button type="primary" @click="saveDisease">确定</el-button>
</div>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data: function () {
return {
projectList: [],
diseaseList: [],
selectedProject: null,
projectKeyword: "",
diseaseKeyword: "",
projectDialogVisible: false,
diseaseDialogVisible: false,
projectForm: {},
diseaseForm: {}
};
},
computed: {
filteredProjects: function () {
var keyword = this.projectKeyword;
return this.projectList.filter(function (item) {
return !keyword || item.projectName.indexOf(keyword) >= 0;
});
}
},
methods: {
loadProject: function () {
var self = this;
return this.$axios.post("/platform/difficultSubsidy/basicSetting/project/projectList").then(function (res) {
if (res.code === 0) {
self.$set(self, "projectList", res.data || []);
if (!self.selectedProject && self.projectList.length > 0) {
self.selectProject(self.projectList[0]);
} else if (self.selectedProject) {
var selected = self.projectList.find(function (item) {
return item.id === self.selectedProject.id;
});
self.$set(self, "selectedProject", selected || null);
}
}
});
},
selectProject: function (project) {
this.$set(this, "selectedProject", project);
this.$set(this, "diseaseKeyword", "");
this.loadDisease();
},
loadDisease: function () {
var self = this;
if (!this.selectedProject) {
this.$set(this, "diseaseList", []);
return;
}
this.$axios.post("/platform/difficultSubsidy/basicSetting/project/diseaseList", {
projectId: this.selectedProject.id,
keyword: this.diseaseKeyword
}).then(function (res) {
if (res.code === 0) {
self.$set(self, "diseaseList", res.data || []);
}
});
},
openProjectDialog: function (row) {
this.$set(this, "projectForm", row ? {id: row.id, projectName: row.projectName} : {});
this.$set(this, "projectDialogVisible", true);
this.$nextTick(function () {
this.$refs.projectFormRef.clearValidate();
});
},
closeProjectDialog: function () {
this.$set(this, "projectDialogVisible", false);
},
saveProject: function () {
var self = this;
this.$refs.projectFormRef.validate(function (valid) {
if (!valid) {
return;
}
self.$axios.post("/platform/difficultSubsidy/basicSetting/project/saveProject", {
data: JSON.stringify(self.projectForm)
}).then(function (res) {
if (res.code === 0) {
self.$message.success("保存成功");
self.$set(self, "projectDialogVisible", false);
self.loadProject();
}
});
});
},
deleteProject: function (row) {
var self = this;
this.$confirm("确定删除该补助范围吗?", "提示", {type: "warning"}).then(function () {
self.$axios.post("/platform/difficultSubsidy/basicSetting/project/deleteProject", {id: row.id}).then(function (res) {
if (res.code === 0) {
self.$message.success("删除成功");
if (self.selectedProject && self.selectedProject.id === row.id) {
self.$set(self, "selectedProject", null);
self.$set(self, "diseaseList", []);
}
self.loadProject();
}
});
});
},
openDiseaseDialog: function (row) {
var data = row ? {id: row.id, projectId: row.projectId, diseaseName: row.diseaseName,
subsidyAmountUpperLimit: row.subsidyAmountUpperLimit} : {projectId: this.selectedProject.id};
this.$set(this, "diseaseForm", data);
this.$set(this, "diseaseDialogVisible", true);
this.$nextTick(function () {
this.$refs.diseaseFormRef.clearValidate();
});
},
closeDiseaseDialog: function () {
this.$set(this, "diseaseDialogVisible", false);
},
saveDisease: function () {
var self = this;
this.$refs.diseaseFormRef.validate(function (valid) {
if (!valid) {
return;
}
self.$axios.post("/platform/difficultSubsidy/basicSetting/project/saveDisease", {
data: JSON.stringify(self.diseaseForm)
}).then(function (res) {
if (res.code === 0) {
self.$message.success("保存成功");
self.$set(self, "diseaseDialogVisible", false);
self.loadDisease();
self.loadProject();
}
});
});
},
deleteDisease: function (row) {
var self = this;
this.$confirm("确定删除该说明吗?", "提示", {type: "warning"}).then(function () {
self.$axios.post("/platform/difficultSubsidy/basicSetting/project/deleteDisease", {id: row.id}).then(function (res) {
if (res.code === 0) {
self.$message.success("删除成功");
self.loadDisease();
self.loadProject();
}
});
});
}
},
created: function () {
this.loadProject();
}
});
</script>
<!--#
}
#-->
@@ -0,0 +1,454 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.difficult-apply .el-form-item { margin-bottom: 0; }
.difficult-apply .el-descriptions-item__label { min-width: 150px; }
.difficult-apply .required-label:before { content: "*"; color: #f56c6c; margin-right: 4px; }
.difficult-apply .proof-help { line-height: 2; color: #7b8794; }
.difficult-apply .nowrap-select .el-select__tags { flex-wrap: nowrap; overflow: hidden; }
</style>
<div id="app" class="difficult-apply" v-cloak>
<el-card shadow="never">
<snaker-start slot="header" label="特困补助申请" define_key="TKBZ"></snaker-start>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="0"
label-suffix="" class="flow-task-form">
<el-descriptions :column="3" border>
<el-descriptions-item label="申请人姓名/工号">
<el-form-item prop="applyUserName">
{{ formData.applyUserName }}{{ formData.applyLoginName }}
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="姓名">
<el-form-item prop="userId" :rules="requiredRule('请选择申请补助的教职工')">
<el-select v-if="!isSchoolAuditEdit" v-model="formData.userId" filterable remote reserve-keyword
:remote-method="queryRecipients" :loading="userLoading"
placeholder="请输入姓名或工号查找" style="width:100%"
@change="userChange">
<el-option v-for="item in userOptions" :key="item.id"
:label="item.username + '' + item.loginname + ''"
:value="item.id"></el-option>
</el-select>
<el-input v-else :value="formData.userName + '' + formData.loginName + ''" readonly></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="性别">
<el-radio-group v-model="formData.sex" disabled>
<el-radio border label="男"></el-radio>
<el-radio border label="女"></el-radio>
</el-radio-group>
</el-descriptions-item>
<el-descriptions-item>
<span slot="label">年龄</span>
<el-form-item prop="age" :rules="requiredRule('请填写年龄')">
<el-input-number v-model="formData.age" :min="0" :max="120"></el-input-number>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="所在部门">
<el-input v-model="formData.unitName" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<span slot="label">联系电话</span>
<el-form-item prop="mobile" :rules="requiredRule('请填写联系电话')">
<el-input v-model="formData.mobile" maxlength="30"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<span slot="label">申请补助类别</span>
<el-form-item prop="subsidyType" :rules="requiredRule('请选择申请补助类别')">
<el-checkbox-group v-model="formData.subsidyType" @change="subsidyTypeChange">
<el-checkbox v-for="item in projectList" :key="item.id" :label="item.id">
{{ item.projectName }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<span slot="label" class="required-label">住院记录</span>
<el-table :data="formData.hospitalRecords" border style="width:100%">
<el-table-column label="就诊医院" min-width="130">
<template slot-scope="scope">
<el-form-item :prop="'hospitalRecords.' + scope.$index + '.hospitalName'"
:rules="requiredRule('必填')">
<el-input v-model="scope.row.hospitalName"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="入院时间" width="145">
<template slot-scope="scope">
<el-form-item :prop="'hospitalRecords.' + scope.$index + '.startDate'"
:rules="conditionalRule(isHospitalCare(scope.row))">
<el-date-picker v-model="scope.row.startDate" type="date" value-format="yyyy-MM-dd"
:disabled="!isHospitalCare(scope.row)"
style="width:100%"></el-date-picker>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="出院时间" width="145">
<template slot-scope="scope">
<el-form-item :prop="'hospitalRecords.' + scope.$index + '.endDate'"
:rules="conditionalRule(isHospitalCare(scope.row))">
<el-date-picker v-model="scope.row.endDate" type="date" value-format="yyyy-MM-dd"
:disabled="!isHospitalCare(scope.row)"
style="width:100%"></el-date-picker>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="自费金额" width="130">
<template slot-scope="scope">
<el-form-item :prop="'hospitalRecords.' + scope.$index + '.selfFundedAmount'"
:rules="conditionalRule(isAccidentSubsidy(scope.row))">
<el-input-number v-model="scope.row.selfFundedAmount" :min="0" :precision="2"
:disabled="!isAccidentSubsidy(scope.row)"
style="width:100%"></el-input-number>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="补助类型" min-width="165">
<template slot-scope="scope">
<el-form-item :prop="'hospitalRecords.' + scope.$index + '.subsidyType'"
:rules="requiredRule('必填')">
<el-select v-model="scope.row.subsidyType" class="nowrap-select"
style="width:100%" @change="hospitalTypeChange(scope.row)">
<el-option v-for="item in selectedProjects" :key="item.id"
:label="item.projectName" :value="item.id"></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="疾病详情" min-width="210">
<template slot-scope="scope">
<el-form-item :prop="'hospitalRecords.' + scope.$index + '.diseaseDetail'"
:rules="requiredRule('必填')">
<el-select v-model="scope.row.diseaseDetail" multiple collapse-tags
class="nowrap-select" style="width:100%">
<el-option v-for="item in diseasesByProject(scope.row.subsidyType)"
:key="item.id" :label="item.diseaseName"
:value="item.id"></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="备注" min-width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.notes" maxlength="100"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="145" fixed="right">
<template slot="header" slot-scope="scope">
<el-button size="mini" type="primary" icon="el-icon-plus"
@click="addHospitalRecord">增加记录</el-button>
</template>
<template slot-scope="scope">
<el-button size="mini" type="danger" icon="el-icon-delete"
@click="removeHospitalRecord(scope.$index)"></el-button>
</template>
</el-table-column>
</el-table>
</el-descriptions-item>
<el-descriptions-item :span="3">
<span slot="label">申请补助原因</span>
<el-form-item prop="reason" :rules="requiredRule('请填写申请补助原因')">
<el-input v-model="formData.reason" type="textarea" :rows="5"
maxlength="2000" show-word-limit></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<span slot="label">纸质申请表</span>
<el-form-item prop="applyFile" :rules="requiredRule('请上传纸质申请表')">
<file-upload :value.sync="formData.applyFile" upload_mode="drag"
:upload_number="1" upload_result_category="array"
complete_result></file-upload>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<span slot="label">证明材料</span>
<el-form-item prop="files" :rules="requiredRule('请上传证明材料')">
<el-alert :closable="false" type="info" class="proof-help">
<div>申请补助范围第1—3项的教职工需提交材料:</div>
<div style="text-indent:2em">1. 医院疾病证明;</div>
<div style="text-indent:2em">
2. 住院证明等相关就医辅助材料,其中住院15天及以下天数按半个月计算,住院16天至30天按一个月计算,每次出院及住院为一个时间段计算天数。
</div>
<div>申请补助范围第4项重大变故项目的教职工需提交材料:</div>
<div style="text-indent:2em">
1. 遭受自然灾害需提交当地消防部门、公安部门及社区的相关证明材料,相关财产损失评估报告;
</div>
<div style="text-indent:2em">
2. 遭受意外伤害需提交公安、消防、交通等相关部门证明材料,医院疾病证明和住院证明,保险、医保结算清单复印件;
</div>
<div style="text-indent:2em">
3. 教职工直系亲属患严重疾病的需提交《户口薄》复印件,医院疾病证明,住院和治疗结算清单复印件,保险、医保结算清单复印件。
</div>
</el-alert>
<file-upload :value.sync="formData.files" upload_mode="drag"
:upload_number="25" upload_result_category="array"
complete_result></file-upload>
</el-form-item>
</el-descriptions-item>
</el-descriptions>
</el-form>
<el-row type="flex" justify="end" class="mt20">
<el-button v-if="!isSchoolAuditEdit" type="primary" plain @click="onSave">保存</el-button>
<el-button type="primary" @click="onSubmit">提交</el-button>
</el-row>
</el-card>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
store: store,
data: function () {
return {
bizId: GetQueryString("bizId") || GetQueryString("id"),
taskId: GetQueryString("taskId"),
isSchoolAuditEdit: GetQueryString("schoolAuditEdit") === "1",
formData: {
id: GetQueryString("bizId") || GetQueryString("id"),
subsidyType: [],
hospitalRecords: [],
applyFile: [],
files: []
},
formRules: {
applyUserName: [{required: true, message: "申请人不能为空", trigger: ["blur", "change"]}],
userId: [{required: true, message: "请选择申请补助的教职工", trigger: ["blur", "change"]}],
age: [{required: true, message: "请填写年龄", trigger: ["blur", "change"]}],
mobile: [{required: true, message: "请填写联系电话", trigger: ["blur", "change"]}],
subsidyType: [{required: true, type: "array", min: 1, message: "请选择申请补助类别", trigger: "change"}],
reason: [{required: true, message: "请填写申请补助原因", trigger: ["blur", "change"]}],
applyFile: [{required: true, type: "array", min: 1, message: "请上传纸质申请表", trigger: "change"}],
files: [{required: true, type: "array", min: 1, message: "请上传证明材料", trigger: "change"}]
},
projectList: [],
diseaseList: [],
userOptions: [],
userLoading: false
};
},
computed: {
selectedProjects: function () {
var selected = this.formData.subsidyType || [];
return this.projectList.filter(function (item) {
return selected.indexOf(item.id) >= 0;
});
}
},
methods: {
requiredRule: function (message) {
return [{required: true, message: message, trigger: ["change", "blur"]}];
},
conditionalRule: function (required) {
return [{required: required, message: "必填", trigger: ["change", "blur"]}];
},
loadProject: function () {
var self = this;
return this.$axios.post("/platform/difficultSubsidy/common/projectInfo").then(function (res) {
if (res.code === 0) {
self.$set(self, "projectList", res.data.projectList || []);
self.$set(self, "diseaseList", res.data.diseaseList || []);
}
});
},
loadDetail: function () {
var self = this;
var id = this.bizId;
if (!id) {
this.initCurrentUser();
return;
}
var detailUrl = this.isSchoolAuditEdit
? "/platform/difficultSubsidy/common/findOne"
: "/platform/difficultSubsidy/apply/findOne";
this.$axios.post(detailUrl, {id: id}).then(function (res) {
if (res.code === 0) {
var data = res.data || {};
self.ensureCollections(data);
self.$set(self, "formData", data);
if (!self.isSchoolAuditEdit) {
self.queryRecipients(data.loginName, false);
}
}
});
},
initCurrentUser: function () {
var user = this.$store.state.user;
if (!user) {
this.$message.warning("当前登录人信息尚未加载,请刷新页面后重试");
return;
}
this.$set(this.formData, "applyUserId", user.id);
this.$set(this.formData, "applyUserName", user.username);
this.$set(this.formData, "applyLoginName", user.loginname);
this.$set(this, "userOptions", [user]);
this.$set(this.formData, "userId", user.id);
this.setSelectedUser(user);
},
ensureCollections: function (data) {
if (!data.subsidyType) {
this.$set(data, "subsidyType", []);
}
if (!data.hospitalRecords) {
this.$set(data, "hospitalRecords", []);
}
if (!data.applyFile) {
this.$set(data, "applyFile", []);
}
if (!data.files) {
this.$set(data, "files", []);
}
},
queryRecipients: function (keyword, selectFirst) {
var self = this;
if (!keyword) {
this.$set(this, "userOptions", []);
return;
}
this.$set(this, "userLoading", true);
this.$axios.post("/platform/difficultSubsidy/apply/queryRecipients", {key: keyword}).then(function (res) {
var users = res.code === 0 ? (res.data || []) : [];
self.$set(self, "userOptions", users);
self.$set(self, "userLoading", false);
if (selectFirst && users.length > 0) {
self.$set(self.formData, "userId", users[0].id);
self.setSelectedUser(users[0]);
}
}).catch(function () {
self.$set(self, "userLoading", false);
});
},
userChange: function (userId) {
var user = this.userOptions.find(function (item) {
return item.id === userId;
});
if (user) {
this.setSelectedUser(user);
}
},
setSelectedUser: function (user) {
var unit = user.unit || {};
var union = user.union || {};
this.$set(this.formData, "userName", user.username);
this.$set(this.formData, "loginName", user.loginname);
this.$set(this.formData, "sex", user.sex);
this.$set(this.formData, "unitId", user.unitId || user.unitid || unit.id || "");
this.$set(this.formData, "unitName", user.unitName || user.unitname || unit.name || "");
this.$set(this.formData, "unionId", user.unionId || user.unionid || union.id || "");
this.$set(this.formData, "unionName", user.unionName || user.unionname || union.name || "");
this.$set(this.formData, "mobile", user.mobile || "");
if (user.birthday) {
this.$set(this.formData, "age", this.$moment().diff(user.birthday, "years"));
}
},
subsidyTypeChange: function (values) {
var self = this;
values.forEach(function (projectId) {
var exists = self.formData.hospitalRecords.some(function (record) {
return record.subsidyType === projectId;
});
if (!exists) {
self.formData.hospitalRecords.push({subsidyType: projectId, diseaseDetail: []});
}
});
var records = this.formData.hospitalRecords.filter(function (record) {
return values.indexOf(record.subsidyType) >= 0;
});
this.$set(this.formData, "hospitalRecords", records);
},
diseasesByProject: function (projectId) {
return this.diseaseList.filter(function (item) {
return item.projectId === projectId;
});
},
projectName: function (projectId) {
var project = this.projectList.find(function (item) {
return item.id === projectId;
});
return project ? project.projectName : "";
},
isHospitalCare: function (row) {
return this.projectName(row.subsidyType).indexOf("住院护理") >= 0;
},
isAccidentSubsidy: function (row) {
return this.projectName(row.subsidyType).indexOf("重大变故") >= 0;
},
hospitalTypeChange: function (row) {
this.$set(row, "diseaseDetail", []);
if (!this.isHospitalCare(row)) {
this.$set(row, "startDate", null);
this.$set(row, "endDate", null);
}
if (!this.isAccidentSubsidy(row)) {
this.$set(row, "selfFundedAmount", null);
}
},
addHospitalRecord: function () {
this.formData.hospitalRecords.push({diseaseDetail: []});
},
removeHospitalRecord: function (index) {
this.formData.hospitalRecords.splice(index, 1);
},
onSave: function () {
var self = this;
this.$confirm("您确定保存吗?", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"
}).then(function () {
self.$axios.post("/platform/difficultSubsidy/apply/save", {
data: JSON.stringify(self.formData)
}).then(function (res) {
if (res.code === 0) {
self.$message.success("保存成功");
commonUtil.pjaxPush("/platform/difficultSubsidy/mine");
}
});
});
},
onSubmit: function () {
var self = this;
this.$refs.formRef.validate(function (valid) {
if (!valid) {
return;
}
if (!self.formData.hospitalRecords || self.formData.hospitalRecords.length === 0) {
self.$message.warning("请填写住院记录");
return;
}
self.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"
}).then(function () {
if (self.isSchoolAuditEdit) {
self.$axios.post("/platform/difficultSubsidy/schoolAudit/modifyApply", {
processTaskId: self.taskId,
data: JSON.stringify(self.formData)
}).then(function (res) {
if (res.code === 0) {
self.$message.success("修改成功");
commonUtil.pjaxPush("/platform/difficultSubsidy/schoolAudit");
}
});
return;
}
self.$axios.post("/platform/difficultSubsidy/apply/submit", {
data: JSON.stringify(self.formData)
}).then(function (res) {
if (res.code === 0) {
self.$message.success("提交成功");
commonUtil.pjaxPush("/platform/difficultSubsidy/mine");
}
});
});
});
}
},
created: function () {
var self = this;
this.loadProject().then(function () {
self.loadDetail();
});
}
});
</script>
<!--#
}
#-->
@@ -0,0 +1,166 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="申请年份">
<el-date-picker v-model="pageForm.year" type="year" value-format="yyyy"
clearable placeholder="选择申请年份" style="width:100%"></el-date-picker>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="我的申请">
<el-button type="primary" size="small" icon="el-icon-plus" @click="newApply">发起申请</el-button>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width:100%">
<el-table-column :index="indexMethod" type="index" label="序号" width="65"></el-table-column>
<el-table-column prop="userName" label="姓名" width="110"></el-table-column>
<el-table-column prop="loginName" label="工号" width="130"></el-table-column>
<el-table-column prop="unitName" label="所属单位" min-width="180" show-overflow-tooltip></el-table-column>
<el-table-column label="申请补助类别" min-width="220" show-overflow-tooltip>
<template slot-scope="scope">{{ subsidyTypeNames(scope.row.subsidyType) }}</template>
</el-table-column>
<el-table-column prop="applyDate" label="申请时间" width="170"></el-table-column>
<el-table-column prop="currentTaskName" label="当前环节" width="150"></el-table-column>
<el-table-column label="状态" width="120">
<template slot-scope="scope">
<el-tag v-if="!scope.row.instanceId" type="info" size="small">待提交</el-tag>
<enum-tag v-else :value="scope.row.instanceState" name="ProcessInstanceStateEnum"
label_key="message" size="small"></enum-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="360" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="viewDetail(scope.row)">查看</el-button>
<el-button v-if="canEdit(scope.row)" type="primary" size="mini" @click="editApply(scope.row)">编辑</el-button>
<el-button v-if="canRevoke(scope.row)" type="danger" size="mini" @click="revoke(scope.row)">撤回</el-button>
<el-button type="primary" size="mini" @click="exportApply(scope.row)">导出</el-button>
<el-button v-if="canEdit(scope.row)" type="danger" size="mini" @click="deleteApplication(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #view>
<difficult-subsidy-info ref="detailRef"></difficult-subsidy-info>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store: store,
mixins: [initTableMixins],
components: {
"difficult-subsidy-info": httpVueLoader("/components/zhgh/difficultSubsidy/ProcessInfo.vue")
},
data: function () {
return {
pageDataUrl: "/platform/difficultSubsidy/mine/pageData",
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
pageOrderName: "",
pageOrderBy: "",
year: ""
},
projectList: []
};
},
methods: {
loadProject: function () {
var self = this;
this.$axios.post("/platform/difficultSubsidy/common/projectInfo").then(function (res) {
if (res.code === 0) {
self.$set(self, "projectList", res.data.projectList || []);
}
});
},
subsidyTypeNames: function (ids) {
var selected = ids || [];
return this.projectList.filter(function (item) {
return selected.indexOf(item.id) >= 0;
}).map(function (item) {
return item.projectName;
}).join("、");
},
canEdit: function (row) {
return !row.instanceId || !!row.returnedStartTaskId;
},
canRevoke: function (row) {
return !!row.instanceId && row.instanceState === 10 && !!row.startTaskId && !row.returnedStartTaskId;
},
newApply: function () {
commonUtil.pjaxPush("/platform/difficultSubsidy/apply");
},
editApply: function (row) {
commonUtil.pjaxPush("/platform/difficultSubsidy/apply?taskId="
+ (row.returnedStartTaskId || "") + "&bizId=" + row.id);
},
viewDetail: function (row) {
var self = this;
this.$refs.guava.view(function () {
self.openDetailWhenReady(row, 0);
});
},
openDetailWhenReady: function (row, retryCount) {
var self = this;
this.$nextTick(function () {
if (self.$refs.detailRef) {
self.$refs.detailRef.open(row.id);
return;
}
// httpVueLoader 首次进入时可能尚未挂载组件,短暂重试后再传入申请ID。
if (retryCount < 20) {
setTimeout(function () {
self.openDetailWhenReady(row, retryCount + 1);
}, 50);
} else {
self.$message.error("详情组件加载失败,请刷新后重试");
}
});
},
revoke: function (row) {
var self = this;
this.$confirm("确定撤回该申请吗?撤回后可以修改并重新提交。", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"
}).then(function () {
self.$axios.post("/flow/common/revokeTask", {taskId: row.startTaskId}).then(function (res) {
if (res.code === 0) {
self.$message.success("撤回成功");
self.pageData();
}
});
});
},
deleteApplication: function (row) {
var self = this;
this.$confirm("确定删除该申请吗?申请记录、审核记录及相关待办将一并永久删除。", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"
}).then(function () {
self.$axios.post("/platform/difficultSubsidy/mine/delete", {id: row.id}).then(function (res) {
if (res.code === 0) {
self.$message.success("删除成功");
self.pageData();
}
});
});
},
exportApply: function (row) {
this.$downLoad("/platform/difficultSubsidy/mine/exportApplyInfo?id=" + row.id);
}
},
created: function () {
this.loadProject();
this.pageData();
}
});
</script>
<!--#
}
#-->
@@ -0,0 +1,494 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="申请年份">
<el-date-picker v-model="pageForm.year" type="year" value-format="yyyy"
clearable placeholder="选择申请年份" style="width:100%"></el-date-picker>
</search-item>
<search-item label="申请月份">
<el-date-picker class="difficult-subsidy-month-range" v-model="monthRange" type="monthrange" value-format="yyyy-MM"
clearable unlink-panels range-separator="至" start-placeholder="开始月份"
end-placeholder="结束月份" style="width:100%"
@change="monthRangeChange"></el-date-picker>
</search-item>
<search-item label="姓名工号">
<el-input v-model="pageForm.searchKeyword" clearable maxlength="30"></el-input>
</search-item>
<search-item label="所属工会">
<el-select v-model="pageForm.unionId" clearable placeholder="所属工会" style="width:100%"
@change="flushUnits">
<el-option v-for="item in unions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="所属单位">
<el-select v-model="pageForm.unitId" clearable filterable placeholder="所属单位" style="width:100%">
<el-option v-for="item in units" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="特困补助校工会审核">
<el-button type="primary" size="small" style="margin-right:10px" @click="exportZip">导出审核材料</el-button>
<el-radio-group v-model="pageForm.isAudit" size="small" @change="doSearch">
<el-radio-button :label="0">全部</el-radio-button>
<el-radio-button :label="1">已审核</el-radio-button>
<el-radio-button :label="-1">未审核</el-radio-button>
</el-radio-group>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width:100%">
<el-table-column :index="indexMethod" type="index" label="序号" width="65"></el-table-column>
<el-table-column prop="loginName" label="工号" width="130"></el-table-column>
<el-table-column prop="userName" label="姓名" width="110"></el-table-column>
<el-table-column prop="unitName" label="所属单位" min-width="170" show-overflow-tooltip></el-table-column>
<el-table-column prop="unionName" label="所属工会" min-width="160" show-overflow-tooltip></el-table-column>
<el-table-column label="申请补助类别" min-width="220" show-overflow-tooltip>
<template slot-scope="scope">{{ subsidyTypeNames(scope.row.subsidyType) }}</template>
</el-table-column>
<el-table-column prop="applyDate" label="申请时间" width="170"></el-table-column>
<el-table-column prop="currentTaskName" label="当前环节" width="140"></el-table-column>
<el-table-column label="操作" width="370" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="viewDetail(scope.row)">查看</el-button>
<el-button v-if="scope.row.taskState === 10" type="primary" size="mini"
@click="openAudit(scope.row)">审核</el-button>
<el-button v-if="scope.row.taskState === 20" type="danger" size="mini"
@click="revoke(scope.row)">撤销</el-button>
<el-button v-if="scope.row.stateId === 7070" type="warning" size="mini"
@click="openAmount(scope.row)">修改金额</el-button>
<el-button v-if="scope.row.taskState === 10" type="primary" size="mini"
@click="editApply(scope.row)">修改</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #view>
<difficult-subsidy-info ref="detailRef">
<div v-if="auditMode" style="margin-top:20px">
<div class="process-title">校工会审核</div>
<el-form ref="auditFormRef" :model="auditForm" class="school-audit-form">
<el-descriptions :column="2" border class="flow-task-form">
<el-descriptions-item label="审核人">
<el-input :value="$store.state.user.username" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item label="审核时间">
<el-input :value="auditForm.auditDate" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item label="审核意见" :span="2">
<el-form-item prop="opinion"
:rules="[{required:true,message:'请填写审核意见',trigger:['blur','change']}]">
<el-input v-model="auditForm.opinion" type="textarea" :rows="3"
maxlength="1000" show-word-limit></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="补助项目" :span="2">
<el-table :data="moneyData.hospitalRecords || []" border style="width:100%">
<el-table-column label="就诊医院" prop="hospitalName" min-width="120"></el-table-column>
<el-table-column label="入院时间" prop="startDate" width="110"></el-table-column>
<el-table-column label="出院时间" prop="endDate" width="110"></el-table-column>
<el-table-column label="自费金额(元)" prop="selfFundedAmount" width="120"></el-table-column>
<el-table-column label="补助类型" min-width="150">
<template slot-scope="scope">{{ projectName(scope.row.subsidyType) }}</template>
</el-table-column>
<el-table-column label="疾病详情" min-width="180" show-overflow-tooltip>
<template slot-scope="scope">{{ diseaseNames(scope.row.diseaseDetail) }}</template>
</el-table-column>
<el-table-column label="备注" prop="notes" min-width="110" show-overflow-tooltip></el-table-column>
<el-table-column label="是否通过" width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.isPass === true" type="success" size="small">通过</el-tag>
<el-tag v-else-if="scope.row.isPass === false" type="danger" size="small">拒绝</el-tag>
<el-tag v-else type="info" size="small">待审核</el-tag>
</template>
</el-table-column>
<el-table-column label="补助金额" width="110">
<template slot-scope="scope">{{ recordSubsidyMoney(scope.row) }}</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right">
<template slot-scope="scope">
<el-button :type="scope.row.isPass === true ? 'primary' : 'default'" size="mini"
@click="setRecordPass(scope.row, true)">通过</el-button>
<el-button :type="scope.row.isPass === false ? 'danger' : 'default'" size="mini"
@click="setRecordPass(scope.row, false)">拒绝</el-button>
</template>
</el-table-column>
</el-table>
</el-descriptions-item>
<el-descriptions-item label="历史申请记录" :span="2">
<el-table v-if="moneyData.flatHistoryRecords && moneyData.flatHistoryRecords.length"
:data="moneyData.flatHistoryRecords" border style="width:100%">
<el-table-column label="就诊医院" prop="hospitalName" min-width="130"></el-table-column>
<el-table-column label="入院时间" prop="startDate" width="110"></el-table-column>
<el-table-column label="出院时间" prop="endDate" width="110"></el-table-column>
<el-table-column label="自费金额(元)" prop="selfFundedAmount" width="120"></el-table-column>
<el-table-column label="补助类型" min-width="160">
<template slot-scope="scope">{{ projectName(scope.row.subsidyType) }}</template>
</el-table-column>
<el-table-column label="疾病详情" min-width="200" show-overflow-tooltip>
<template slot-scope="scope">{{ diseaseNames(scope.row.diseaseDetail) }}</template>
</el-table-column>
<el-table-column label="补助金额" width="110">
<template slot-scope="scope">{{ recordSubsidyMoney(scope.row) }}</template>
</el-table-column>
</el-table>
<el-empty v-else description="暂无历史申请记录" :image-size="70"></el-empty>
</el-descriptions-item>
<el-descriptions-item label="预测补助金额(元)" :span="2">
<el-form-item prop="finalSubsidyAmount"
:rules="[{required:true,message:'通过时请填写预测补助金额',trigger:'change'}]">
<el-input-number v-model="auditForm.finalSubsidyAmount" :min="0" :precision="2"
style="width:180px"></el-input-number>
<span class="audit-amount-tip">(通过此项必填)</span>
</el-form-item>
</el-descriptions-item>
</el-descriptions>
</el-form>
</div>
<div v-if="auditMode" style="padding-top:20px;text-align:right">
<el-button @click="closeAudit">取消</el-button>
<el-button type="danger" @click="submitAudit(2)">拒绝</el-button>
<el-button type="info" @click="submitAudit(6)">退回</el-button>
<el-button type="primary" @click="submitAudit(1)">通过</el-button>
</div>
</difficult-subsidy-info>
</template>
<el-dialog title="修改最终补助金额" :visible.sync="amountVisible" width="460px" :close-on-click-modal="false">
<el-form label-width="130px">
<el-form-item label="最终补助金额">
<el-input-number v-model="amountForm.finalSubsidyAmount" :min="0" :precision="2"
style="width:100%"></el-input-number>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="closeAmount">取消</el-button>
<el-button type="primary" @click="saveAmount">确定</el-button>
</div>
</el-dialog>
</guava>
</div>
<style>
/* 月份范围控件在筛选栏较窄时,固定分隔符宽度,避免“至”被压缩裁切。 */
.difficult-subsidy-month-range .el-range-input {
flex: 1 1 0;
width: 0;
min-width: 0;
}
.difficult-subsidy-month-range .el-range-separator {
flex: 0 0 24px;
width: 24px;
padding: 0;
}
.school-audit-form .el-descriptions-item__label {
width: 130px;
}
.school-audit-form .el-form-item {
margin-bottom: 16px;
}
.audit-amount-tip {
color: #f56c6c;
margin-left: 8px;
}
</style>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store: store,
mixins: [initTableMixins],
components: {
"difficult-subsidy-info": httpVueLoader("/components/zhgh/difficultSubsidy/ProcessInfo.vue")
},
data: function () {
return {
pageDataUrl: "/platform/difficultSubsidy/schoolAudit/pageData",
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
pageOrderName: "",
pageOrderBy: "",
year: "",
startMonth: "",
endMonth: "",
searchKeyword: "",
unionId: "",
unitId: "",
isAudit: -1
},
monthRange: [],
unions: [],
units: [],
projectList: [],
diseaseList: [],
auditMode: false,
amountVisible: false,
moneyData: {},
auditForm: {},
amountForm: {}
};
},
methods: {
loadOptions: function () {
var self = this;
this.$businessTool.listUnion().then(function (items) {
self.$set(self, "unions", items || []);
});
this.$axios.post("/platform/difficultSubsidy/common/projectInfo").then(function (res) {
if (res.code === 0) {
self.$set(self, "projectList", res.data.projectList || []);
self.$set(self, "diseaseList", res.data.diseaseList || []);
}
});
},
flushUnits: function () {
var self = this;
this.$set(this.pageForm, "unitId", null);
this.$set(this, "units", []);
if (this.pageForm.unionId) {
this.$businessTool.listUnit(this.pageForm.unionId).then(function (items) {
self.$set(self, "units", items || []);
});
}
},
monthRangeChange: function (value) {
var months = value || [];
if (months.length === 2 && months[0].substring(0, 4) !== months[1].substring(0, 4)) {
this.$message.warning("开始月份和结束月份必须属于同一年");
this.$set(this, "monthRange", []);
this.$set(this.pageForm, "startMonth", "");
this.$set(this.pageForm, "endMonth", "");
return;
}
this.$set(this.pageForm, "startMonth", months[0] || "");
this.$set(this.pageForm, "endMonth", months[1] || "");
if (months.length === 2) {
this.$set(this.pageForm, "year", months[0].substring(0, 4));
}
},
projectName: function (id) {
var item = this.projectList.find(function (project) {
return project.id === id;
});
return item ? item.projectName : "";
},
subsidyTypeNames: function (ids) {
var self = this;
var idList = this.normalizeIdList(ids);
return idList.map(function (id) {
return self.projectName(id);
}).filter(function (name) {
return name;
}).join("、");
},
normalizeIdList: function (ids) {
if (Array.isArray(ids)) {
return ids;
}
if (typeof ids === "string" && ids) {
try {
var parsedIds = JSON.parse(ids);
return Array.isArray(parsedIds) ? parsedIds : [];
} catch (e) {
return [];
}
}
return [];
},
diseaseNames: function (ids) {
var selected = ids || [];
return this.diseaseList.filter(function (item) {
return selected.indexOf(item.id) >= 0;
}).map(function (item) {
return item.diseaseName;
}).join("、");
},
viewDetail: function (row) {
var self = this;
this.$set(this, "auditMode", false);
this.$refs.guava.view(function () {
self.openDetailWhenReady(row, 0);
});
},
editApply: function (row) {
commonUtil.pjaxPush("/platform/difficultSubsidy/schoolAudit/edit?schoolAuditEdit=1&taskId="
+ row.taskId + "&bizId=" + row.id);
},
openAudit: function (row) {
var self = this;
this.$set(this, "auditMode", true);
this.$set(this, "auditForm", {
id: row.id,
processTaskId: row.taskId,
opinion: "",
auditDate: this.$moment().format("YYYY-MM-DD"),
finalSubsidyAmount: row.finalSubsidyAmount
});
this.$axios.post("/platform/difficultSubsidy/schoolAudit/calculateSubsidy", {id: row.id}).then(function (res) {
if (res.code === 0) {
var data = res.data || {};
(data.hospitalRecords || []).forEach(function (record) {
self.$set(record, "isPass", typeof record.isPass === "boolean" ? record.isPass : null);
});
self.$set(data, "flatHistoryRecords", self.flattenHistoryRecords(data.historyHospitalRecords));
self.$set(self, "moneyData", data);
if (self.auditForm.finalSubsidyAmount === null || self.auditForm.finalSubsidyAmount === undefined) {
self.$set(self.auditForm, "finalSubsidyAmount", data.totalMoney || 0);
}
}
});
this.$refs.guava.view(function () {
self.openDetailWhenReady(row, 0);
});
},
openDetailWhenReady: function (row, retryCount) {
var self = this;
this.$nextTick(function () {
if (self.$refs.detailRef) {
self.$refs.detailRef.open(row.id);
return;
}
// httpVueLoader 首次进入时可能尚未挂载组件,短暂重试后再传入申请ID。
if (retryCount < 20) {
setTimeout(function () {
self.openDetailWhenReady(row, retryCount + 1);
}, 50);
} else {
self.$message.error("详情组件加载失败,请刷新后重试");
}
});
},
closeAudit: function () {
this.$set(this, "auditMode", false);
this.$refs.guava.index();
},
setRecordPass: function (row, pass) {
this.$set(row, "isPass", pass);
this.refreshAuditMoney();
},
recordSubsidyMoney: function (row) {
if (row.isPass === false) {
return "0.00";
}
var subsidyMoney = Number(row.subsidyMoney || 0);
var hospitalMoney = Number(row.hospitalMoney || 0);
return (subsidyMoney + hospitalMoney).toFixed(2);
},
refreshAuditMoney: function () {
var totalSubsidyMoney = 0;
var totalHospitalMoney = 0;
(this.moneyData.hospitalRecords || []).forEach(function (record) {
if (record.isPass !== false) {
totalSubsidyMoney += Number(record.subsidyMoney || 0);
totalHospitalMoney += Number(record.hospitalMoney || 0);
}
});
this.$set(this.moneyData, "totalSubsidyMoney", totalSubsidyMoney);
this.$set(this.moneyData, "totalHospitalMoney", totalHospitalMoney);
this.$set(this.moneyData, "totalMoney", totalSubsidyMoney + totalHospitalMoney);
this.$set(this.auditForm, "finalSubsidyAmount", totalSubsidyMoney + totalHospitalMoney);
},
flattenHistoryRecords: function (historyHospitalRecords) {
return (historyHospitalRecords || []).reduce(function (records, item) {
return records.concat(Array.isArray(item) ? item : []);
}, []);
},
submitAudit: function (submitType) {
var self = this;
if (!this.auditForm.opinion) {
this.$message.warning("请填写审核意见");
return;
}
if (submitType === 1 && (this.moneyData.hospitalRecords || []).some(function (record) {
return typeof record.isPass !== "boolean";
})) {
this.$message.warning("请完成每个补助项目的通过或拒绝审核");
return;
}
if (submitType === 1 && (this.auditForm.finalSubsidyAmount === null || this.auditForm.finalSubsidyAmount === undefined)) {
this.$message.warning("请填写最终补助金额");
return;
}
this.$confirm("确定提交该审核结果吗?", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"
}).then(function () {
self.$axios.post("/platform/difficultSubsidy/schoolAudit/executeTask", {
processTaskId: self.auditForm.processTaskId,
submitType: submitType,
opinion: self.auditForm.opinion,
finalSubsidyAmount: self.auditForm.finalSubsidyAmount,
hospitalRecords: JSON.stringify(self.moneyData.hospitalRecords || [])
}).then(function (res) {
if (res.code === 0) {
self.$message.success("审核完成");
self.$set(self, "auditMode", false);
self.$refs.guava.index();
self.pageData();
}
});
});
},
revoke: function (row) {
var self = this;
this.$confirm("确定撤销该审核结果吗?", "提示", {type: "warning"}).then(function () {
self.$axios.post("/platform/difficultSubsidy/schoolAudit/revoke", {processTaskId: row.taskId}).then(function (res) {
if (res.code === 0) {
self.$message.success("撤销成功");
self.pageData();
}
});
});
},
openAmount: function (row) {
this.$set(this, "amountForm", {id: row.id, finalSubsidyAmount: row.finalSubsidyAmount});
this.$set(this, "amountVisible", true);
},
closeAmount: function () {
this.$set(this, "amountVisible", false);
},
saveAmount: function () {
var self = this;
if (this.amountForm.finalSubsidyAmount === null || this.amountForm.finalSubsidyAmount === undefined) {
this.$message.warning("请填写最终补助金额");
return;
}
this.$axios.post("/platform/difficultSubsidy/schoolAudit/modifyFinalSubsidyAmount", this.amountForm).then(function (res) {
if (res.code === 0) {
self.$message.success("修改成功");
self.$set(self, "amountVisible", false);
self.pageData();
}
});
},
exportZip: function () {
var year = this.pageForm.year;
if (!year) {
this.$message.warning("请选择需要导出的申请年份");
return;
}
var query = "?year=" + encodeURIComponent(year);
if (this.pageForm.startMonth && this.pageForm.endMonth) {
query += "&startMonth=" + encodeURIComponent(this.pageForm.startMonth)
+ "&endMonth=" + encodeURIComponent(this.pageForm.endMonth);
}
this.$downLoad("/platform/difficultSubsidy/schoolAudit/exportZip" + query);
}
},
created: function () {
this.loadOptions();
this.pageData();
}
});
</script>
<!--#
}
#-->
@@ -0,0 +1,211 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="申请年份">
<el-date-picker v-model="pageForm.year" type="year" value-format="yyyy"
clearable placeholder="选择申请年份" style="width:100%"></el-date-picker>
</search-item>
<search-item label="申请月份">
<el-date-picker class="difficult-subsidy-month-range" v-model="monthRange" type="monthrange" value-format="yyyy-MM"
clearable unlink-panels range-separator="至" start-placeholder="开始月份"
end-placeholder="结束月份" style="width:100%"
@change="monthRangeChange"></el-date-picker>
</search-item>
<search-item label="姓名工号">
<el-input v-model="pageForm.searchKeyword" clearable maxlength="30"></el-input>
</search-item>
<search-item label="所属工会">
<el-select v-model="pageForm.unionId" clearable placeholder="所属工会" style="width:100%"
@change="flushUnits">
<el-option v-for="item in unions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="所属单位">
<el-select v-model="pageForm.unitId" clearable filterable placeholder="所属单位" style="width:100%">
<el-option v-for="item in units" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="特困补助查询">
<el-button type="primary" size="small" @click="exportExcel">导出汇总表 Excel</el-button>
<el-button type="primary" size="small" @click="exportWord">导出汇总表 Word</el-button>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width:100%">
<el-table-column :index="indexMethod" type="index" label="序号" width="65"></el-table-column>
<el-table-column prop="loginName" label="工号" width="130"></el-table-column>
<el-table-column prop="userName" label="姓名" width="110"></el-table-column>
<el-table-column prop="unitName" label="所属单位" min-width="180" show-overflow-tooltip></el-table-column>
<el-table-column prop="unionName" label="所属工会" min-width="160" show-overflow-tooltip></el-table-column>
<el-table-column label="申请补助类别" min-width="220" show-overflow-tooltip>
<template slot-scope="scope">{{ subsidyTypeNames(scope.row.subsidyType) }}</template>
</el-table-column>
<el-table-column prop="applyDate" label="申请时间" width="170"></el-table-column>
<el-table-column prop="subsidyAmount" label="建议补助金额" width="130"></el-table-column>
<el-table-column prop="finalSubsidyAmount" label="最终补助金额" width="130"></el-table-column>
<el-table-column label="操作" width="90" fixed="right">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="viewDetail(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #view>
<difficult-subsidy-info ref="detailRef"></difficult-subsidy-info>
</template>
</guava>
</div>
<style>
/* 月份范围控件在筛选栏较窄时,固定分隔符宽度,避免“至”被压缩裁切。 */
.difficult-subsidy-month-range .el-range-input {
flex: 1 1 0;
width: 0;
min-width: 0;
}
.difficult-subsidy-month-range .el-range-separator {
flex: 0 0 24px;
width: 24px;
padding: 0;
}
</style>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store: store,
mixins: [initTableMixins],
components: {
"difficult-subsidy-info": httpVueLoader("/components/zhgh/difficultSubsidy/ProcessInfo.vue")
},
data: function () {
return {
pageDataUrl: "/platform/difficultSubsidy/query/pageData",
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
pageOrderName: "",
pageOrderBy: "",
year: "",
startMonth: "",
endMonth: "",
searchKeyword: "",
unionId: "",
unitId: ""
},
monthRange: [],
unions: [],
units: [],
projectList: []
};
},
methods: {
loadOptions: function () {
var self = this;
this.$businessTool.listUnion().then(function (items) {
self.$set(self, "unions", items || []);
});
this.$axios.post("/platform/difficultSubsidy/common/projectInfo").then(function (res) {
if (res.code === 0) {
self.$set(self, "projectList", res.data.projectList || []);
}
});
},
flushUnits: function () {
var self = this;
this.$set(this.pageForm, "unitId", null);
this.$set(this, "units", []);
if (this.pageForm.unionId) {
this.$businessTool.listUnit(this.pageForm.unionId).then(function (items) {
self.$set(self, "units", items || []);
});
}
},
monthRangeChange: function (value) {
var months = value || [];
if (months.length === 2 && months[0].substring(0, 4) !== months[1].substring(0, 4)) {
this.$message.warning("开始月份和结束月份必须属于同一年");
this.$set(this, "monthRange", []);
this.$set(this.pageForm, "startMonth", "");
this.$set(this.pageForm, "endMonth", "");
return;
}
this.$set(this.pageForm, "startMonth", months[0] || "");
this.$set(this.pageForm, "endMonth", months[1] || "");
if (months.length === 2) {
this.$set(this.pageForm, "year", months[0].substring(0, 4));
}
},
subsidyTypeNames: function (ids) {
var selected = ids || [];
return this.projectList.filter(function (item) {
return selected.indexOf(item.id) >= 0;
}).map(function (item) {
return item.projectName;
}).join("、");
},
viewDetail: function (row) {
var self = this;
this.$refs.guava.view(function () {
self.openDetailWhenReady(row, 0);
});
},
openDetailWhenReady: function (row, retryCount) {
var self = this;
this.$nextTick(function () {
if (self.$refs.detailRef) {
self.$refs.detailRef.open(row.id);
return;
}
// httpVueLoader 首次进入时可能尚未挂载组件,短暂重试后再传入申请ID。
if (retryCount < 20) {
setTimeout(function () {
self.openDetailWhenReady(row, retryCount + 1);
}, 50);
} else {
self.$message.error("详情组件加载失败,请刷新后重试");
}
});
},
yearQuery: function () {
var year = this.pageForm.year;
if (!year) {
this.$message.warning("请选择需要导出的申请年份");
return null;
}
var query = "?year=" + encodeURIComponent(year);
if (this.pageForm.startMonth && this.pageForm.endMonth) {
query += "&startMonth=" + encodeURIComponent(this.pageForm.startMonth)
+ "&endMonth=" + encodeURIComponent(this.pageForm.endMonth);
}
return query;
},
exportExcel: function () {
var query = this.yearQuery();
if (query) {
this.$downLoad("/platform/difficultSubsidy/query/exportSummaryExcel" + query);
}
},
exportWord: function () {
var query = this.yearQuery();
if (query) {
this.$downLoad("/platform/difficultSubsidy/query/exportSummary" + query);
}
}
},
created: function () {
this.loadOptions();
this.pageData();
}
});
</script>
<!--#
}
#-->
@@ -63,7 +63,7 @@ layout("/layouts/platform.html"){
</el-checkbox>
<div style="cursor: pointer;" @click="isReadClick" class="ml5">
<div style="color: #1e88e5">
中国地质大学(武汉)教职工重大疾病互助基金管理办法
西南财经大学教职工重大疾病互助基金管理办法
</div>
</div>
</div>
@@ -137,7 +137,7 @@ layout("/layouts/platform.html"){
this.$commonUtil.previewFile({
suffix: "pdf",
downloadPath: "/platform/sys/file/download?id=orpgde84uggjmpflbob83tadan",
name: "中国地质大学(武汉)教职工重大疾病互助基金管理办法.pdf",
name: "西南财经大学教职工重大疾病互助基金管理办法.pdf",
id: "orpgde84uggjmpflbob83tadan"
})
},
@@ -0,0 +1,277 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="提案委员会立案" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<van-search v-model="pageForm.name" :show-action="false" :reverse-color="false" input-align="left"
placeholder="请输入提案名称搜索" @search="doSearch"></van-search>
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item v-model="pageForm.sessionId" :options="sessionOptions" :multiple="false"
@change="doSearch"></van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.approvalText" @change="changeApproval">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky>
<table-list api="/platform/proposal/committeeFiling/h5/pageData" :page_form.sync="pageForm" @ready="onReady"
ref="tableListRef" title="name">
<template v-slot="{index,row}">
<table-column label="提案编号">{{row.code}}</table-column>
<table-column label="提案类别">{{row.typeName}}</table-column>
<table-column label="提案人">{{row.createUserName}}</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)">
<i class="fa fa-eye"></i><span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
<i class="fa fa-edit"></i><span>立案</span>
</div>
</template>
</table-list>
<proposal-info ref="proposalInfoRef">
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<van-form ref="formRef">
<van-field v-model="formData.tf_caseFilingResultName" name="tf_caseFilingResultName" label="立案结果"
placeholder="请选择立案结果" readonly is-link @click="openResultPicker"
:rules="[{ required: true, message: '请选择立案结果' }]" required></van-field>
<van-popup v-model="showResultPicker" position="bottom">
<van-picker show-toolbar :columns="caseFilingResultOptions" @confirm="onResultConfirm"
@cancel="closeResultPicker"></van-picker>
</van-popup>
<van-field v-if="showUndertakeUnit" :value="masterUnitNames" :label="masterUnitLabel" readonly is-link
:placeholder="masterUnitLabel === '办理单位' ? '请选择办理单位' : '请选择主办单位'"
@click="openMasterUnitPicker"
:rules="[{ required: showUndertakeUnit, message: '请选择承办单位' }]" required></van-field>
<van-popup v-model="showMasterUnitPicker" position="bottom" round>
<div style="padding: 16px">
<div class="process-title">{{masterUnitLabel}}</div>
<van-checkbox-group v-model="formData.tf_masterUnitIds">
<van-cell-group>
<van-cell v-for="item in availableMasterUnits" :key="item.id" clickable :title="item.name"
@click="toggleMasterUnit(item.id)">
<template #right-icon><van-checkbox :name="item.id" @click.stop></van-checkbox></template>
</van-cell>
</van-cell-group>
</van-checkbox-group>
<van-button type="primary" block style="margin-top: 12px" @click="closeMasterUnitPicker">确定</van-button>
</div>
</van-popup>
<van-field v-if="formData.tf_caseFilingResult === 'CONFIRM_FILING'" :value="slaveUnitNames" label="协办单位"
readonly is-link placeholder="请选择协办单位" @click="openSlaveUnitPicker"></van-field>
<van-popup v-model="showSlaveUnitPicker" position="bottom" round>
<div style="padding: 16px">
<div class="process-title">协办单位</div>
<van-checkbox-group v-model="formData.tf_slaveUnitIds">
<van-cell-group>
<van-cell v-for="item in availableSlaveUnits" :key="item.id" clickable :title="item.name"
@click="toggleSlaveUnit(item.id)">
<template #right-icon><van-checkbox :name="item.id" @click.stop></van-checkbox></template>
</van-cell>
</van-cell-group>
</van-checkbox-group>
<van-button type="primary" block style="margin-top: 12px" @click="closeSlaveUnitPicker">确定</van-button>
</div>
</van-popup>
<van-field v-model="formData.tf_opinion" name="tf_opinion" label="审核意见" placeholder="请输入审核意见"
type="textarea" rows="3" maxlength="100" show-word-limit
:rules="[{ required: true, message: '请填写审核意见' }]" required></van-field>
</van-form>
<div style="display: flex; column-gap: 10px; padding: 10px">
<van-button type="danger" block @click="handleTaskAction(6)">退回提案人</van-button>
<van-button type="primary" block @click="handleTaskAction(1)">提交</van-button>
</div>
</div>
</proposal-info>
</div>
<script nonce="${cspNonce!}">
<!--#include("../../common/info.js"){}#-->
const vue = new Vue({
el: "#app",
store,
dicts: ["PROPOSAL_CASE_FILING_RESULT"],
components: {"proposal-info": PROPOSAL_INFO},
data() {
return {
pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0, name: null, sessionId: null, approvalText: "0", approval: false},
sessionOptions: [],
underTakeOptions: [],
formData: {},
showApprovalForm: false,
showResultPicker: false,
showMasterUnitPicker: false,
showSlaveUnitPicker: false
}
},
computed: {
caseFilingResultOptions() {
return (this.dict.type.PROPOSAL_CASE_FILING_RESULT || []).map(function (item) {
return {text: item.label, value: item.code}
})
},
showUndertakeUnit() {
return ["CONFIRM_FILING", "SUGGESTION"].includes(this.formData.tf_caseFilingResult)
},
masterUnitLabel() {
return this.formData.tf_caseFilingResult === "SUGGESTION" ? "办理单位" : "主办单位"
},
masterUnitNames() {
return this.getUnitNames(this.formData.tf_masterUnitIds)
},
slaveUnitNames() {
return this.getUnitNames(this.formData.tf_slaveUnitIds)
},
availableMasterUnits() {
return this.underTakeOptions.filter((item) => !this.formData.tf_slaveUnitIds.includes(item.id))
},
availableSlaveUnits() {
return this.underTakeOptions.filter((item) => !this.formData.tf_masterUnitIds.includes(item.id))
}
},
methods: {
onReady() {
this.listSession()
this.listUnderTake()
},
openResultPicker() {
this.$set(this, "showResultPicker", true)
},
closeResultPicker() {
this.$set(this, "showResultPicker", false)
},
openMasterUnitPicker() {
this.$set(this, "showMasterUnitPicker", true)
},
closeMasterUnitPicker() {
this.$set(this, "showMasterUnitPicker", false)
},
openSlaveUnitPicker() {
this.$set(this, "showSlaveUnitPicker", true)
},
closeSlaveUnitPicker() {
this.$set(this, "showSlaveUnitPicker", false)
},
listSession() {
this.$axios.post("/platform/proposal/common/listSession").then((res) => {
if (res.code === 0) {
this.$set(this, "sessionOptions", [{text: "全部届次", value: null}].concat(res.data.map(function (item) {
return {text: item.fullName, value: item.id}
})))
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].value)
this.doSearch()
}
})
},
listUnderTake() {
this.$axios.post("/platform/proposal/common/listUnderTake").then((res) => {
if (res.code === 0) this.$set(this, "underTakeOptions", res.data)
})
},
changeApproval(value) {
this.$set(this.pageForm, "approval", value === "1")
this.doSearch()
},
onView(row) {
this.$set(this, "showApprovalForm", false)
this.$refs.proposalInfoRef.onOpen(row)
},
onApproval(row) {
this.$set(this, "showApprovalForm", true)
this.$set(this, "formData", {
processTaskId: row.taskId,
proposalId: row.id,
taskName: row.taskName,
tf_caseFilingResult: "",
tf_caseFilingResultName: "",
tf_masterUnitIds: [],
tf_slaveUnitIds: [],
tf_opinion: ""
})
this.$refs.proposalInfoRef.onOpen(row)
},
onResultConfirm(value) {
this.$set(this.formData, "tf_caseFilingResult", value.value)
this.$set(this.formData, "tf_caseFilingResultName", value.text)
if (value.value === "SUGGESTION") this.$set(this.formData, "tf_slaveUnitIds", [])
if (value.value !== "CONFIRM_FILING" && value.value !== "SUGGESTION") {
this.$set(this.formData, "tf_masterUnitIds", [])
this.$set(this.formData, "tf_slaveUnitIds", [])
}
this.$set(this, "showResultPicker", false)
},
toggleMasterUnit(unitId) {
this.toggleUnit(this.formData.tf_masterUnitIds, unitId, "tf_masterUnitIds")
},
toggleSlaveUnit(unitId) {
this.toggleUnit(this.formData.tf_slaveUnitIds, unitId, "tf_slaveUnitIds")
},
toggleUnit(unitIds, unitId, fieldName) {
const nextUnitIds = unitIds.slice()
const index = nextUnitIds.indexOf(unitId)
if (index === -1) nextUnitIds.push(unitId)
else nextUnitIds.splice(index, 1)
this.$set(this.formData, fieldName, nextUnitIds)
},
getUnitNames(unitIds) {
return this.underTakeOptions.filter(function (item) {
return unitIds.includes(item.id)
}).map(function (item) {
return item.name
}).join("、")
},
buildSubmitData(submitType) {
const masterUnitIds = this.formData.tf_masterUnitIds || []
const slaveUnitIds = this.formData.tf_caseFilingResult === "CONFIRM_FILING" ? (this.formData.tf_slaveUnitIds || []) : []
return Object.assign({}, this.formData, {
tf_masterUnitIds: masterUnitIds,
tf_slaveUnitIds: slaveUnitIds,
tf_masterUnitId: masterUnitIds.length ? masterUnitIds[0] : "",
tf_masterUnitName: masterUnitIds.length ? this.getUnitName(masterUnitIds[0]) : "",
tf_masterUnitNames: masterUnitIds.map((unitId) => this.getUnitName(unitId)),
tf_masterUnitNameStr: this.getUnitNames(masterUnitIds),
tf_slaveUnitNames: slaveUnitIds.map((unitId) => this.getUnitName(unitId)),
tf_slaveUnitNameStr: this.getUnitNames(slaveUnitIds),
tf_helpunitreply: slaveUnitIds.length ? "HAS_HELP_UNIT" : "NO_HELP_UNIT",
submitType: submitType
})
},
getUnitName(unitId) {
const unit = this.underTakeOptions.find(function (item) { return item.id === unitId })
return unit ? unit.name : ""
},
async handleTaskAction(submitType) {
try {
await this.$refs.formRef.validate()
this.$dialog.confirm({title: "提示", message: "您确定要提交吗?"}).then(() => {
this.$axios.post("/flow/common/executeTask", {data: JSON.stringify(this.buildSubmitData(submitType))}).then((res) => {
if (res.code === 0) {
this.$refs.proposalInfoRef.onClose()
this.$toast.success(res.msg)
this.doSearch()
}
})
}).catch(function () {})
} catch (error) {}
},
doSearch() {
this.$nextTick(() => {
this.$set(this.pageForm, "pageNumber", 1)
this.$set(this.pageForm, "totalCount", 0)
this.$refs.tableListRef.doSearch()
})
}
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,322 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="委员会确认承办单位" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<van-search v-model="pageForm.name" :show-action="false" :reverse-color="false" input-align="left"
placeholder="请输入提案名称搜索" @search="doSearch"></van-search>
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item v-model="pageForm.sessionId" :options="sessionOptions" :multiple="false"
@change="doSearch"></van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.approvalText" @change="changeApproval">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky>
<table-list api="/platform/proposal/committeeFilingUnit/h5/pageData" :page_form.sync="pageForm" @ready="onReady"
ref="tableListRef" title="name">
<template v-slot="{index,row}">
<table-column label="提案编号">{{row.code}}</table-column>
<table-column label="提案类别">{{row.typeName}}</table-column>
<table-column label="提案人">{{row.createUserName}}</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)">
<i class="fa fa-eye"></i><span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
<i class="fa fa-edit"></i><span>确认</span>
</div>
</template>
</table-list>
<proposal-info ref="proposalInfoRef">
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<van-form ref="formRef">
<van-field v-model="formData.tf_caseFilingResultName" name="tf_caseFilingResultName" label="立案结果"
placeholder="请选择立案结果" readonly is-link @click="openResultPicker"
:rules="[{ required: true, message: '请选择立案结果' }]" required></van-field>
<van-popup v-model="showResultPicker" position="bottom">
<van-picker show-toolbar :columns="caseFilingResultOptions" @confirm="onResultConfirm"
@cancel="closeResultPicker"></van-picker>
</van-popup>
<van-field v-if="formData.tf_caseFilingResult === 'CONFIRM_FILING'" v-model="formData.tf_caseFilingCode"
name="tf_caseFilingCode" label="立案编号" placeholder="请输入立案编号" maxlength="20" show-word-limit
:rules="[{ required: true, message: '请填写立案编号' }]" required></van-field>
<van-field v-if="showUndertakeUnit" :value="masterUnitNames" :label="masterUnitLabel" readonly is-link
:placeholder="masterUnitLabel === '办理单位' ? '请选择办理单位' : '请选择主办单位'"
@click="openMasterUnitPicker"
:rules="[{ required: showUndertakeUnit, message: '请选择承办单位' }]" required></van-field>
<van-popup v-model="showMasterUnitPicker" position="bottom" round>
<div style="padding: 16px">
<div class="process-title">{{masterUnitLabel}}</div>
<van-checkbox-group v-model="formData.tf_masterUnitIds">
<van-cell-group>
<van-cell v-for="item in availableMasterUnits" :key="item.id" clickable :title="item.name"
@click="toggleMasterUnit(item.id)">
<template #right-icon><van-checkbox :name="item.id" @click.stop></van-checkbox></template>
</van-cell>
</van-cell-group>
</van-checkbox-group>
<van-button type="primary" block style="margin-top: 12px" @click="closeMasterUnitPicker">确定</van-button>
</div>
</van-popup>
<van-field v-if="formData.tf_caseFilingResult === 'CONFIRM_FILING'" :value="slaveUnitNames" label="协办单位"
readonly is-link placeholder="请选择协办单位" @click="openSlaveUnitPicker"></van-field>
<van-popup v-model="showSlaveUnitPicker" position="bottom" round>
<div style="padding: 16px">
<div class="process-title">协办单位</div>
<van-checkbox-group v-model="formData.tf_slaveUnitIds">
<van-cell-group>
<van-cell v-for="item in availableSlaveUnits" :key="item.id" clickable :title="item.name"
@click="toggleSlaveUnit(item.id)">
<template #right-icon><van-checkbox :name="item.id" @click.stop></van-checkbox></template>
</van-cell>
</van-cell-group>
</van-checkbox-group>
<van-button type="primary" block style="margin-top: 12px" @click="closeSlaveUnitPicker">确定</van-button>
</div>
</van-popup>
<van-field v-model="formData.tf_opinion" name="tf_opinion" label="审核意见" placeholder="请输入审核意见"
type="textarea" rows="3" maxlength="100" show-word-limit
:rules="[{ required: true, message: '请填写审核意见' }]" required></van-field>
</van-form>
<div style="display: flex; column-gap: 10px; padding: 10px">
<van-button block @click="closeApproval">取消</van-button>
<van-button type="primary" block @click="handleTaskAction(1)">提交</van-button>
</div>
</div>
</proposal-info>
</div>
<script nonce="${cspNonce!}">
<!--#include("../../common/info.js"){}#-->
const vue = new Vue({
el: "#app",
store,
dicts: ["PROPOSAL_CASE_FILING_RESULT"],
components: {"proposal-info": PROPOSAL_INFO},
data() {
return {
pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0, name: null, sessionId: null, approvalText: "0", approval: false},
sessionOptions: [],
underTakeOptions: [],
formData: {},
showApprovalForm: false,
showResultPicker: false,
showMasterUnitPicker: false,
showSlaveUnitPicker: false
}
},
computed: {
caseFilingResultOptions() {
return (this.dict.type.PROPOSAL_CASE_FILING_RESULT || []).map(function (item) {
return {text: item.label, value: item.code}
})
},
showUndertakeUnit() {
return ["CONFIRM_FILING", "SUGGESTION"].includes(this.formData.tf_caseFilingResult)
},
masterUnitLabel() {
return this.formData.tf_caseFilingResult === "SUGGESTION" ? "办理单位" : "主办单位"
},
masterUnitNames() {
return this.getUnitNames(this.formData.tf_masterUnitIds)
},
slaveUnitNames() {
return this.getUnitNames(this.formData.tf_slaveUnitIds)
},
availableMasterUnits() {
return this.underTakeOptions.filter((item) => !this.formData.tf_slaveUnitIds.includes(item.id))
},
availableSlaveUnits() {
return this.underTakeOptions.filter((item) => !this.formData.tf_masterUnitIds.includes(item.id))
}
},
methods: {
onReady() {
this.listSession()
this.listUnderTake()
},
openResultPicker() {
this.$set(this, "showResultPicker", true)
},
closeResultPicker() {
this.$set(this, "showResultPicker", false)
},
openMasterUnitPicker() {
this.$set(this, "showMasterUnitPicker", true)
},
closeMasterUnitPicker() {
this.$set(this, "showMasterUnitPicker", false)
},
openSlaveUnitPicker() {
this.$set(this, "showSlaveUnitPicker", true)
},
closeSlaveUnitPicker() {
this.$set(this, "showSlaveUnitPicker", false)
},
listSession() {
this.$axios.post("/platform/proposal/common/listSession").then((res) => {
if (res.code === 0) {
this.$set(this, "sessionOptions", [{text: "全部届次", value: null}].concat(res.data.map(function (item) {
return {text: item.fullName, value: item.id}
})))
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].value)
this.doSearch()
}
})
},
listUnderTake() {
this.$axios.post("/platform/proposal/common/listUnderTake").then((res) => {
if (res.code === 0) this.$set(this, "underTakeOptions", res.data)
})
},
changeApproval(value) {
this.$set(this.pageForm, "approval", value === "1")
this.doSearch()
},
onView(row) {
this.$set(this, "showApprovalForm", false)
this.$refs.proposalInfoRef.onOpen(row)
},
onApproval(row) {
this.$set(this, "showApprovalForm", true)
this.$set(this, "formData", {
processTaskId: row.taskId,
proposalId: row.id,
taskName: row.taskName,
tf_caseFilingResult: "",
tf_caseFilingResultName: "",
tf_caseFilingCode: "",
tf_masterUnitIds: [],
tf_slaveUnitIds: [],
tf_opinion: ""
})
this.loadCommitteeFilingData(row.id)
this.$refs.proposalInfoRef.onOpen(row)
},
/**
* 确认承办单位必须延续上一“提案委员会立案”的结果和单位,
* 先读取已办记录回显,仍允许审核人按 PC 页面规则调整后再提交。
*/
loadCommitteeFilingData(proposalId) {
this.$axios.post("/flow/common/doneTasks", {bizId: proposalId}).then((res) => {
if (res.code !== 0) return
const committeeTasks = res.data.filter(function (task) { return task.taskName === "committee" })
const committeeTask = committeeTasks.length ? committeeTasks[committeeTasks.length - 1] : null
if (!committeeTask || !committeeTask.ext) return
const ext = committeeTask.ext
this.$set(this.formData, "tf_caseFilingResult", ext.tf_caseFilingResult || "")
this.$set(this.formData, "tf_caseFilingResultName", this.getCaseFilingResultName(ext.tf_caseFilingResult))
this.$set(this.formData, "tf_caseFilingCode", ext.tf_caseFilingCode || "")
this.$set(this.formData, "tf_masterUnitIds", this.normalizeUnitIds(ext.tf_masterUnitIds))
this.$set(this.formData, "tf_slaveUnitIds", this.normalizeUnitIds(ext.tf_slaveUnitIds))
})
},
getCaseFilingResultName(code) {
const result = (this.dict.type.PROPOSAL_CASE_FILING_RESULT || []).find(function (item) { return item.code === code })
return result ? result.label : ""
},
normalizeUnitIds(unitIds) {
if (Array.isArray(unitIds)) return unitIds
if (!unitIds) return []
if (typeof unitIds === "string") {
try {
const parsedValue = JSON.parse(unitIds)
if (Array.isArray(parsedValue)) return parsedValue
} catch (error) {}
return unitIds.split(",").filter(function (item) { return item && item.trim() }).map(function (item) { return item.trim() })
}
return []
},
onResultConfirm(value) {
this.$set(this.formData, "tf_caseFilingResult", value.value)
this.$set(this.formData, "tf_caseFilingResultName", value.text)
if (value.value === "SUGGESTION") this.$set(this.formData, "tf_slaveUnitIds", [])
if (value.value !== "CONFIRM_FILING" && value.value !== "SUGGESTION") {
this.$set(this.formData, "tf_caseFilingCode", "")
this.$set(this.formData, "tf_masterUnitIds", [])
this.$set(this.formData, "tf_slaveUnitIds", [])
}
this.$set(this, "showResultPicker", false)
},
toggleMasterUnit(unitId) {
this.toggleUnit(this.formData.tf_masterUnitIds, unitId, "tf_masterUnitIds")
},
toggleSlaveUnit(unitId) {
this.toggleUnit(this.formData.tf_slaveUnitIds, unitId, "tf_slaveUnitIds")
},
toggleUnit(unitIds, unitId, fieldName) {
const nextUnitIds = unitIds.slice()
const index = nextUnitIds.indexOf(unitId)
if (index === -1) nextUnitIds.push(unitId)
else nextUnitIds.splice(index, 1)
this.$set(this.formData, fieldName, nextUnitIds)
},
getUnitNames(unitIds) {
return this.underTakeOptions.filter(function (item) {
return unitIds.includes(item.id)
}).map(function (item) {
return item.name
}).join("、")
},
getUnitName(unitId) {
const unit = this.underTakeOptions.find(function (item) { return item.id === unitId })
return unit ? unit.name : ""
},
buildSubmitData() {
const masterUnitIds = this.formData.tf_masterUnitIds || []
const slaveUnitIds = this.formData.tf_caseFilingResult === "CONFIRM_FILING" ? (this.formData.tf_slaveUnitIds || []) : []
return Object.assign({}, this.formData, {
tf_masterUnitIds: masterUnitIds,
tf_slaveUnitIds: slaveUnitIds,
tf_masterUnitId: masterUnitIds.length ? masterUnitIds[0] : "",
tf_masterUnitName: masterUnitIds.length ? this.getUnitName(masterUnitIds[0]) : "",
tf_masterUnitNames: masterUnitIds.map((unitId) => this.getUnitName(unitId)),
tf_masterUnitNameStr: this.getUnitNames(masterUnitIds),
tf_slaveUnitNames: slaveUnitIds.map((unitId) => this.getUnitName(unitId)),
tf_slaveUnitNameStr: this.getUnitNames(slaveUnitIds),
tf_helpunitreply: slaveUnitIds.length ? "HAS_HELP_UNIT" : "NO_HELP_UNIT",
submitType: 1
})
},
async handleTaskAction() {
try {
await this.$refs.formRef.validate()
this.$dialog.confirm({title: "提示", message: "您确定要提交吗?"}).then(() => {
this.$axios.post("/platform/proposal/committeeFilingUnit/h5/executeTask", {
data: JSON.stringify(this.buildSubmitData())
}).then((res) => {
if (res.code === 0) {
this.$refs.proposalInfoRef.onClose()
this.$toast.success(res.msg)
this.doSearch()
}
})
}).catch(function () {})
} catch (error) {}
},
closeApproval() {
this.$refs.proposalInfoRef.onClose()
},
doSearch() {
this.$nextTick(() => {
this.$set(this.pageForm, "pageNumber", 1)
this.$set(this.pageForm, "totalCount", 0)
this.$refs.tableListRef.doSearch()
})
}
}
})
</script>
<!--#
}
#-->
@@ -2,7 +2,7 @@
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<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="pageTitle" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<van-search
v-model="pageForm.name"
@@ -23,7 +23,7 @@ layout("/layouts/platform_h5.html"){
</van-tabs>
</van-sticky>
<table-list api="/platform/proposal/delegation/pageData" :page_form.sync="pageForm" @ready="onReady"
<table-list :api="pageDataUrl" :page_form.sync="pageForm" @ready="onReady"
ref="tableListRef"
title="name">
<template v-slot="{index,row}">
@@ -76,6 +76,32 @@ layout("/layouts/platform_h5.html"){
<script nonce="${cspNonce!}">
<!--#include("../../common/info.js"){}#-->
/**
* 同一审核页面模板按当前访问地址选择标题和手机端数据接口,
* 使 PC、H5 的权限接口保持分离。
*/
function getH5AuditPageConfig() {
const pathname = window.location.pathname
if (pathname.indexOf("/secondaryUnionAudit/") !== -1) {
return {
title: "二级工会审核",
pageDataUrl: "/platform/proposal/secondaryUnionAudit/h5/pageData"
}
}
if (pathname.indexOf("/partyOrganizationAudit/") !== -1) {
return {
title: "院级党组织审核",
pageDataUrl: "/platform/proposal/partyOrganizationAudit/h5/pageData"
}
}
return {
title: "团长审核",
pageDataUrl: "/platform/proposal/delegation/pageData"
}
}
const auditPageConfig = getH5AuditPageConfig()
const vue = new Vue({
el: "#app",
store,
@@ -91,6 +117,8 @@ layout("/layouts/platform_h5.html"){
approval: false
},
pageTitle: auditPageConfig.title,
pageDataUrl: auditPageConfig.pageDataUrl,
sessionOptions: [],
formData: {},
showApprovalForm: false
@@ -0,0 +1,38 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar @click-left="back" left-arrow left-text="返回" placeholder title="幸福工荟详情"></van-nav-bar>
<van-image :src="previewUrl(detail.cover)" fit="cover" height="220" v-if="detail.cover" width="100%"></van-image>
<van-cell-group>
<van-cell :title="detail.title"></van-cell>
<van-cell :value="detail.tagTypeName || '幸福工荟'" title="标签"></van-cell>
<van-cell :value="formatDate(detail.publishTime || detail.createdAt)" title="发布时间"></van-cell>
<van-cell :label="detail.summary" title="摘要"></van-cell>
</van-cell-group>
<div class="inclusive-content" v-html="detail.content"></div>
<div class="inclusive-footer" v-if="hasLink"><van-button @click="openLink" block type="primary">查看活动页面</van-button></div>
</div>
<style>
.inclusive-content { padding: 16px; line-height: 1.8; word-break: break-word; }
.inclusive-content img { height: auto; max-width: 100%; }
.inclusive-footer { padding: 12px; }
</style>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data: function () { return {id: "", detail: {}} },
computed: { hasLink: function () { return !!(this.detail.linkUrl && this.detail.linkUrl.trim()) } },
methods: {
back: function () { history.back() },
previewUrl: function (cover) { return cover || "" },
formatDate: function (value) { return value ? moment(value).format("YYYY-MM-DD") : "" },
findOne: function () { var self = this; $.post("/platform/h5/inclusive/findOne", {id: this.id}).then(function (res) { if (res.code === 0 && res.data) { self.$set(self, "detail", res.data) } else { self.$toast(res.msg || "未查询到活动详情") } }) },
openLink: function () { if (this.hasLink) { window.location.href = this.detail.linkUrl.trim() } }
},
created: function () { this.$set(this, "id", GetQueryString("id")); this.findOne() }
})
</script>
<!--#
}
#-->
@@ -0,0 +1,91 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style>
#app { background: #f6f7f9; min-height: 100vh; }
.inclusive-tab-wrap { padding: 10px 12px 8px; background: #fff; box-shadow: 0 2px 8px rgba(8, 23, 118, .08); }
.inclusive-tabs { background: transparent; border-radius: 0; padding: 0; box-shadow: none; }
.inclusive-tabs .van-tabs__wrap { height: 32px; overflow: hidden; }
.inclusive-tabs .van-tabs__nav { display: flex; overflow-x: auto; overflow-y: hidden; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; padding-right: 10px; background: transparent; }
.inclusive-tabs .van-tabs__nav::-webkit-scrollbar { display: none; }
.inclusive-tabs .van-tab { flex: 0 0 auto; padding: 0 5px; }
.inclusive-tabs .van-tab__text { padding: 7px 12px; border-radius: 9px; background: #f2f5f8; color: #303846; font-size: 12px; line-height: 18px; white-space: nowrap; font-weight: 600; }
.inclusive-tabs .van-tab--active .van-tab__text { color: #fff; background: #081776; }
.inclusive-tabs .van-tabs__line { display: none; }
.inclusive-list { padding: 12px 12px 22px; }
.inclusive-card { overflow: hidden; margin-bottom: 14px; background: #fff; border-radius: 12px; box-shadow: 0 3px 10px rgba(31, 45, 61, .14); }
.inclusive-cover { width: 100%; height: 150px; overflow: hidden; background: #e8eef6; }
.inclusive-cover img { width: 100%; height: 100%; display: block; }
.inclusive-card-main { display: flex; flex-direction: column; min-width: 0; min-height: 132px; position: relative; padding: 12px 42px 14px 14px; }
.inclusive-tag { display: inline-block; max-width: 120px; padding: 3px 9px; border-radius: 5px; color: #0b63d8; background: #eaf3ff; font-size: 12px; line-height: 18px; }
.inclusive-title { margin-top: 8px; color: #111827; font-size: 16px; line-height: 24px; font-weight: bold; min-height: 24px; }
.inclusive-summary { margin-top: 5px; color: #53657f; font-size: 13px; line-height: 19px; min-height: 38px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.inclusive-date { margin-top: auto; color: #6b7c96; font-size: 12px; display: flex; align-items: center; gap: 4px; }
.inclusive-arrow { color: #8a96a8; position: absolute; right: 14px; bottom: 16px; font-size: 22px; }
.inclusive-empty { min-height: 320px; display: flex; align-items: center; justify-content: center; flex-direction: column; color: #909399; font-size: 14px; background: transparent; }
.inclusive-empty-icon { width: 120px; height: 120px; margin-bottom: 10px; opacity: .56; }
</style>
<div id="app" v-cloak>
<van-nav-bar @click-left="back" fixed left-arrow left-text="返回" placeholder title="幸福工荟"></van-nav-bar>
<van-sticky :offset-top="46">
<div class="inclusive-tab-wrap">
<van-tabs @change="tagChange" class="inclusive-tabs" v-model="activeTag">
<van-tab name="" title="全部"></van-tab>
<van-tab :key="item.value" :name="item.value" :title="item.text" v-for="item in tagOptions"></van-tab>
</van-tabs>
</div>
</van-sticky>
<div class="inclusive-list">
<van-pull-refresh @refresh="onRefresh" v-model="refreshing">
<van-list :finished="finished" :finished-text="tableData.length ? '没有更多了' : ''" @load="pageData" v-model="loading">
<div :key="item.id" @click="openDetail(item)" class="inclusive-card" v-for="item in tableData">
<div class="inclusive-cover"><img :src="previewUrl(item.cover)" v-if="item.cover"></div>
<div class="inclusive-card-main">
<div class="inclusive-tag van-ellipsis">{{item.tagTypeName || '幸福工荟'}}</div>
<div class="inclusive-title van-multi-ellipsis--l2">{{item.title}}</div>
<div class="inclusive-summary">{{item.summary}}</div>
<div class="inclusive-date"><van-icon name="underway-o"></van-icon><span>{{formatDate(item.publishTime || item.createdAt)}}</span></div>
<van-icon class="inclusive-arrow" name="arrow"></van-icon>
</div>
</div>
</van-list>
</van-pull-refresh>
<div class="inclusive-empty" v-if="!loading && tableData.length === 0">
<img class="inclusive-empty-icon" src="https://img01.yzcdn.cn/vant/empty-image-default.png">
<div>暂无数据</div>
</div>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data: function () { return {activeTag: "", tagOptions: [], tableData: [], loading: false, refreshing: false, finished: false, pageForm: {pageNumber: 1, pageSize: 6, totalCount: 0, tagType: ""}} },
methods: {
previewUrl: function (cover) { return cover || "" },
formatDate: function (value) { return value ? moment(value).format("YYYY-MM-DD") : "" },
back: function () { history.back() },
getTagOptions: function () { var self = this; $.post("/platform/h5/inclusive/tagOptions", {}).then(function (res) { if (res.code === 0) { self.$set(self, "tagOptions", res.data || []) } }) },
pageData: function () {
var self = this;
this.$set(this, "loading", true);
this.$set(this.pageForm, "tagType", this.activeTag);
$.post("/platform/h5/inclusive/pageData", this.pageForm).then(function (res) {
if (self.refreshing) { self.$set(self, "tableData", []); self.$set(self, "refreshing", false) }
if (res.code === 0) {
var data = res.data || {}; var list = data.list || [];
self.$set(self, "tableData", self.tableData.concat(list)); self.$set(self.pageForm, "totalCount", data.totalCount || 0);
if (self.tableData.length >= self.pageForm.totalCount) { self.$set(self, "finished", true) } else { self.$set(self.pageForm, "pageNumber", self.pageForm.pageNumber + 1) }
} else { self.$toast(res.msg || "查询失败") }
}).always(function () { self.$set(self, "loading", false) })
},
resetList: function () { this.$set(this, "tableData", []); this.$set(this, "finished", false); this.$set(this.pageForm, "pageNumber", 1); this.$set(this.pageForm, "totalCount", 0) },
tagChange: function (value) { this.$set(this, "activeTag", value); this.resetList(); this.pageData() },
onRefresh: function () { this.resetList(); this.$set(this, "refreshing", true); this.pageData() },
openDetail: function (item) { window.location.href = "/platform/h5/inclusive/detail?id=" + item.id }
},
created: function () { this.getTagOptions() }
})
</script>
<!--#
}
#-->
@@ -92,7 +92,7 @@ layout("/layouts/platform_h5.html"){
<div style="padding: 10px;">
<van-checkbox v-model="formData.isRead" shape="square" @change="isReadChange">
<div style="color:#246FB4;" @click.stop="isReadClick">
中国地质大学(武汉)教职工重大疾病互助基金管理办法
西南财经大学教职工重大疾病互助基金管理办法
</div>
</van-checkbox>
</div>
@@ -203,7 +203,7 @@ layout("/layouts/platform_h5.html"){
this.$commonUtil.previewFile({
suffix: "pdf",
downloadPath: "/platform/sys/file/download?id=orpgde84uggjmpflbob83tadan",
name: "中国地质大学(武汉)教职工重大疾病互助基金管理办法.pdf",
name: "西南财经大学教职工重大疾病互助基金管理办法.pdf",
id: "orpgde84uggjmpflbob83tadan"
})
},