From fb25ad45a84644661198c25e301cdf5b07694707 Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Tue, 14 Jul 2026 18:05:53 +0800 Subject: [PATCH 1/4] commit --- ...ProposalExportComprehensiveController.java | 31 +++ .../transact/ProposalWriteController.java | 16 ++ .../controller/vo/ProposalWriteTypeVO.java | 21 ++ .../proposal/models/ProposalType.java | 2 +- .../service/ProposalExportService.java | 8 + .../service/ProposalWriteService.java | 17 ++ .../common/ProposalCommonServiceImpl.java | 2 +- .../impl/ProposalExportServiceImpl.java | 248 ++++++++++++++++++ .../impl/ProposalWriteServiceImpl.java | 68 +++++ .../TeacherCongressSessionController.java | 12 +- .../models/Teacher_congress_session.java | 6 +- .../TeacherCongressSessionService.java | 7 + .../TeacherCongressSessionServiceImpl.java | 125 ++++++++- .../proposal/export/comprehensive/index.html | 37 ++- .../proposal/transact/write/index.html | 20 +- .../prepare/session/index.html | 56 +++- .../proposal/transact/write/index.html | 29 +- 17 files changed, 660 insertions(+), 45 deletions(-) create mode 100644 src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/vo/ProposalWriteTypeVO.java diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/export/ProposalExportComprehensiveController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/export/ProposalExportComprehensiveController.java index aa9fabaa..fb37eeff 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/export/ProposalExportComprehensiveController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/export/ProposalExportComprehensiveController.java @@ -7,6 +7,7 @@ import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.zhgh.democratic.proposal.param.ProposalQueryComprehensiveParam; import com.budwk.app.zhgh.democratic.proposal.service.ProposalExportService; +import com.budwk.app.zhgh.democratic.proposal.service.ProposalWriteService; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @@ -32,6 +33,8 @@ public class ProposalExportComprehensiveController { private ProposalExportService proposalExportService; @Inject private ProposalCommonService proposalCommonService; + @Inject + private ProposalWriteService proposalWriteService; @At("") @Ok("beetl:/platform/zhgh/democratic/proposal/export/comprehensive/index.html") @@ -83,6 +86,19 @@ public class ProposalExportComprehensiveController { return Result.success(pagination); } + /** + * 查询综合导出页面当前教代会届次配置的提案类别。 + * + * @param sessionId 教代会届次ID,用于读取该届次配置的提案类别 + * @return VO 列表,每项包含筛选使用的 id 和页面展示使用的 name + */ + @At + @SaCheckPermission("proposal.export.comprehensive") + @ApiOperation("查询当前届次提案类别") + public Result listSessionProposalTypes(@Valid String sessionId) { + return Result.success(proposalWriteService.listSessionProposalTypes(sessionId)); + } + @At @Ok("void") @@ -92,6 +108,21 @@ public class ProposalExportComprehensiveController { proposalExportService.exportSummaryAsExcel(pageForm, response); } + /** + * 按办理单位导出提案承办单位表压缩包。 + * + * @param pageForm 综合导出页面查询参数,其中 sessionId 为必传的教代会届次ID + * @param response HTTP 响应,返回内容为包含多个 XSSF Excel 的 ZIP 文件 + */ + @At + @Ok("void") + @ApiOperation("按办理单位导出提案承办单位表压缩包") + @SaCheckPermission("proposal.query.comprehensive") + public void exportUndertakeUnitTablesAsZip(@Valid @Param("pageForm") ProposalQueryComprehensiveParam pageForm, + HttpServletResponse response) { + proposalExportService.exportUndertakeUnitTablesAsZip(pageForm, response); + } + @At @Ok("void") diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalWriteController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalWriteController.java index 52f0da5e..c05c1a5b 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalWriteController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalWriteController.java @@ -76,6 +76,19 @@ public class ProposalWriteController { } + /** + * 查询当前教代会届次可用于撰写提案的类型。 + * + * @param sessionId 教代会届次ID + * @return VO 列表,每项包含保存使用的 id 和页面展示使用的 name + */ + @At + @SaCheckPermission("proposal.write") + @ApiOperation("查询届次可用提案类型") + public Result listSessionProposalTypes(@Valid String sessionId) { + return Result.success(proposalWriteService.listSessionProposalTypes(sessionId)); + } + @At @SaCheckPermission("proposal.write") @@ -84,6 +97,7 @@ public class ProposalWriteController { @SLog(tag = "提案管理系统-我的提案", msg = "保存提案") public Result save(@Param("info") ProposalInfo proposalInfo) { proposalWriteService.checkCurrentUserDelegate(proposalInfo.getSessionId()); + proposalWriteService.validateProposalType(proposalInfo.getSessionId(), proposalInfo.getTypeId()); if (StrUtil.isBlank(proposalInfo.getCode())) { proposalInfo.setCode(proposalWriteService.generateProposalCode(proposalInfo.getSessionId(), proposalInfo.getDelegationId())); } @@ -100,6 +114,7 @@ public class ProposalWriteController { @SLog(tag = "提案管理系统-我的提案", msg = "提交提案") public Result submit(@Param("info") ProposalInfo proposalInfo) { proposalWriteService.checkCurrentUserDelegate(proposalInfo.getSessionId()); + proposalWriteService.validateProposalType(proposalInfo.getSessionId(), proposalInfo.getTypeId()); if (StrUtil.isBlank(proposalInfo.getCode())) { proposalInfo.setCode(proposalWriteService.generateProposalCode(proposalInfo.getSessionId(), proposalInfo.getDelegationId())); } @@ -130,6 +145,7 @@ public class ProposalWriteController { @ApiOperation("重新提交提案") @SLog(tag = "提案管理系统-我的提案", msg = "重新提交提案") public Result submitAgain(@Param("info") ProposalInfo proposalInfo, @Param("taskId") Long taskId) { + proposalWriteService.validateProposalType(proposalInfo.getSessionId(), proposalInfo.getTypeId()); dao.insertOrUpdate(proposalInfo); Dict dict = Dict.create(); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/vo/ProposalWriteTypeVO.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/vo/ProposalWriteTypeVO.java new file mode 100644 index 00000000..d21a37a1 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/vo/ProposalWriteTypeVO.java @@ -0,0 +1,21 @@ +package com.budwk.app.zhgh.democratic.proposal.controller.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 撰写提案时当前教代会届次允许选择的提案类型。 + */ +@Data +@AllArgsConstructor +@ApiModel("届次可用提案类型") +public class ProposalWriteTypeVO { + + @ApiModelProperty("提案类型ID,保存提案时写入 proposal_info.typeId") + private Integer id; + + @ApiModelProperty("提案类型名称,来源于当前教代会届次的提案类型配置") + private String name; +} diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/models/ProposalType.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/models/ProposalType.java index f4968382..0f807be1 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/models/ProposalType.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/models/ProposalType.java @@ -25,7 +25,7 @@ public class ProposalType extends BaseModel { @Column @Comment("名称") - @ColDefine(type = ColType.VARCHAR, width = 10) + @ColDefine(type = ColType.VARCHAR, width = 50) private String name; @Column diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalExportService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalExportService.java index ad65c3df..0015ea1a 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalExportService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalExportService.java @@ -34,6 +34,14 @@ public interface ProposalExportService extends BaseService { */ void exportSummaryAsExcel(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response); + /** + * 按办理单位导出提案承办单位表压缩包。 + * + * @param pageForm 综合导出页面查询参数;sessionId 应传当前教代会届次ID,其他条件沿用页面查询条件 + * @param response HTTP 响应,方法直接输出 ZIP;ZIP 中每个有提案的单位对应一个 XSSF Excel + */ + void exportUndertakeUnitTablesAsZip(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response); + /** * 导出提案立案汇总表excel * diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalWriteService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalWriteService.java index 10a932c4..ae07bd50 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalWriteService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalWriteService.java @@ -2,6 +2,7 @@ package com.budwk.app.zhgh.democratic.proposal.service; import com.budwk.app.base.service.BaseService; import com.budwk.app.sys.models.Sys_dict; +import com.budwk.app.zhgh.democratic.proposal.controller.vo.ProposalWriteTypeVO; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import javax.validation.Valid; @@ -10,6 +11,22 @@ import java.util.Map; public interface ProposalWriteService extends BaseService { + /** + * 查询指定教代会届次允许选择的提案类型。 + * + * @param sessionId 教代会届次ID + * @return 提案类型 VO 列表,id 用于保存,name 用于页面展示 + */ + List listSessionProposalTypes(String sessionId); + + /** + * 校验所选提案类型是否属于当前教代会届次。 + * + * @param sessionId 教代会届次ID + * @param typeId 提案类型ID + */ + void validateProposalType(String sessionId, Integer typeId); + List listSource(String sessionId); /** diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java index f7fb81b2..7487dc25 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java @@ -158,7 +158,7 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp pages.put("preAudit", standardOrderColumns()); pages.put("committeeFiling", orderColumns("code", "info.code", "name", "info.name", "typeName", "type.name", "sessionName", "tcs.fullName", "caseFilingResult", "info.caseFilingResult", "delegationName", "tcd.name", "curTaskName", "curTaskName", "instanceState", "ins.state")); pages.put("caseCheck", orderColumns("code", "info.code", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "sessionName", "tcs.fullName", "delegationName", "tcd.name", "curTaskName", "curTaskName", "instanceState", "ins.state", "finishTime", "t.finishTime")); - pages.put("exportComprehensive", orderColumns("code", "info.code", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "sessionName", "tcs.fullName", "delegationName", "tcd.name", "caseFilingResult", "info.caseFilingResult", "caseFilingType", "info.caseFilingType", "merge", "merge", "brief", "info.brief", "measures", "info.measures", "undertakeUnits", "masterUnitName", "curTaskName", "curTaskName", "instanceState", "ins.state")); + pages.put("exportComprehensive", orderColumns("code", "info.code", "caseFilingCode", "info.caseFilingCode", "name", "info.name", "createUserName", "info.createUserName", "typeName", "type.name", "sessionName", "tcs.fullName", "delegationName", "tcd.name", "caseFilingResult", "info.caseFilingResult", "caseFilingType", "info.caseFilingType", "merge", "merge", "brief", "info.brief", "measures", "info.measures", "undertakeUnits", "masterUnitName", "curTaskName", "curTaskName", "instanceState", "ins.state")); pages.put("exportSingleCustom", standardOrderColumns()); pages.put("configUnit", orderColumns("name", "t1.name", "code", "t1.code", "unitLeader", "u2.username")); pages.put("configType", orderColumns("name", "name", "code", "code")); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalExportServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalExportServiceImpl.java index 5b23f4fd..721fdc83 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalExportServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalExportServiceImpl.java @@ -4,8 +4,10 @@ import cn.afterturn.easypoi.excel.ExcelExportUtil; 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.util.StrUtil; import cn.hutool.http.HtmlUtil; +import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.utils.CommonDownloadUtil; @@ -27,7 +29,15 @@ import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.VerticalAlignment; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xwpf.usermodel.BreakType; import org.apache.poi.xwpf.usermodel.IBodyElement; import org.apache.poi.xwpf.usermodel.XWPFDocument; @@ -51,6 +61,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -61,6 +73,9 @@ import java.util.zip.ZipOutputStream; @Slf4j public class ProposalExportServiceImpl extends BaseServiceImpl implements ProposalExportService { + private static final String WORK_SUGGESTION_SECTION_MARKER = "__WORK_SUGGESTION_SECTION__"; + private static final int UNDERTAKE_UNIT_EXPORT_COLUMN_COUNT = 8; + @Inject private ProposalCommonService proposalCommonService; @Inject @@ -246,6 +261,239 @@ public class ProposalExportServiceImpl extends BaseServiceImpl imp CommonDownloadUtil.download(title + ".xlsx", workbook, response); } + /** + * 按办理单位生成提案承办单位表。查询时忽略页面单个承办单位条件,其他查询条件继续生效; + * 同一提案涉及多个单位时,会进入每个相关单位的 Excel,并保留完整主办、协办单位信息。 + * + * @param pageForm 综合导出页面查询参数,sessionId 必须为当前教代会届次ID + * @param response HTTP 响应,直接输出包含多个 XSSF Excel 的 ZIP 文件 + */ + @Override + public void exportUndertakeUnitTablesAsZip(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response) { + if (pageForm == null || StrUtil.isBlank(pageForm.getSessionId())) { + throw new BaseException("请选择教代会"); + } + Teacher_congress_session session = dao().fetch(Teacher_congress_session.class, pageForm.getSessionId()); + if (session == null) { + throw new BaseException("教代会届次不存在"); + } + + List unitRows = queryUndertakeUnitExportRows(pageForm); + if (Lang.isEmpty(unitRows)) { + throw new BaseException("暂无可导出的承办单位提案数据"); + } + + Map proposalRows = new LinkedHashMap<>(); + Map unitNames = new LinkedHashMap<>(); + Map> unitProposalIds = new LinkedHashMap<>(); + for (NutMap row : unitRows) { + String proposalId = row.getString("id"); + String unitId = row.getString("unitId"); + if (StrUtil.isBlank(proposalId) || StrUtil.isBlank(unitId)) { + continue; + } + String unitName = StrUtil.blankToDefault(row.getString("unitName"), "未命名单位"); + NutMap proposalRow = proposalRows.computeIfAbsent(proposalId, key -> NutMap.NEW() + .addv("id", proposalId) + .addv("code", row.getString("code")) + .addv("caseFilingCode", row.getString("caseFilingCode")) + .addv("name", row.getString("name")) + .addv("createUserName", row.getString("createUserName")) + .addv("caseFilingResult", row.getString("caseFilingResult")) + .addv("masterUnitNames", new LinkedHashSet()) + .addv("slaveUnitNames", new LinkedHashSet())); + getUnitNameSet(proposalRow, Boolean.TRUE.equals(row.getBoolean("isMaster")) + ? "masterUnitNames" : "slaveUnitNames").add(unitName); + unitNames.putIfAbsent(unitId, unitName); + unitProposalIds.computeIfAbsent(unitId, key -> new LinkedHashSet<>()).add(proposalId); + } + if (unitProposalIds.isEmpty()) { + throw new BaseException("暂无可导出的承办单位提案数据"); + } + + String title = StrUtil.format("{}第{}教职工代表大会第{}会议提案承办单位表", + Globals.AppName, session.getJ(), session.getC()); + ByteArrayOutputStream zipBytes = new ByteArrayOutputStream(); + Map zipEntryNameCounts = new HashMap<>(); + try (ZipOutputStream zipOutputStream = new ZipOutputStream(zipBytes)) { + for (Map.Entry> unitEntry : unitProposalIds.entrySet()) { + String unitName = unitNames.get(unitEntry.getKey()); + List excelRows = buildUndertakeUnitExcelRows(unitEntry.getValue(), proposalRows); + try (Workbook workbook = buildUndertakeUnitWorkbook(title, unitName, excelRows); + ByteArrayOutputStream workbookBytes = new ByteArrayOutputStream()) { + workbook.write(workbookBytes); + String baseEntryName = "提案承办单位表-" + sanitizeZipFileName(unitName); + int sameNameCount = zipEntryNameCounts.merge(baseEntryName, 1, Integer::sum); + String entryName = baseEntryName + (sameNameCount > 1 ? "-" + sameNameCount : "") + ".xlsx"; + zipOutputStream.putNextEntry(new ZipEntry(entryName)); + workbookBytes.writeTo(zipOutputStream); + zipOutputStream.closeEntry(); + } + } + zipOutputStream.finish(); + } catch (IOException e) { + log.error("按办理单位导出提案承办单位表失败:{}", e.getMessage(), e); + throw new BaseException("导出提案承办单位表失败"); + } + CommonDownloadUtil.download(sanitizeZipFileName(title) + ".zip", zipBytes.toByteArray(), response); + } + + /** + * 查询当前届次已确定立案或作为工作建议的提案及其全部办理单位。 + * 页面承办单位单选条件会被清空,避免只生成一个单位文件;其他页面条件继续沿用。 + */ + private List queryUndertakeUnitExportRows(ProposalQueryComprehensiveParam pageForm) { + ProposalQueryComprehensiveParam exportPageForm = new ProposalQueryComprehensiveParam(); + BeanUtil.copyProperties(pageForm, exportPageForm); + exportPageForm.setUnderTakeUnitId(null); + + Sql sql = Sqls.create(""" + SELECT DISTINCT + info.id, + info.code, + info.caseFilingCode, + info.name, + info.createUserName, + info.caseFilingResult, + pru.unitId, + pru.unitName, + pru.isMaster + FROM + proposal_info info + 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 + LEFT JOIN teacher_congress_delegate tcde ON tcde.loginName = info.createUserLoginName + INNER JOIN proposal_reply_unit pru ON pru.proposalId = info.id + $condition + """); + Cnd cnd = Cnd.where("info.sessionId", "=", pageForm.getSessionId()); + cnd.and("info.caseFilingResult", "in", List.of("CONFIRM_FILING", "SUGGESTION")); + ProposalQueryComprehensiveParam.buildSearch(cnd, exportPageForm); + cnd.asc("info.code"); + cnd.asc("pru.unitName"); + sql.setCondition(cnd); + return listMap(sql); + } + + /** + * 将某个办理单位涉及的提案整理为 Excel 行。确定立案在前,工作建议通过标记行单独分组。 + */ + private List buildUndertakeUnitExcelRows(LinkedHashSet proposalIds, + Map proposalRows) { + List filingRows = new ArrayList<>(); + List suggestionRows = new ArrayList<>(); + for (String proposalId : proposalIds) { + NutMap proposalRow = proposalRows.get(proposalId); + if (proposalRow == null) { + continue; + } + LinkedHashSet masterUnitNames = getUnitNameSet(proposalRow, "masterUnitNames"); + LinkedHashSet slaveUnitNames = getUnitNameSet(proposalRow, "slaveUnitNames"); + NutMap excelRow = NutMap.NEW() + .addv("code", proposalRow.getString("code")) + .addv("caseFilingCode", proposalRow.getString("caseFilingCode")) + .addv("name", proposalRow.getString("name")) + .addv("createUserName", proposalRow.getString("createUserName")) + .addv("masterUnitNames", "") + .addv("slaveUnitNames", "") + .addv("workSuggestionUnitNames", "") + .addv("remark", ""); + if ("CONFIRM_FILING".equals(proposalRow.getString("caseFilingResult"))) { + excelRow.put("masterUnitNames", String.join(",", masterUnitNames)); + excelRow.put("slaveUnitNames", String.join(",", slaveUnitNames)); + filingRows.add(excelRow); + } else { + LinkedHashSet workSuggestionUnitNames = new LinkedHashSet<>(masterUnitNames); + workSuggestionUnitNames.addAll(slaveUnitNames); + excelRow.put("workSuggestionUnitNames", String.join(",", workSuggestionUnitNames)); + suggestionRows.add(excelRow); + } + } + if (!suggestionRows.isEmpty()) { + filingRows.add(NutMap.NEW() + .addv("code", WORK_SUGGESTION_SECTION_MARKER) + .addv("caseFilingCode", "") + .addv("name", "") + .addv("createUserName", "") + .addv("masterUnitNames", "") + .addv("slaveUnitNames", "") + .addv("workSuggestionUnitNames", "") + .addv("remark", "")); + filingRows.addAll(suggestionRows); + } + return filingRows; + } + + /** + * 创建单个办理单位的 XSSF Excel,并把工作建议标记行合并为分组标题。 + */ + private Workbook buildUndertakeUnitWorkbook(String title, String unitName, List excelRows) { + List exportEntities = new ArrayList<>(); + exportEntities.add(new ExcelExportEntity("提案编号", "code", 18)); + exportEntities.add(new ExcelExportEntity("立案编号", "caseFilingCode", 18)); + exportEntities.add(new ExcelExportEntity("提案名称", "name", 45)); + exportEntities.add(new ExcelExportEntity("提案人", "createUserName", 15)); + exportEntities.add(new ExcelExportEntity("主办单位", "masterUnitNames", 28)); + exportEntities.add(new ExcelExportEntity("协办单位", "slaveUnitNames", 28)); + exportEntities.add(new ExcelExportEntity("工作建议送达单位", "workSuggestionUnitNames", 32)); + exportEntities.add(new ExcelExportEntity("备注", "remark", 20)); + for (ExcelExportEntity exportEntity : exportEntities) { + exportEntity.setWrap(true); + } + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + exportParams.setTitle(title); + exportParams.setSecondTitle("(" + unitName + ")"); + exportParams.setSheetName("提案承办单位表"); + exportParams.setHeaderHeight(30); + exportParams.setHeight((short) 24); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, excelRows); + mergeWorkSuggestionSection(workbook); + return workbook; + } + + /** + * 查找工作建议标记行,将八个单元格合并并设置为居中的分组标题。 + */ + private void mergeWorkSuggestionSection(Workbook workbook) { + Sheet sheet = workbook.getSheetAt(0); + for (Row row : sheet) { + Cell firstCell = row.getCell(0); + if (firstCell == null || !WORK_SUGGESTION_SECTION_MARKER.equals(firstCell.toString())) { + continue; + } + CellStyle sectionStyle = workbook.createCellStyle(); + sectionStyle.cloneStyleFrom(firstCell.getCellStyle()); + sectionStyle.setAlignment(HorizontalAlignment.CENTER); + sectionStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font sectionFont = workbook.createFont(); + sectionFont.setFontName("宋体"); + sectionFont.setFontHeightInPoints((short) 11); + sectionFont.setBold(true); + sectionStyle.setFont(sectionFont); + for (int columnIndex = 0; columnIndex < UNDERTAKE_UNIT_EXPORT_COLUMN_COUNT; columnIndex++) { + Cell cell = row.getCell(columnIndex); + if (cell == null) { + cell = row.createCell(columnIndex); + } + cell.setCellValue(columnIndex == 0 ? "作为工作建议的提案" : ""); + cell.setCellStyle(sectionStyle); + } + row.setHeightInPoints(24); + sheet.addMergedRegion(new CellRangeAddress(row.getRowNum(), row.getRowNum(), 0, + UNDERTAKE_UNIT_EXPORT_COLUMN_COUNT - 1)); + return; + } + } + + /** 获取提案行中用于去重且保持顺序的办理单位名称集合。 */ + @SuppressWarnings("unchecked") + private LinkedHashSet getUnitNameSet(NutMap proposalRow, String key) { + return (LinkedHashSet) proposalRow.get(key); + } + @Override public void exportProposalRegisterSummaryAsExcel(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response) { Sql sql = exportComprehensiveSql(pageForm); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalWriteServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalWriteServiceImpl.java index a4e08b4e..adc755b5 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalWriteServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalWriteServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.sys.models.Sys_dict; import com.budwk.app.sys.models.Sys_user; @@ -11,6 +12,7 @@ import com.budwk.app.sys.services.SysDictService; import com.budwk.app.sys.services.SysUserService; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.democratic.proposal.constants.ProposalState; +import com.budwk.app.zhgh.democratic.proposal.controller.vo.ProposalWriteTypeVO; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalType; import com.budwk.app.zhgh.democratic.proposal.service.ProposalWriteService; @@ -24,6 +26,7 @@ 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.json.Json; import org.nutz.lang.Strings; import java.util.ArrayList; @@ -44,6 +47,70 @@ public class ProposalWriteServiceImpl extends BaseServiceImpl impl super(dao); } + @Override + public List listSessionProposalTypes(String sessionId) { + List typeNames = getSessionProposalTypeNames(sessionId); + if (typeNames.isEmpty()) { + return List.of(); + } + List proposalTypes = dao().query(ProposalType.class, + Cnd.where(ProposalType::getName, "in", typeNames)); + List result = new ArrayList<>(); + for (String typeName : typeNames) { + ProposalType proposalType = proposalTypes.stream() + .filter(item -> typeName.equals(item.getName())) + .findFirst() + .orElse(null); + if (proposalType != null) { + result.add(new ProposalWriteTypeVO(proposalType.getId(), proposalType.getName())); + } + } + return result; + } + + @Override + public void validateProposalType(String sessionId, Integer typeId) { + if (typeId == null) { + throw new BaseException("请选择提案类型"); + } + ProposalType proposalType = dao().fetch(ProposalType.class, typeId); + if (proposalType == null || !getSessionProposalTypeNames(sessionId).contains(proposalType.getName())) { + throw new BaseException("所选提案类型不属于当前教代会届次"); + } + } + + /** + * 解析指定届次配置的提案类型。标准数据为 JSON 数组,同时兼容历史单值字符串。 + * + * @param sessionId 教代会届次ID + * @return 按届次配置顺序排列的提案类型名称;未配置时返回空列表 + */ + private List getSessionProposalTypeNames(String sessionId) { + if (StrUtil.isBlank(sessionId)) { + throw new BaseException("所属教代会不能为空"); + } + Teacher_congress_session session = dao().fetch(Teacher_congress_session.class, sessionId); + if (session == null) { + throw new BaseException("所属教代会不存在"); + } + if (StrUtil.isBlank(session.getProposalType())) { + return List.of(); + } + String proposalTypeValue = StrUtil.trim(session.getProposalType()); + if (!proposalTypeValue.startsWith("[")) { + return List.of(proposalTypeValue); + } + try { + return Json.fromJsonAsList(String.class, proposalTypeValue).stream() + .map(StrUtil::trim) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + } catch (Exception e) { + throw new BaseException("教代会届次提案类型配置格式不正确"); + } + } + @Override public List listSource(String sessionId) { Sql sql = Sqls.create(""" @@ -224,6 +291,7 @@ public class ProposalWriteServiceImpl extends BaseServiceImpl impl throw new RuntimeException("没有开启的教代会"); } Teacher_congress_session teacherCongressSession = teacherCongressSessions.get(0); + validateProposalType(teacherCongressSession.getId(), proposalTypeObj.getId()); // 保存到数据库 ProposalInfo proposalInfo = new ProposalInfo(); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/controller/TeacherCongressSessionController.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/controller/TeacherCongressSessionController.java index 301bf59d..92bdb5cf 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/controller/TeacherCongressSessionController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/controller/TeacherCongressSessionController.java @@ -1,7 +1,6 @@ package com.budwk.app.zhgh.democratic.teachercongress.prepare.controller; import cn.dev33.satoken.annotation.SaCheckPermission; -import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; @@ -87,16 +86,7 @@ public class TeacherCongressSessionController { @SaCheckPermission("tc.prepare.session") @ApiOperation(value = "修改届次信息") public Result update(@Valid Teacher_congress_session session) { - int count = dao.count(Teacher_congress_session.class, - Cnd.where(Teacher_congress_session::getJ, "=", session.getJ()) - .and(Teacher_congress_session::getC, "=", session.getC()) - .and(Teacher_congress_session::getId, "!=", session.getId()) - ); - if (count > 0) { - throw new BaseException("请勿重复创建"); - } - session.setFullName("第" + session.getJ() + "第" + session.getC()); - dao.updateIgnoreNull(session); + teacherCongressSessionService.updateSession(session); return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/models/Teacher_congress_session.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/models/Teacher_congress_session.java index 4740f845..caaadb81 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/models/Teacher_congress_session.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/models/Teacher_congress_session.java @@ -44,10 +44,10 @@ public class Teacher_congress_session extends BaseModel { private String fullName; @Column - @Comment("提案类型") - @ColDefine(type = ColType.VARCHAR, width = 50) + @Comment("提案类型(JSON数组)") + @ColDefine(type = ColType.VARCHAR, width = 1000) @NotBlank(message = "提案类型不能为空") - @Size(max = 50, message = "提案类型最多50个字") + @Size(max = 1000, message = "提案类型数据不能超过1000个字符") private String proposalType; @Column diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/service/TeacherCongressSessionService.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/service/TeacherCongressSessionService.java index f568cf82..8e61c20a 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/service/TeacherCongressSessionService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/prepare/service/TeacherCongressSessionService.java @@ -20,5 +20,12 @@ public interface TeacherCongressSessionService extends BaseService '' ORDER BY proposalType"); sql.setCallback(Sqls.callback.strList()); dao().execute(sql); - return sql.getList(String.class).stream() + LinkedHashSet proposalTypes = new LinkedHashSet<>(); + for (String proposalTypeValue : sql.getList(String.class)) { + for (String proposalType : parseProposalTypes(proposalTypeValue)) { + String normalizedProposalType = StrUtil.trim(proposalType); + if (StrUtil.isNotBlank(normalizedProposalType)) { + proposalTypes.add(normalizedProposalType); + } + } + } + return proposalTypes.stream() + .sorted() .map(TeacherCongressSessionProposalTypeVO::new) .toList(); } @@ -74,6 +88,7 @@ public class TeacherCongressSessionServiceImpl extends BaseServiceImpl 0) { @@ -257,6 +272,114 @@ public class TeacherCongressSessionServiceImpl extends BaseServiceImpl values = parseProposalTypes(session.getProposalType()); + LinkedHashSet normalizedValues = new LinkedHashSet<>(); + for (String value : values) { + String normalizedValue = StrUtil.trim(value); + if (StrUtil.isBlank(normalizedValue)) { + continue; + } + if (normalizedValue.length() > 50) { + throw new BaseException("单个提案类型不能超过50个字符"); + } + normalizedValues.add(normalizedValue); + } + if (normalizedValues.isEmpty()) { + throw new BaseException("请至少选择或输入一个提案类型"); + } + + String proposalTypeJson = Json.toJson(new ArrayList<>(normalizedValues)); + if (proposalTypeJson.length() > 1000) { + throw new BaseException("提案类型数据不能超过1000个字符"); + } + session.setProposalType(proposalTypeJson); + syncProposalTypes(normalizedValues); + } + + /** + * 将届次中新输入的类型同步到全局提案类型表,以便继续使用 proposal_info.typeId + * 以及现有列表、统计和导出的关联查询。已有同名类型直接复用,不删除历史类型。 + * + * @param proposalTypeNames 当前届次规范化、去重后的提案类型名称 + */ + private void syncProposalTypes(LinkedHashSet proposalTypeNames) { + ProposalType lastType = dao().fetch(ProposalType.class, Cnd.NEW().desc(ProposalType::getSort)); + int nextSort = lastType == null || lastType.getSort() == null ? 1 : lastType.getSort() + 1; + for (String proposalTypeName : proposalTypeNames) { + ProposalType proposalType = dao().fetch(ProposalType.class, + Cnd.where(ProposalType::getName, "=", proposalTypeName)); + if (proposalType != null) { + continue; + } + ProposalType newProposalType = new ProposalType(); + newProposalType.setCode(generateProposalTypeCode()); + newProposalType.setName(proposalTypeName); + newProposalType.setSort(nextSort++); + dao().insert(newProposalType); + } + } + + /** + * 生成不超过 10 个字符的内部提案类型编码。 + * + * @return 以 TC 开头且在 proposal_type 表中未使用的编码 + */ + private String generateProposalTypeCode() { + String code; + do { + code = "TC" + R.UU32().substring(0, 8).toUpperCase(); + } while (dao().count(ProposalType.class, Cnd.where(ProposalType::getCode, "=", code)) > 0); + return code; + } + + /** + * 解析届次中保存的提案类型。标准数据为 JSON 字符串数组,同时兼容调整前可能存在的单值数据。 + * + * @param proposalTypeValue 数据库字段值或前端提交的 JSON 字符串 + * @return 提案类型字符串列表;空值返回空列表 + */ + private List parseProposalTypes(String proposalTypeValue) { + if (StrUtil.isBlank(proposalTypeValue)) { + return List.of(); + } + String value = proposalTypeValue.trim(); + if (!value.startsWith("[")) { + return List.of(value); + } + try { + return Json.fromJsonAsList(String.class, value); + } catch (Exception e) { + throw new BaseException("提案类型数据格式不正确"); + } + } + @Override @Aop(TransAop.READ_COMMITTED) public void deleteSession(String id) { diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/export/comprehensive/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/export/comprehensive/index.html index f8233d47..8441e7ab 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/export/comprehensive/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/export/comprehensive/index.html @@ -88,7 +88,7 @@ layout("/layouts/platform.html"){
@@ -125,6 +125,7 @@ layout("/layouts/platform.html"){ 导出反馈表压缩包 导出汇总表 + 导出承办单位表压缩包 自定义导出 @@ -205,6 +206,7 @@ layout("/layouts/platform.html"){ }, tableColumns: [ {label: "提案编号", prop: "code"}, + {label: "立案编号", prop: "caseFilingCode"}, {label: "提案名称", prop: "name"}, {label: "提案人", prop: "createUserName"}, {label: "提案类别", prop: "typeName"}, @@ -290,6 +292,15 @@ layout("/layouts/platform.html"){ pageForm: JSON.stringify(this.pageForm) }) }, + exportUndertakeUnitTablesAsZip() { + if (!this.pageForm.sessionId) { + this.$message.warning("请选择教代会") + return + } + this.$downLoad(loc() + "/exportUndertakeUnitTablesAsZip", { + pageForm: JSON.stringify(this.pageForm) + }) + }, exportProposalRegisterSummaryAsExcel() { this.$downLoad("/platform/proposal/export/comprehensive/exportProposalRegisterSummaryAsExcel", { pageForm: JSON.stringify(this.pageForm) @@ -313,15 +324,25 @@ layout("/layouts/platform.html"){ }, listProposalType() { - this.$axios.post("/platform/proposal/common/listProposalType").then((res) => { + if (!this.pageForm.sessionId) { + this.$set(this, "typeOptions", []) + return Promise.resolve() + } + return this.$axios.post(loc() + "/listSessionProposalTypes", { + sessionId: this.pageForm.sessionId + }).then((res) => { if (res.code === 0) { - this.typeOptions = res.data + this.$set(this, "typeOptions", res.data) } }) }, - async meetingChange(val) { - this.pageForm.delegationId = null + meetingChange(val) { + // 切换届次后旧类别不再适用,需清空选择并加载当前届次类别 + this.$set(this.pageForm, "typeIds", []) + this.$set(this, "typeOptions", []) + this.listProposalType() + this.$set(this.pageForm, "delegationId", null) this.listDelegation() }, listDelegation() { @@ -334,9 +355,10 @@ layout("/layouts/platform.html"){ listSession() { this.$axios.post("/platform/proposal/common/listSession").then((res) => { if (res.code === 0) { - this.sessionOptions = res.data - if (this.sessionOptions) { + this.$set(this, "sessionOptions", res.data) + if (this.sessionOptions && this.sessionOptions.length > 0) { this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id) + this.listProposalType() this.listDelegation() this.pageData() } @@ -379,7 +401,6 @@ layout("/layouts/platform.html"){ this.listUnderTake() this.listUnion() this.listUnit() - this.listProposalType() } }) diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/write/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/write/index.html index 8c3d2be8..b4638741 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/write/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/write/index.html @@ -101,7 +101,7 @@ layout("/layouts/platform.html"){ - @@ -270,7 +270,7 @@ layout("/layouts/platform.html"){ delegationId: [{required: true, message: "请选择所属代表团", trigger: ["blur", "change"]}], sessionId: [{required: true, message: "请选择所属教代会", trigger: ["blur", "change"]}], committeeId: [{required: true, message: "请选择所属委员会", trigger: ["blur", "change"]}], - typeId: [{required: true, message: "请选择提案类别", trigger: ["blur", "change"]}], + typeId: [{required: true, message: "请选择提案类型", trigger: ["blur", "change"]}], suggestUnits: [{required: true, message: "请选择建议办理单位", trigger: ["blur", "change"]}], sign: [{required: true, message: "请扫描二维码进行签字", trigger: ["blur", "change"]}], excerpt: [{required: true, message: "请填写", trigger: ["blur", "change"]}], @@ -446,9 +446,15 @@ layout("/layouts/platform.html"){ } }, - // 获取提案类别 + // 根据当前教代会届次获取允许撰写的提案类型 listProposalType() { - this.$axios.post("/platform/proposal/common/listProposalType").then((res) => { + if (!this.formData.sessionId) { + this.typeOptions = [] + return Promise.resolve() + } + return this.$axios.post("/platform/proposal/write/listSessionProposalTypes", { + sessionId: this.formData.sessionId + }).then((res) => { if (res.code === 0) { this.typeOptions = res.data } @@ -466,6 +472,10 @@ layout("/layouts/platform.html"){ //教代会change async meetingChange(val) { + // 切换届次后,原提案类型不再有效,需按新届次重新选择 + this.$set(this.formData, "typeId", null) + this.typeOptions = [] + this.listProposalType() this.formData.delegationId = null this.formData.committeeId = null this.listDelegation() @@ -506,6 +516,7 @@ layout("/layouts/platform.html"){ this.checkWriteTime() } await this.listDelegation() + this.listProposalType() // await this.listSource() } }) @@ -578,7 +589,6 @@ layout("/layouts/platform.html"){ created() { this.init() this.listSuggestUnit() - this.listProposalType() this.getProposalConfig() } }) diff --git a/src/main/resources/views/platform/zhgh/democratic/teachercongress/prepare/session/index.html b/src/main/resources/views/platform/zhgh/democratic/teachercongress/prepare/session/index.html index 5f6d2be9..3bf67ffd 100644 --- a/src/main/resources/views/platform/zhgh/democratic/teachercongress/prepare/session/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/teachercongress/prepare/session/index.html @@ -35,7 +35,12 @@ layout("/layouts/platform.html"){ - + + + @@ -79,9 +84,9 @@ layout("/layouts/platform.html"){ - + - @@ -143,6 +148,23 @@ layout("/layouts/platform.html"){ el: "#app", mixins: [initTableMixins], data() { + // 多选项至少保留一项,单个自定义类型最多 50 个字符,序列化后总长度不超过数据库字段限制。 + const validateProposalTypes = (rule, value, callback) => { + const proposalTypes = value || [] + if (!proposalTypes.length) { + callback(new Error("请至少选择或输入一个提案类型")) + return + } + if (proposalTypes.some((item) => item && item.trim().length > 50)) { + callback(new Error("单个提案类型最多50个字符")) + return + } + if (JSON.stringify(proposalTypes).length > 1000) { + callback(new Error("提案类型数据不能超过1000个字符")) + return + } + callback() + } return { dialogFormVisible: false, formRules: { @@ -150,8 +172,7 @@ layout("/layouts/platform.html"){ j: [{required: true, message: "必填", trigger: ["change", "blur"]}], c: [{required: true, message: "必填", trigger: ["change", "blur"]}], proposalType: [ - {required: true, message: "请选择或输入提案类型", trigger: ["change", "blur"]}, - {max: 50, message: "提案类型最多50个字", trigger: ["change", "blur"]} + {validator: validateProposalTypes, trigger: ["change", "blur"]} ], enable: [{required: true, message: "必填", trigger: ["change", "blur"]}], description: [{required: true, message: "必填", trigger: ["change", "blur"]}], @@ -167,7 +188,7 @@ layout("/layouts/platform.html"){ this.dialogFormVisible = true this.$nextTick(() => { this.formData = {} - this.$set(this.formData, "proposalType", "") + this.$set(this.formData, "proposalType", []) }) }, openEdit(id) { @@ -175,6 +196,7 @@ layout("/layouts/platform.html"){ this.$axios.post(loc() + "/findOne", {id}).then((res) => { if (res.code === 0) { res.data.year = res.data.year.toString() + this.$set(res.data, "proposalType", this.parseProposalTypes(res.data.proposalType)) this.formData = res.data } }) @@ -184,7 +206,11 @@ layout("/layouts/platform.html"){ this.$refs.formRef.validate((valid) => { if (valid) { const loading = createLoading() - this.$axios.post(loc() + (this.formData.id ? "/update" : "/insert"), this.formData).then((res) => { + const submitData = { + ...this.formData, + proposalType: JSON.stringify(this.formData.proposalType || []) + } + this.$axios.post(loc() + (this.formData.id ? "/update" : "/insert"), submitData).then((res) => { if (res.code === 0) { this.dialogFormVisible = false this.$message.success(res.msg) @@ -198,6 +224,22 @@ layout("/layouts/platform.html"){ }) }, + // 数据库存储 JSON 数组;同时兼容字段调整前可能存在的单值内容。 + parseProposalTypes(proposalType) { + if (Array.isArray(proposalType)) { + return proposalType + } + if (!proposalType) { + return [] + } + try { + const values = JSON.parse(proposalType) + return Array.isArray(values) ? values : [proposalType] + } catch (e) { + return [proposalType] + } + }, + doDelete(id) { this.$confirm("您确定要删除吗?", "提示", { confirmButtonText: "确定", diff --git a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/write/index.html b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/write/index.html index 28db6adc..c671b056 100644 --- a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/write/index.html +++ b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/write/index.html @@ -210,10 +210,10 @@ layout("/layouts/platform_h5.html"){ is-link readonly name="typeName" - label="提案类别" - placeholder="请选择提案类别" + label="提案类型" + placeholder="请选择提案类型" @click="showTypePicker = true" - :rules="[{ required: true, message: '请选择提案类别' }]" + :rules="[{ required: true, message: '请选择提案类型' }]" required > @@ -411,7 +411,7 @@ layout("/layouts/platform_h5.html"){ }, onTypeConfirm(value) { - this.formData.typeId = value.value; + this.$set(this.formData, 'typeId', value.value); this.$set(this.formData, 'typeName', value.text); this.showTypePicker = false; }, @@ -589,9 +589,15 @@ layout("/layouts/platform_h5.html"){ } }, - // 获取提案类别 + // 根据当前教代会届次获取允许撰写的提案类型 listProposalType() { - this.$axios.post("/platform/proposal/common/listProposalType").then((res) => { + if (!this.formData.sessionId) { + this.typeOptions = []; + return Promise.resolve(); + } + return this.$axios.post("/platform/proposal/write/listSessionProposalTypes", { + sessionId: this.formData.sessionId + }).then((res) => { if (res.code === 0) { this.typeOptions = res.data; } @@ -609,6 +615,11 @@ layout("/layouts/platform_h5.html"){ //教代会change async meetingChange(val) { + // 切换届次后清空旧值,并加载新届次允许选择的提案类型 + this.$set(this.formData, "typeId", null); + this.$set(this.formData, "typeName", ""); + this.typeOptions = []; + this.listProposalType(); this.formData.delegationId = null; this.formData.committeeId = null; this.formData.delegationName = ''; @@ -646,6 +657,7 @@ layout("/layouts/platform_h5.html"){ if (!isModify && this.sessionOptions && this.sessionOptions.length > 0) { this.$set(this.formData, "sessionId", this.sessionOptions[0].id); this.$set(this.formData, "sessionName", this.sessionOptions[0].fullName); + this.listProposalType(); await this.listDelegation(); await this.listSource(); // 获取代表团 @@ -655,7 +667,9 @@ layout("/layouts/platform_h5.html"){ } else { await this.listDelegation(); await this.listSource(); - this.echoPicker() + this.listProposalType().then(() => { + this.echoPicker(); + }); } } }); @@ -742,7 +756,6 @@ layout("/layouts/platform_h5.html"){ created() { this.init(); this.listSuggestUnit(); - this.listProposalType(); this.getProposalConfig(); }, watch: { From b974d84086c49a50ac23a374b0c3a2790b652d68 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 15 Jul 2026 09:23:00 +0800 Subject: [PATCH 2/4] commit --- .../transact/ProposalCaseCheckController.java | 12 + .../ProposalCommitteeFilingController.java | 11 + ...ProposalCommitteeFilingUnitController.java | 11 + .../ProposalDelegationController.java | 11 + .../ProposalFeedbackEvaluationController.java | 11 + .../transact/ProposalPreAuditController.java | 12 + ...roposalSchoolLeaderApprovalController.java | 11 + .../ProposalUnderTakeReplyController.java | 11 + .../service/common/ProposalCommonService.java | 14 ++ .../common/ProposalCommonServiceImpl.java | 225 ++++++++++++++++++ .../proposal/transact/caseCheck/index.html | 11 + .../transact/committeeFiling/index.html | 11 + .../transact/committeeFilingUnit/index.html | 11 + .../proposal/transact/delegation/index.html | 11 + .../proposal/transact/feedback/index.html | 13 +- .../proposal/transact/preAudit/index.html | 11 + .../transact/schoolLeaderApproval/index.html | 13 +- .../proposal/transact/unitReply/index.html | 11 + 18 files changed, 419 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCaseCheckController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCaseCheckController.java index f96e9315..6cd1af9a 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCaseCheckController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCaseCheckController.java @@ -3,6 +3,7 @@ package com.budwk.app.zhgh.democratic.proposal.controller.transact; import cn.dev33.satoken.annotation.SaCheckPermission; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.base.service.BaseService; import com.budwk.app.flow.enums.ProcessTaskStateEnum; @@ -21,7 +22,9 @@ 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; import java.util.List; @@ -116,6 +119,15 @@ public class ProposalCaseCheckController { return Result.success(pagination); } + @At + @Ok("void") + @SaCheckPermission("proposal.caseCheck") + @ApiOperation("导出列表") + public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval, + @Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) { + proposalCommonService.exportWorkflowList("caseCheck", pageForm, approval, tableColumns, response); + } + @At @SaCheckPermission("proposal.caseCheck") @ApiOperation("查询当前提案承办单位") diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java index 78306070..f3f487a3 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java @@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.lang.Dict; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.web.commons.auth.utils.AuthUtil; @@ -28,6 +29,7 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.validation.Valid; +import javax.servlet.http.HttpServletResponse; import java.util.List; @IocBean @@ -114,6 +116,15 @@ public class ProposalCommitteeFilingController { return Result.success(pagination); } + @At + @Ok("void") + @SaCheckPermission("proposal.committeeFiling") + @ApiOperation("导出列表") + public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval, + @Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) { + proposalCommonService.exportWorkflowList("committeeFiling", pageForm, approval, tableColumns, response); + } + @At @SaCheckPermission("proposal.committeeFiling") @ApiOperation("并案审核") diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java index e2883d49..38d4af98 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java @@ -6,6 +6,7 @@ import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.base.service.BaseService; import com.budwk.app.flow.entity.ProcessInstance; @@ -36,6 +37,7 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.validation.Valid; +import javax.servlet.http.HttpServletResponse; import java.util.List; @IocBean @@ -127,6 +129,15 @@ public class ProposalCommitteeFilingUnitController { return Result.success(pagination); } + @At + @Ok("void") + @SaCheckPermission("proposal.committeeFilingUnit") + @ApiOperation("导出列表") + public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval, + @Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) { + proposalCommonService.exportWorkflowList("committeeFilingUnit", pageForm, approval, tableColumns, response); + } + @At @SaCheckPermission("proposal.committeeFilingUnit") @ApiOperation("执行任务") diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalDelegationController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalDelegationController.java index 8249ac24..cd024a5a 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalDelegationController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalDelegationController.java @@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.base.service.BaseService; import com.budwk.app.bpm.enums.BpmProcessTaskStatusEnum; @@ -32,6 +33,7 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.validation.Valid; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -127,4 +129,13 @@ public class ProposalDelegationController { return Result.success(pagination); } + @At + @Ok("void") + @SaCheckPermission("proposal.delegation") + @ApiOperation("导出列表") + public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval, + @Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) { + proposalCommonService.exportWorkflowList("delegation", pageForm, approval, tableColumns, response); + } + } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalFeedbackEvaluationController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalFeedbackEvaluationController.java index 537882a7..bef308e3 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalFeedbackEvaluationController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalFeedbackEvaluationController.java @@ -5,6 +5,7 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessTaskStateEnum; @@ -26,6 +27,7 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.validation.Valid; +import javax.servlet.http.HttpServletResponse; import java.util.List; @IocBean @@ -116,6 +118,15 @@ public class ProposalFeedbackEvaluationController { return Result.success(pagination); } + @At + @Ok("void") + @SaCheckPermission("proposal.feedbackEvaluation") + @ApiOperation("导出列表") + public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval, + @Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) { + proposalCommonService.exportWorkflowList("feedbackEvaluation", pageForm, approval, tableColumns, response); + } + @At @SaCheckPermission("proposal.feedbackEvaluation") @ApiOperation("获取立案信息") diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalPreAuditController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalPreAuditController.java index e3e7fa16..a2381d9d 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalPreAuditController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalPreAuditController.java @@ -3,6 +3,7 @@ package com.budwk.app.zhgh.democratic.proposal.controller.transact; import cn.dev33.satoken.annotation.SaCheckPermission; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.base.service.BaseService; import com.budwk.app.flow.enums.ProcessTaskStateEnum; @@ -20,7 +21,9 @@ 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; import java.util.List; @@ -113,4 +116,13 @@ public class ProposalPreAuditController { Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(),pageForm.getPageSize(), sql); return Result.success(pagination); } + + @At + @Ok("void") + @SaCheckPermission("proposal.preAudit") + @ApiOperation("导出列表") + public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval, + @Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) { + proposalCommonService.exportWorkflowList("preAudit", pageForm, approval, tableColumns, response); + } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSchoolLeaderApprovalController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSchoolLeaderApprovalController.java index 4c8625b4..7561186b 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSchoolLeaderApprovalController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSchoolLeaderApprovalController.java @@ -7,6 +7,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.base.vo.LabelValueVO; import com.budwk.app.flow.constant.FlowConst; @@ -38,6 +39,7 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.validation.Valid; +import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; @@ -144,6 +146,15 @@ public class ProposalSchoolLeaderApprovalController { return Result.success(pagination); } + @At + @Ok("void") + @SaCheckPermission("proposal.schoolLeaderApproval") + @ApiOperation("导出列表") + public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval, + @Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) { + proposalCommonService.exportWorkflowList("schoolLeaderApproval", pageForm, approval, tableColumns, response); + } + @At @SaCheckPermission("proposal.schoolLeaderApproval") @ApiOperation("获取主办单位") diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java index 53f9cb7c..47e4a5d8 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.result.Result; import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessTaskStateEnum; @@ -37,6 +38,7 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.validation.Valid; +import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.util.List; @@ -170,6 +172,15 @@ public class ProposalUnderTakeReplyController { return Result.success(pagination); } + @At + @Ok("void") + @SaCheckPermission("proposal.unitReply") + @ApiOperation("导出列表") + public void exportList(@Valid @Param("pageForm") ProposalSearchParam pageForm, boolean approval, + @Param("tableColumns") ExportTableColumns[] tableColumns, HttpServletResponse response) { + proposalCommonService.exportWorkflowList("unitReply", pageForm, approval, tableColumns, response); + } + /** * 承办单位答复节点撤回需要基于整组当前节点判断。 * 只有“当前流程正在办理的节点”和“这条列表记录所属节点”完全一致时,才允许撤回; diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java index 7d6f645d..207a0ade 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java @@ -2,8 +2,10 @@ package com.budwk.app.zhgh.democratic.proposal.service.common; import com.budwk.app.base.service.BaseService; import com.budwk.app.base.param.PageForm; +import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; +import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import org.nutz.dao.Cnd; import org.nutz.lang.util.NutMap; @@ -137,6 +139,18 @@ public interface ProposalCommonService extends BaseService { */ void exportYearReport(String sessionId, HttpServletResponse response); + /** + * 按办理页面当前筛选条件和可见列导出提案列表。 + * + * @param pageCode 页面代码,用于固定查询范围、数据权限和排序白名单 + * @param pageForm 页面筛选条件 + * @param approval 审核状态筛选条件 + * @param tableColumns 当前页面可见列 + * @param response HTTP 响应 + */ + void exportWorkflowList(String pageCode, ProposalSearchParam pageForm, boolean approval, + ExportTableColumns[] tableColumns, HttpServletResponse response); + /** * 合并提案 diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java index f7fb81b2..f20a6067 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java @@ -7,8 +7,13 @@ import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +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.param.ExportTableColumns; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.utils.CommonDownloadUtil; @@ -19,6 +24,7 @@ import com.budwk.app.flow.engine.FlowEngine; import com.budwk.app.flow.engine.util.FlowUtil; import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.enums.ProcessInstanceStateEnum; import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.vo.ProcessTaskVO; import com.budwk.app.sys.models.Sys_dict; @@ -28,11 +34,13 @@ 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.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.base.Globals; import com.budwk.app.zhgh.democratic.proposal.constants.ProposalState; import com.budwk.app.zhgh.democratic.proposal.models.*; import com.budwk.app.zhgh.democratic.proposal.param.ProposalQueryComprehensiveParam; +import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.service.ProposalWriteService; import com.budwk.app.zhgh.democratic.teachercongress.delegate.models.Teacher_congress_delegate; import com.budwk.app.zhgh.democratic.teachercongress.delegate.service.TeacherCongressDelegateService; @@ -55,6 +63,7 @@ import org.nutz.json.Json; import org.nutz.lang.Lang; import org.nutz.lang.Strings; import org.nutz.lang.util.NutMap; +import org.apache.poi.ss.usermodel.Workbook; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; @@ -73,6 +82,9 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp /** PC端提案列表页面的排序字段白名单,页面代码由对应Controller固定传入。 */ private static final Map> PROPOSAL_LIST_ORDER_COLUMNS = createProposalListOrderColumns(); + /** 办理页面允许导出的列表字段,避免请求参数携带非页面字段。 */ + private static final Map> WORKFLOW_EXPORT_COLUMNS = createWorkflowExportColumns(); + //找出富文本里面上传的图片 static String IMG_SRC_REGEX = "]*src\\s*=\\s*([\"'])(/platform/sys/file/download\\?id=.*?)\\1[^>]*>"; @@ -204,6 +216,219 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp return Collections.unmodifiableMap(columns); } + /** + * 按办理页面当前筛选条件和可见列导出列表,查询范围与页面分页列表保持一致。 + * + * @param pageCode 页面代码 + * @param pageForm 页面筛选条件 + * @param approval 审核状态 + * @param tableColumns 页面可见列 + * @param response HTTP 响应 + */ + @Override + public void exportWorkflowList(String pageCode, ProposalSearchParam pageForm, boolean approval, + ExportTableColumns[] tableColumns, HttpServletResponse response) { + List exportColumns = getWorkflowExportColumns(pageCode, tableColumns); + if (exportColumns.isEmpty()) { + throw Lang.makeThrow("请选择至少一个列表字段后再导出"); + } + + Sql sql = buildWorkflowExportSql(pageCode, pageForm, approval); + List rows = listMap(sql); + convertWorkflowExportValues(rows, pageCode); + + List excelColumns = new ArrayList<>(); + for (ExportTableColumns column : exportColumns) { + excelColumns.add(new ExcelExportEntity(column.getLabel(), column.getProp(), 20)); + } + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + exportParams.setTitle("提案列表"); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, excelColumns, rows); + CommonDownloadUtil.download("提案列表.xlsx", workbook, response); + } + + /** 根据页面代码构造导出查询,保证数据权限、筛选条件及排序规则与列表页一致。 */ + 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", + "schoolLeaderApproval", "caseCheck").contains(pageCode); + String taskName = getWorkflowTaskName(pageCode); + + String schoolLeaderColumns = isSchoolLeaderApproval ? """ + GROUP_CONCAT(DISTINCT CASE WHEN pru.isMaster = 1 THEN pru.unitName END) AS masterUnitName, + GROUP_CONCAT(DISTINCT CASE WHEN pru.isMaster = 0 THEN pru.unitName END) AS slaveUnitNames, + """ : ""; + String auditUserColumn = (isSchoolLeaderApproval || isUnitReply) + ? "GROUP_CONCAT(DISTINCT ta.actorName, '(', ta.actorAccount, ')') AS auditUser," + : ""; + String unitReplyColumns = isUnitReply ? """ + COALESCE(NULLIF(t.variable->>'$.underTakeName', ''), replyUnit.unitName) AS underTakeName, + CASE + WHEN JSON_EXTRACT(t.variable, '$.underTakeIsMaster') IS NOT NULL + THEN IF(JSON_EXTRACT(t.variable, '$.underTakeIsMaster') = true, 1, 0) + ELSE IFNULL(replyUnit.isMaster, 0) + END AS underTakeIsMaster, + """ : ""; + String unionJoin = requiresUnionFilter ? "LEFT JOIN vw_user vu ON vu.id = info.createUserId" : ""; + String schoolLeaderJoin = isSchoolLeaderApproval ? "LEFT JOIN proposal_reply_unit pru ON pru.proposalId = info.id" : ""; + String unitReplyJoin = isUnitReply + ? "LEFT JOIN proposal_reply_unit replyUnit ON replyUnit.proposalId = info.id AND replyUnit.unitId = ta.actorUnitId\n LEFT JOIN sys_user transferUser ON transferUser.id = t.variable->>'$.tf_transferUserId'" + : ""; + + Sql sql = Sqls.create(""" + SELECT + info.*, + type.name AS typeName, + IF(mer.proposalId IS NOT NULL, 1, 0) AS merge, + tcs.fullName AS sessionName, + tcd.`name` AS delegationName, + ins.state AS instanceState, + t.finishTime, + %s + %s + %s + IFNULL(GROUP_CONCAT(DISTINCT nt.displayName), '结束') AS curTaskName + 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 proposal_info info ON info.id = ins.businessNo + LEFT JOIN proposal_type type ON type.id = info.typeId + LEFT JOIN proposal_merge mer ON mer.proposalId = info.id + LEFT JOIN teacher_congress_session tcs ON tcs.id = info.sessionId + LEFT JOIN teacher_congress_delegation tcd ON tcd.id = info.delegationId + %s + %s + %s + $condition + """.formatted(schoolLeaderColumns, auditUserColumn, unitReplyColumns, unionJoin, schoolLeaderJoin, unitReplyJoin)); + + Cnd cnd = Cnd.NEW(); + if (isUnitReply) { + cnd.and("t.taskName", "in", List.of("unit_reply", "two_unit_reply", "opinion_unit_reply")); + if (!"superadmin".equals(SecurityUtil.getUserLoginname())) { + cnd.and("ta.actorId", "=", SecurityUtil.getUserId()); + } + } else { + cnd.and("t.taskName", "=", taskName); + if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { + cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); + } + } + if (approval) { + cnd.and("t.taskState", "in", isUnitReply + ? List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.TRANSFER.getCode()) + : List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode())); + } else { + cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); + } + if (requiresUnionFilter) { + cnd.andEX("vu.unionId", "=", pageForm.getUnionId()); + } + ProposalSearchParam.buildSearch(cnd, pageForm); + cnd.groupBy("t.id"); + if (!applySafeProposalListOrder(cnd, pageForm, pageCode)) { + cnd.desc("t.createdAt"); + } + sql.setCondition(cnd); + return sql; + } + + /** 获取页面对应的流程任务编码,页面代码不匹配时拒绝导出。 */ + private String getWorkflowTaskName(String pageCode) { + Map taskNames = Map.of( + "delegation", "delegation", + "preAudit", "preAudit", + "committeeFiling", "committee", + "committeeFilingUnit", "committeeFilingUnit", + "schoolLeaderApproval", "schoolLeader", + "feedbackEvaluation", "feedback", + "caseCheck", "caseCheck" + ); + String taskName = taskNames.get(pageCode); + if (StrUtil.isBlank(taskName) && !"unitReply".equals(pageCode)) { + throw Lang.makeThrow("不支持的提案列表导出页面"); + } + return taskName; + } + + /** 将字典、枚举及组合字段转换为与列表页面相同的显示文本。 */ + private void convertWorkflowExportValues(List rows, String pageCode) { + Map caseFilingResultMap = sysDictService.getSubListByCode("PROPOSAL_CASE_FILING_RESULT") + .stream().collect(Collectors.toMap(Sys_dict::getCode, Sys_dict::getName)); + Map processStateMap = Arrays.stream(ProcessInstanceStateEnum.values()) + .collect(Collectors.toMap(ProcessInstanceStateEnum::getCode, ProcessInstanceStateEnum::getMessage)); + for (NutMap row : rows) { + if ("schoolLeaderApproval".equals(pageCode)) { + row.put("undertakeUnits", formatUndertakeUnits(row)); + } + // 所有含“是否并案”列的办理页面统一导出“是/否”,避免未并案记录出现空白。 + row.put("merge", row.getInt("merge") == 1 ? "是" : "否"); + if ("unitReply".equals(pageCode)) { + row.put("underTakeIsMaster", row.getInt("underTakeIsMaster") == 1 ? "主办" : "协办"); + } + row.put("caseFilingResult", caseFilingResultMap.getOrDefault(row.getString("caseFilingResult"), row.getString("caseFilingResult"))); + Integer instanceState = row.getInt("instanceState"); + row.put("instanceState", processStateMap.getOrDefault(instanceState, row.getString("instanceState"))); + } + } + + /** 校领导审批页按页面展示规则拼接承办单位。 */ + private String formatUndertakeUnits(NutMap row) { + String masterUnitNames = StrUtil.nullToEmpty(row.getString("masterUnitName")); + String slaveUnitNames = StrUtil.nullToEmpty(row.getString("slaveUnitNames")); + if ("SUGGESTION".equals(row.getString("caseFilingResult"))) { + return StrUtil.isNotBlank(masterUnitNames) ? masterUnitNames : slaveUnitNames; + } + if ("CONFIRM_FILING".equals(row.getString("caseFilingResult"))) { + List unitNames = new ArrayList<>(); + if (StrUtil.isNotBlank(masterUnitNames)) { + unitNames.add("主办:" + masterUnitNames); + } + if (StrUtil.isNotBlank(slaveUnitNames)) { + unitNames.add("协办:" + slaveUnitNames); + } + return StrUtil.join(";", unitNames); + } + return ""; + } + + /** 根据页面列定义过滤导出字段,确保导出范围只包含列表可展示字段。 */ + private List getWorkflowExportColumns(String pageCode, ExportTableColumns[] tableColumns) { + Set allowedColumns = WORKFLOW_EXPORT_COLUMNS.get(pageCode); + if (allowedColumns == null || tableColumns == null) { + return List.of(); + } + return Arrays.stream(tableColumns) + .filter(Objects::nonNull) + .filter(column -> StrUtil.isNotBlank(column.getLabel()) && allowedColumns.contains(column.getProp())) + .collect(Collectors.toList()); + } + + /** 初始化各办理页面可导出的列表字段白名单。 */ + private static Map> createWorkflowExportColumns() { + Set commonColumns = Set.of("code", "caseFilingCode", "name", "createUserName", "typeName", + "sessionName", "delegationName", "curTaskName", "instanceState"); + Map> columns = new HashMap<>(); + columns.put("delegation", commonColumns); + columns.put("preAudit", 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", + "caseFilingResult", "delegationName", "curTaskName", "instanceState")); + columns.put("committeeFilingUnit", Set.of("code", "caseFilingCode", "name", "typeName", "sessionName", + "caseFilingResult", "delegationName", "curTaskName", "instanceState")); + columns.put("schoolLeaderApproval", Set.of("code", "caseFilingCode", "name", "typeName", "undertakeUnits", + "merge", "curTaskName", "auditUser", "instanceState")); + columns.put("unitReply", Set.of("code", "caseFilingCode", "name", "typeName", "caseFilingResult", + "merge", "underTakeName", "underTakeIsMaster", "curTaskName", "auditUser", "instanceState")); + columns.put("feedbackEvaluation", Set.of("code", "caseFilingCode", "name", "typeName", "merge", + "curTaskName", "instanceState")); + return Collections.unmodifiableMap(columns); + } + /** * 按白名单字段对统计结果执行后端排序,数字按数值比较,文本按中文区域规则比较。 * diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/caseCheck/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/caseCheck/index.html index 28faf0c1..69840198 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/caseCheck/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/caseCheck/index.html @@ -35,6 +35,7 @@ layout("/layouts/platform.html"){ + 导出 已审核 未审核 @@ -147,6 +148,7 @@ 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"}, @@ -172,6 +174,15 @@ layout("/layouts/platform.html"){ this.listUnion() }, methods: { + exportList() { + this.$downLoad(loc() + "/exportList", { + pageForm: JSON.stringify(this.pageForm), + approval: this.pageForm.approval, + tableColumns: JSON.stringify(this.tableColumns.filter(function (item) { + return item.visible !== false + })) + }) + }, openView(row) { this.$refs.guava.edit(() => { this.showApprovalForm = false diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/committeeFiling/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/committeeFiling/index.html index 62f6055e..272d0ca5 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/committeeFiling/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/committeeFiling/index.html @@ -59,6 +59,7 @@ layout("/layouts/platform.html"){ + 导出 并案审核 已审核 @@ -200,6 +201,7 @@ layout("/layouts/platform.html"){ return { tableColumns: [ { label: "提案编号", prop: "code" }, + { label: "立案编号", prop: "caseFilingCode" }, { label: "提案名称", prop: "name", width: "200px" }, { label: "提案类别", prop: "typeName" }, { label: "届次", prop: "sessionName" }, @@ -229,6 +231,15 @@ layout("/layouts/platform.html"){ } }, methods: { + exportList() { + this.$downLoad(loc() + "/exportList", { + pageForm: JSON.stringify(this.pageForm), + approval: this.pageForm.approval, + tableColumns: JSON.stringify(this.tableColumns.filter(function (item) { + return item.visible !== false + })) + }) + }, /** * “作为建议”不再区分主办/协办,只保留办理单位; * 切到“不予立案”等结果时,同时清空已选单位,避免旧值被误提交。 diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/committeeFilingUnit/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/committeeFilingUnit/index.html index 9dbd7349..fc101a00 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/committeeFilingUnit/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/committeeFilingUnit/index.html @@ -59,6 +59,7 @@ layout("/layouts/platform.html"){ + 导出 @@ -208,6 +209,7 @@ layout("/layouts/platform.html"){ return { tableColumns: [ {label: "提案编号", prop: "code"}, + {label: "立案编号", prop: "caseFilingCode"}, {label: "提案名称", prop: "name", width: "200px"}, {label: "提案类别", prop: "typeName"}, {label: "届次", prop: "sessionName"}, @@ -231,6 +233,15 @@ layout("/layouts/platform.html"){ } }, methods: { + exportList() { + this.$downLoad(loc() + "/exportList", { + pageForm: JSON.stringify(this.pageForm), + approval: this.pageForm.approval, + tableColumns: JSON.stringify(this.tableColumns.filter(function (item) { + return item.visible !== false + })) + }) + }, /** * “作为建议”场景下只保留办理单位,协办单位和立案类型都要及时清空, * 避免旧表单值跟着一起提交到流程里。 diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/delegation/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/delegation/index.html index 580fe080..04d3bd1d 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/delegation/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/delegation/index.html @@ -25,6 +25,7 @@ layout("/layouts/platform.html"){ + 导出 已审核 未审核 @@ -107,6 +108,7 @@ layout("/layouts/platform.html"){ return { tableColumns: [ {label: "提案编号", prop: "code"}, + {label: "立案编号", prop: "caseFilingCode"}, {label: "提案名称", prop: "name", width: "200px"}, {label: "提案人", prop: "createUserName", width: "80px"}, {label: "提案类别", prop: "typeName"}, @@ -122,6 +124,15 @@ layout("/layouts/platform.html"){ } }, methods: { + exportList() { + this.$downLoad(loc() + "/exportList", { + pageForm: JSON.stringify(this.pageForm), + approval: this.pageForm.approval, + tableColumns: JSON.stringify(this.tableColumns.filter(function (item) { + return item.visible !== false + })) + }) + }, openView(row) { this.$refs.guava.edit(() => { this.showApprovalForm = false diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/feedback/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/feedback/index.html index dbcbff21..f72b54e6 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/feedback/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/feedback/index.html @@ -25,7 +25,8 @@ layout("/layouts/platform.html"){ - + + 导出 已审核 未审核 @@ -119,6 +120,7 @@ layout("/layouts/platform.html"){ return { tableColumns: [ {label: "提案编号", prop: "code"}, + {label: "立案编号", prop: "caseFilingCode"}, {label: "提案名称", prop: "name", width: "200px"}, {label: "提案类别", prop: "typeName"}, {label: "是否并案", prop: "merge"}, @@ -135,6 +137,15 @@ layout("/layouts/platform.html"){ } }, methods: { + exportList() { + this.$downLoad(loc() + "/exportList", { + pageForm: JSON.stringify(this.pageForm), + approval: this.pageForm.approval, + tableColumns: JSON.stringify(this.tableColumns.filter(function (item) { + return item.visible !== false + })) + }) + }, openView(row) { this.$refs.guava.edit(() => { this.showApprovalForm = false diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/preAudit/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/preAudit/index.html index dddbadb6..11b1c3f6 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/preAudit/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/preAudit/index.html @@ -36,6 +36,7 @@ layout("/layouts/platform.html"){ + 导出 已审核 未审核 @@ -111,6 +112,7 @@ 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"}, @@ -134,6 +136,15 @@ layout("/layouts/platform.html"){ this.listUnion() }, methods: { + exportList() { + this.$downLoad(loc() + "/exportList", { + pageForm: JSON.stringify(this.pageForm), + approval: this.pageForm.approval, + tableColumns: JSON.stringify(this.tableColumns.filter(function (item) { + return item.visible !== false + })) + }) + }, openView(row) { this.$refs.guava.edit(() => { this.showApprovalForm = false diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/schoolLeaderApproval/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/schoolLeaderApproval/index.html index 195c9407..85cbd8e2 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/schoolLeaderApproval/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/schoolLeaderApproval/index.html @@ -58,7 +58,8 @@ layout("/layouts/platform.html"){ - + + 导出 已审核 未审核 @@ -145,6 +146,7 @@ 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"}, @@ -164,6 +166,15 @@ layout("/layouts/platform.html"){ } }, methods: { + exportList() { + this.$downLoad(loc() + "/exportList", { + pageForm: JSON.stringify(this.pageForm), + approval: this.pageForm.approval, + tableColumns: JSON.stringify(this.tableColumns.filter(function (item) { + return item.visible !== false + })) + }) + }, /** * 校领导审批列表需要按立案结果切换办理单位展示口径: * 确定立案显示主办/协办,作为建议只显示办理单位。 diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/unitReply/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/unitReply/index.html index 8f3a595f..ad3ffac3 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/unitReply/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/unitReply/index.html @@ -97,6 +97,7 @@ layout("/layouts/platform.html"){ + 导出 已审核 未审核 @@ -288,6 +289,7 @@ layout("/layouts/platform.html"){ return { tableColumns: [ {label: "提案编号", prop: "code"}, + {label: "立案编号", prop: "caseFilingCode"}, {label: "提案名称", prop: "name", width: "200px"}, {label: "提案类别", prop: "typeName"}, {label: "立案结果", prop: "caseFilingResult"}, @@ -319,6 +321,15 @@ layout("/layouts/platform.html"){ } }, methods: { + exportList() { + this.$downLoad(loc() + "/exportList", { + pageForm: JSON.stringify(this.pageForm), + approval: this.pageForm.approval, + tableColumns: JSON.stringify(this.tableColumns.filter(function (item) { + return item.visible !== false + })) + }) + }, openView(row) { this.$refs.guava.edit(() => { this.showApprovalForm = false From 73954cf4e71efa28ac959b7611e7e882f56802a2 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 15 Jul 2026 11:13:08 +0800 Subject: [PATCH 3/4] commit --- .../common/ProposalCommonServiceImpl.java | 70 +++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java index 4e2188d0..b19294ff 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java @@ -632,8 +632,11 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp } // 按任务节点分组 - Map> taskGroups = doneTaskVos.stream().collect(Collectors.groupingBy(ProcessTaskVO::getDisplayName)); + Map> taskGroups = doneTaskVos.stream() + .filter(Objects::nonNull) + .collect(Collectors.groupingBy(ProcessTaskVO::getDisplayName)); docData.putAll(taskGroups); + putProposalExportTaskAliases(id, taskGroups, docData); // 提案附议 List secondInfos = new ArrayList<>(); @@ -704,22 +707,79 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp Configure config = Configure.builder() .bind("secondedList", policy) .bind("seconders", policy) + .bind("提案附议", policy) + .bind("提案委委员审议", policy) .bind("brief", htmlRenderPolicy) .bind("measures", htmlRenderPolicy) .bind("taskFormData.tf_opinion", htmlRenderPolicy) .build(); - Map renderDocCellDataMap = MapUtil.of("proposal", docCellDataMap); - renderDocCellDataMap.put("secondedList", secondInfos); - renderDocCellDataMap.put("delegationAuditList", delegationAuditList); + // 数据库模板使用平铺占位符(例如 {{code}}、{{sessionName}}),不能将基础字段嵌套到 proposal 节点。 + // 流程节点分组已写入 docData,附议人与代表团审核列表也必须写入同一上下文,才能被模板的循环标签识别。 + docData.put("secondedList", secondInfos); + docData.put("delegationAuditList", delegationAuditList); try { - XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("proposal"), config).render(renderDocCellDataMap).writeAndClose(byteArrayOutputStream); + XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("proposal"), config).render(docData).writeAndClose(byteArrayOutputStream); } catch (IOException e) { log.error("提案导出失败{},提案id:{}", e.getMessage(), id); } } + /** + * 将现行工作流节点映射到历史提案表模板使用的区块名称,并按主协办单位拆分办理答复。 + * 模板仍使用旧节点名称时,避免已完成的意见和答复因键名不一致而无法渲染。 + * + * @param proposalId 提案ID,用于查询主办单位 + * @param taskGroups 已完成任务按显示名称分组后的数据 + * @param docData Word 模板的平铺渲染数据 + */ + private void putProposalExportTaskAliases(String proposalId, Map> taskGroups, + NutMap docData) { + putTaskAlias(docData, "提案委主任审核", taskGroups.get("校工会预审核")); + putTaskAlias(docData, "提案委主任意见", taskGroups.get("提案委员会立案")); + putTaskAlias(docData, "提案委委员审议", taskGroups.get("委员会确认承办单位")); + + List replyTasks = taskGroups.get("承办单位答复"); + if (Lang.isEmpty(replyTasks)) { + return; + } + Sql masterUnitSql = Sqls.create("SELECT unitName FROM proposal_reply_unit WHERE proposalId = @proposalId AND isMaster = 1") + .setParam("proposalId", proposalId); + List masterUnitRows = listMap(masterUnitSql); + Set masterUnitNames = masterUnitRows.stream() + .map(row -> row.getString("unitName")) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + + List masterReplyTasks = new ArrayList<>(); + List slaveReplyTasks = new ArrayList<>(); + for (ProcessTaskVO replyTask : replyTasks) { + Dict taskFormData = replyTask.getTaskFormData(); + String unitName = taskFormData == null ? null : taskFormData.getStr("unitName"); + if (masterUnitNames.contains(unitName)) { + masterReplyTasks.add(replyTask); + } else { + slaveReplyTasks.add(replyTask); + } + } + putTaskAlias(docData, "主办单位答复", masterReplyTasks); + putTaskAlias(docData, "协办单位答复", slaveReplyTasks); + } + + /** + * 仅在存在已完成任务时写入模板区块,空任务不触发 Word 条件标签。 + * + * @param docData Word 模板的平铺渲染数据 + * @param templateKey Word 模板中的条件或循环标签名称 + * @param tasks 对应的已完成流程任务 + */ + private void putTaskAlias(NutMap docData, String templateKey, List tasks) { + if (Lang.isNotEmpty(tasks)) { + docData.put(templateKey, tasks); + } + } + @Override public void exportProposalFeedBackAsDocx(String id, ByteArrayOutputStream byteArrayOutputStream) { if (id == null || id.isEmpty()) { From 3425888c9142bde4bdee80c4f9e912b41c5a7814 Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Wed, 15 Jul 2026 11:47:47 +0800 Subject: [PATCH 4/4] commit --- .../sys/controller/SysLoginController.java | 130 +++- .../TeacherCongressInstitutionController.java | 50 +- ...TeacherCongressInstitutionUserService.java | 2 +- ...herCongressInstitutionUserServiceImpl.java | 25 +- .../resources/views/layouts/v4/home/act.js | 25 +- .../resources/views/layouts/v4/home/entry.js | 45 +- .../views/platform/sys/data/tool/index.html | 604 +++++++++++------- .../resources/views/platform/sys/login.html | 28 +- .../activity/sports/infoManage/addActivity.js | 9 + .../zhgh/activity/sports/reading/index.html | 8 +- .../teachercongress/institution/basicTable.js | 162 ++--- .../teachercongress/institution/index.html | 60 +- .../teachercongress/institution/tree.js | 23 +- .../teachercongress/institution/userTable.js | 109 +--- 14 files changed, 691 insertions(+), 589 deletions(-) diff --git a/src/main/java/com/budwk/app/sys/controller/SysLoginController.java b/src/main/java/com/budwk/app/sys/controller/SysLoginController.java index e78a0a5d..7ad931a5 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysLoginController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysLoginController.java @@ -41,15 +41,11 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.security.KeyFactory; import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Base64; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; @IocBean @@ -58,6 +54,11 @@ import java.util.UUID; @Api(tags = "登录") public class SysLoginController { private static final Log log = Logs.get(); + private static final LoginRsaKeyRing LOGIN_RSA_KEY_RING = new LoginRsaKeyRing(); + /** + * TODO: 临时屏蔽 localhost 平台登录验证码,恢复时改为 false。 + */ + private static final boolean DISABLE_LOGIN_CAPTCHA = true; @Inject private SysUserService sysUserService; @Inject @@ -123,14 +124,20 @@ public class SysLoginController { } try { // 验证码校验 - try { - validateService.checkCode(captchaKey, captchaCode); - } catch (BaseException e) { - return Result.error(e.getMessage()); + if (!isLoginCaptchaDisabled(req)) { + try { + validateService.checkCode(captchaKey, captchaCode); + } catch (BaseException e) { + return Result.error(e.getMessage()); + } } // 解密密码 - String decryptPwd = validateService.decryptPwd(keyId, password); + LoginRsaKey rsaKey = LOGIN_RSA_KEY_RING.getPrivateKey(keyId); + if (rsaKey == null) { + throw new BaseException("Login key expired, please refresh the login page"); + } + String decryptPwd = RsaUtils.decrypt(password, rsaKey.privateKey); if (decryptPwd == null) { throw new BaseException("用户登录失败"); } @@ -153,6 +160,17 @@ public class SysLoginController { } } + private boolean isLoginCaptchaDisabled(HttpServletRequest req) { + if (!DISABLE_LOGIN_CAPTCHA || req == null) { + return false; + } + String serverName = req.getServerName(); + return "localhost".equalsIgnoreCase(serverName) + || "127.0.0.1".equals(serverName) + || "0:0:0:0:0:0:0:1".equals(serverName) + || "::1".equals(serverName); + } + @At(value = "/platform/sso/login", top = true) @Ok("re") @ApiOperation("用户cas登录统一入口") @@ -267,25 +285,89 @@ public class SysLoginController { @ApiOperation("获取公钥") public Object publicKey() { try { - // 生成密钥对 - KeyPair keyPair = RsaUtils.generateKeyPair(); - String publicKeyStr = RsaUtils.getPublicKeyBase64(keyPair.getPublic()); - String privateKeyStr = RsaUtils.getPrivateKeyBase64(keyPair.getPrivate()); - - // 生成 UUID 作为 keyId - String keyId = UUID.randomUUID().toString().replace("-", ""); - - // 私钥存入 Redis,5 分钟过期,也防止恶意刷密钥 - redisService.setex(RedisConstant.RSA_KEY_PREFIX + keyId, 5 * 60, privateKeyStr); - - // 返回给前端 + LoginRsaKey rsaKey = LOGIN_RSA_KEY_RING.currentKey(); return Result.success(Map.of( - "publicKey", publicKeyStr, - "keyId", keyId + "publicKey", rsaKey.publicKey, + "keyId", rsaKey.keyId, + "expireAt", rsaKey.encryptExpireAt )); } catch (Exception e) { log.error("生成 RSA 密钥失败", e); return Result.error("系统异常"); } } + + private static class LoginRsaKeyRing { + private static final long ACTIVE_MILLIS = 30 * 60 * 1000L; + private static final long DECRYPT_GRACE_MILLIS = 10 * 60 * 1000L; + private final ConcurrentHashMap keys = new ConcurrentHashMap<>(); + private volatile LoginRsaKey current; + + LoginRsaKey currentKey() throws Exception { + long now = System.currentTimeMillis(); + LoginRsaKey key = current; + if (key != null && now < key.encryptExpireAt) { + return key; + } + synchronized (this) { + key = current; + now = System.currentTimeMillis(); + if (key == null || now >= key.encryptExpireAt) { + key = generateKey(now); + current = key; + keys.put(key.keyId, key); + cleanup(now); + } + return key; + } + } + + LoginRsaKey getPrivateKey(String keyId) { + if (StrUtil.isBlank(keyId)) { + return null; + } + long now = System.currentTimeMillis(); + LoginRsaKey key = keys.get(keyId); + if (key == null) { + return null; + } + if (now >= key.decryptExpireAt) { + keys.remove(keyId); + return null; + } + return key; + } + + private LoginRsaKey generateKey(long now) throws Exception { + KeyPair keyPair = RsaUtils.generateKeyPair(); + String keyId = UUID.randomUUID().toString().replace("-", ""); + return new LoginRsaKey( + keyId, + RsaUtils.getPublicKeyBase64(keyPair.getPublic()), + keyPair.getPrivate(), + now + ACTIVE_MILLIS, + now + ACTIVE_MILLIS + DECRYPT_GRACE_MILLIS + ); + } + + private void cleanup(long now) { + keys.entrySet().removeIf(entry -> now >= entry.getValue().decryptExpireAt); + } + } + + private static class LoginRsaKey { + private final String keyId; + private final String publicKey; + private final PrivateKey privateKey; + private final long encryptExpireAt; + private final long decryptExpireAt; + + private LoginRsaKey(String keyId, String publicKey, PrivateKey privateKey, long encryptExpireAt, long decryptExpireAt) { + this.keyId = keyId; + this.publicKey = publicKey; + this.privateKey = privateKey; + this.encryptExpireAt = encryptExpireAt; + this.decryptExpireAt = decryptExpireAt; + } + } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/controller/TeacherCongressInstitutionController.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/controller/TeacherCongressInstitutionController.java index 7e574d3c..2dcb71d7 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/controller/TeacherCongressInstitutionController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/controller/TeacherCongressInstitutionController.java @@ -25,7 +25,6 @@ import org.nutz.dao.util.cri.SqlExpressionGroup; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.lang.Strings; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; @@ -69,15 +68,11 @@ public class TeacherCongressInstitutionController { Teacher_congress_institution rootInstitution = new Teacher_congress_institution(); rootInstitution.setId("0"); rootInstitution.setParentId(""); - rootInstitution.setName("两代会组织机构"); + rootInstitution.setName("教代会机构"); rootInstitution.setLocation(0); institutionList.add(rootInstitution); - List> treeNodes = institutionList.stream().map(institution -> { - TreeNode treeNode = new TreeNode<>(institution.getId(), institution.getParentId(), institution.getName(), institution.getLocation()); - treeNode.setExtra(Map.of("code", Strings.sNull(institution.getCode()))); - return treeNode; - }).toList(); + List> treeNodes = institutionList.stream().map(institution -> new TreeNode<>(institution.getId(), institution.getParentId(), institution.getName(), institution.getLocation())).toList(); List> treeList = TreeUtil.build(treeNodes, ""); return Result.success(treeList); } @@ -102,16 +97,6 @@ public class TeacherCongressInstitutionController { return Result.success(Map.of("treeFlat", children, "treeList", treeList)); } - @At - @SaCheckPermission("tc.institution") - public Result specialCommitteeRoleOptions() { - Sys_dict parent = sysDictService.fetch(Cnd.where(Sys_dict::getCode, "=", "SPECIAL_COMMITTEE_ROLES")); - if (ObjectUtil.isEmpty(parent)) { - return Result.success(List.of()); - } - List children = sysDictService.query(Cnd.where(Sys_dict::getPath, "like", parent.getPath() + "%").and(Sys_dict::getId, "!=", parent.getId()).asc(Sys_dict::getLocation)); - return Result.success(children); - } /** * 机构分页查询 @@ -128,7 +113,6 @@ public class TeacherCongressInstitutionController { cnd.and("parentId", "=", parentId); cnd.and("sessionId", "=", sessionId); cnd.asc("location"); - cnd.asc("code"); Pagination pagination = baseService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), "teacher_congress_institution", cnd); return Result.success(pagination); } @@ -145,7 +129,7 @@ public class TeacherCongressInstitutionController { if (dao.count(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getCode, "=", institution.getCode()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId())) > 0) { return Result.error("机构已存在"); } - if (StrUtil.isNotBlank(institution.getParentId()) && !institution.getParentId().equals("0") && dao.count(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getId, "=", institution.getParentId()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId())) == 0) { + if (StrUtil.isNotBlank(institution.getParentId()) && !institution.getParentId().equals("0")) { Sys_dict sysDict = sysDictService.fetch(Cnd.where(Sys_dict::getId, "=", institution.getParentId())); Teacher_congress_institution parentInstitution = dao.fetch(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getCode, "=", sysDict.getCode()) @@ -167,34 +151,10 @@ public class TeacherCongressInstitutionController { * @param id * @return */ - @At - @SaCheckPermission("tc.institution") - public Result update(Teacher_congress_institution institution) { - Teacher_congress_institution dbInstitution = dao.fetch(Teacher_congress_institution.class, institution.getId()); - if (ObjectUtil.isEmpty(dbInstitution)) { - return Result.error("机构不存在"); - } - dbInstitution.setName(institution.getName()); - dbInstitution.setIntroduce(institution.getIntroduce()); - dbInstitution.setLocation(institution.getLocation()); - dao.update(dbInstitution); - return Result.success(); - } - @At @SaCheckPermission("tc.institution") @Aop(TransAop.READ_COMMITTED) public Result delete(@Valid String id) { - Teacher_congress_institution institution = dao.fetch(Teacher_congress_institution.class, id); - if (ObjectUtil.isEmpty(institution)) { - return Result.error("机构不存在"); - } - List siblingList = dao.query(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getParentId, "=", institution.getParentId()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId()).asc(Teacher_congress_institution::getLocation).asc(Teacher_congress_institution::getCode)); - for (int i = 0; i < siblingList.size() && i < 6; i++) { - if (id.equals(siblingList.get(i).getId())) { - return Result.error("你选择的组织机构是两代会基本机构,不允许删除。"); - } - } dao.delete(Teacher_congress_institution.class, id); dao.clear(Teacher_congress_institution_user.class, Cnd.where("institutionId", "=", id)); return Result.success(); @@ -254,8 +214,8 @@ public class TeacherCongressInstitutionController { @SaCheckPermission("tc.institution") @SLog(tag = "教代会-机构设置", msg = "添加人员") @Aop(TransAop.READ_COMMITTED) - public Result userInsert(@Valid String userId, @Valid String institutionId, @Valid String sessionId, @Valid String identity, String roleCode) { - teacherCongressInstitutionUserService.userInsert(userId, institutionId, sessionId, identity, roleCode); + public Result userInsert(@Valid String userId, @Valid String institutionId, @Valid String sessionId, @Valid String identity) { + teacherCongressInstitutionUserService.userInsert(userId, institutionId, sessionId, identity); return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/service/TeacherCongressInstitutionUserService.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/service/TeacherCongressInstitutionUserService.java index 83472c4b..302aa97f 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/service/TeacherCongressInstitutionUserService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/service/TeacherCongressInstitutionUserService.java @@ -14,7 +14,7 @@ public interface TeacherCongressInstitutionUserService extends BaseService
- + +
{{ item.name }}
@@ -167,7 +168,7 @@ const entry = { .app-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(108px, 1fr)); justify-items: center; justify-content: start; gap: 12px 18px; @@ -185,13 +186,17 @@ const entry = { display: flex; flex-direction: column; align-items: center; + justify-content: flex-start; border-radius: 8px; background: #fff; width: 100%; - max-width: 100%; + max-width: 108px; + min-width: 0; + box-sizing: border-box; padding: 6px; cursor: pointer; transition: transform 0.2s ease, box-shadow 0.2s ease; + overflow: hidden; } .app-item:hover { @@ -200,24 +205,46 @@ const entry = { } .app-icon { - width: 46px; - height: 46px; + width: 56px; + height: 56px; margin-bottom: 8px; + display: flex; + align-items: center; + justify-content: center; + flex: 0 0 56px; + overflow: hidden; + border-radius: 8px; + background: #f5f8ff; } - .app-icon img { - width: 100%; - height: 100%; - object-fit: contain; + .app-icon img, + .app-icon-img { + display: block; + width: 56px !important; + height: 56px !important; + max-width: 56px !important; + max-height: 56px !important; + object-fit: contain !important; + border-radius: 8px; + } + + .app-icon-fallback { + color: #409eff; + font-size: 24px; } .app-name { min-height: 34px; + width: 100%; font-size: 12px; color: #333; line-height: 1.4; word-break: break-word; text-align: center; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; } .app-empty { diff --git a/src/main/resources/views/platform/sys/data/tool/index.html b/src/main/resources/views/platform/sys/data/tool/index.html index c632e51b..b7ec0121 100644 --- a/src/main/resources/views/platform/sys/data/tool/index.html +++ b/src/main/resources/views/platform/sys/data/tool/index.html @@ -4,225 +4,268 @@ layout("/layouts/platform.html"){
- -
- 高级工具 + +
+ DataKid
-
-
- - - - - - +
+
+ + + + + + -
-
-
- - - 搜索 - 当前选择:{{ formData.tableName }} -
- - -
- {{ item.table_name }} - ({{ item.table_comment }}) + + + +
+ + + + + 当前选中:{{ formData.tableName }} + +
+ + + + {{ item.table_name }}{{ item.table_comment ? " ( " + item.table_comment + " )" : "" }} rows: {{ item.table_rows || 0 }} -
-
-
-
+ + + + -
-
- - - 搜索 - {{ formData.tableName }} -
- - -
- {{ item.column_name }} - ({{ item.column_comment }}) + + +
+ + + + + {{ formData.tableName }} + +
+ + + + {{ item.column_name }}{{ item.column_comment ? " ( " + item.column_comment + " )" : "" }} {{ item.column_type }} -
-
-
-
+ + + + -
- - - - - - - - - - - -
+ + + + + + + + + + + + + + + -
-
- - + + + + + + 追加或更新 仅追加 @@ -230,13 +273,8 @@ layout("/layouts/platform.html"){ - - + + - + - +
将文件拖到此处,或点击上传
-
仅支持 `xls`、`xlsx` 文件
+
只能上传 xls/xlsx 文件
-
-
+ + -
-
- -
- 共 {{ result.total || 0 }} 条,成功 {{ result.success || 0 }} 条 + + + +
{{ resultSummary }}
+
+ + 下载错误记录 +
-
- 下载错误记录 -
-
-
-
+ + + -
@@ -316,11 +359,16 @@ layout("/layouts/platform.html"){ relation: [], plugins: [], fileList: [], + cacheTableName: "", result: { total: 0, success: 0, cacheKey: "" }, + formRules: { + method: [{ required: true, message: "必填", trigger: ["blur", "change"] }], + field: [{ required: true, message: "必填", trigger: ["blur", "change"] }] + }, formData: { tableName: "", columns: [], @@ -331,6 +379,29 @@ layout("/layouts/platform.html"){ } }, computed: { + primaryButtonText() { + if (this.active === 3 && this.formData.method === 3) { + return "更新" + } + return "下一步" + }, + resultSummary() { + const total = this.result.total || 0 + const success = this.result.success || 0 + const fail = Math.max(total - success, 0) + if (fail > 0) { + return "总记录数 " + total + " 条,成功 " + success + " 条,失败 " + fail + " 条!" + } + return "总记录数 " + total + " 条,成功 " + success + " 条!" + }, + resultProgressStatus() { + const total = this.result.total || 0 + const success = this.result.success || 0 + if (total > 0 && success < total) { + return "exception" + } + return "success" + }, resultPercentage() { if (!this.result.total) { return 0 @@ -339,6 +410,7 @@ layout("/layouts/platform.html"){ } }, methods: { + loc, normalizeRecord(item) { const normalized = {} Object.keys(item || {}).forEach(key => { @@ -398,66 +470,75 @@ layout("/layouts/platform.html"){ .filter(item => this.formData.columns.includes(item.column_name)) .map(item => Object.assign({}, item, { relation: "" })) }, - async nextStep() { + async next() { if (this.active === 0) { if (!this.formData.tableName) { - this.$message.warning("请选择数据表") + this.$notify({ + title: "警告", + message: "请选择数据表", + type: "warning" + }) return } - await this.loadColumns() - this.formData.columns = [] - this.formData.field = "" + if (this.cacheTableName !== this.formData.tableName) { + await this.loadColumns() + this.formData.columns = [] + this.formData.field = "" + this.cacheTableName = this.formData.tableName + } } else if (this.active === 1) { if (!this.formData.columns.length) { - this.$message.warning("请至少选择一个字段") + this.$notify({ + title: "警告", + message: "请选择数据列", + type: "warning" + }) return } this.buildRelation() } else if (this.active === 2) { - const hasEmptyRelation = this.relation.some(item => !item.relation) - if (hasEmptyRelation) { - this.$message.warning("请填写所有 Excel 表头对应关系") + if (this.relation.find(item => !item.relation)) { + this.$notify({ + title: "警告", + message: "存在未填写的目标列", + type: "warning" + }) return } } else if (this.active === 3) { - const success = await this.submitImport() + const success = await this.doSubmit() if (!success) { return } } - this.active += 1 + + this.$refs.carousel.next() + this.active++ }, - prevStep() { - if (this.active > 0) { - this.active -= 1 + prev() { + if (this.active <= 0) { + return } + this.$refs.carousel.prev() + this.active-- }, - handleFileRemove(file, fileList) { - this.fileList = fileList - }, - handleFileChange(file, fileList) { - const extension = ((file.name || "").split(".").pop() || "").toLowerCase() - const removeIndex = fileList.findIndex(item => item.uid === file.uid) - if (!["xls", "xlsx"].includes(extension)) { - this.$message.warning("仅支持 xls、xlsx 文件") - if (removeIndex > -1) { - fileList.splice(removeIndex, 1) - } - } else if (file.size === 0) { - this.$message.warning("上传文件不能为空") - if (removeIndex > -1) { - fileList.splice(removeIndex, 1) - } - } - this.fileList = fileList - }, - async submitImport() { - if ([1, 3].includes(this.formData.method) && !this.formData.field) { - this.$message.warning("请选择关键字段") + validateSubmitForm() { + let validForm = true + this.$refs.form.validate(valid => { + validForm = valid + }) + if (!validForm || !this.fileList.length) { + this.$notify({ + title: "警告", + message: "存在未填写的必填项", + type: "warning" + }) return false } - if (!this.fileList.length) { - this.$message.warning("请上传 Excel 文件") + return true + }, + async doSubmit() { + if (!this.validateSubmitForm()) { return false } @@ -476,18 +557,45 @@ layout("/layouts/platform.html"){ "Content-Type": "multipart/form-data" } }) + if (resp.code !== 0) { - this.$message.error(resp.msg) + this.$notify.error({ + title: "错误", + message: resp.msg + }) + this.result = { total: 0, success: 0, cacheKey: "" } return false } + this.result = resp.data || { total: 0, success: 0, cacheKey: "" } - this.$message.success(resp.msg) return true } finally { this.loading = false } }, - restart() { + handleFileRemove(file, fileList) { + this.fileList = fileList + }, + handleFileChange(file, fileList) { + const extension = ((file.name || "").split(".").pop() || "").toLowerCase() + const remove = () => { + const removeIndex = fileList.findIndex(item => item.uid === file.uid) + if (removeIndex > -1) { + fileList.splice(removeIndex, 1) + } + } + + if (file.size === 0) { + this.$message.warning("您选择的是空文件") + remove() + } else if (!["xls", "xlsx"].includes(extension)) { + this.$message.warning("上传文件只能是 xls/xlsx 格式") + remove() + } + + this.fileList = fileList + }, + carry() { this.active = 0 this.tableKeyword = "" this.columnKeyword = "" @@ -495,6 +603,7 @@ layout("/layouts/platform.html"){ this.filteredColumns = [] this.relation = [] this.fileList = [] + this.cacheTableName = "" this.result = { total: 0, success: 0, @@ -511,6 +620,7 @@ layout("/layouts/platform.html"){ this.$refs.upload.clearFiles() } this.filterTables() + this.$refs.carousel.setActiveItem(0) } }, async created() { diff --git a/src/main/resources/views/platform/sys/login.html b/src/main/resources/views/platform/sys/login.html index bb969228..7c89de05 100644 --- a/src/main/resources/views/platform/sys/login.html +++ b/src/main/resources/views/platform/sys/login.html @@ -52,7 +52,7 @@ prefix-icon="el-icon-lock" > - +