新增功能:教代会意见需要导出

bug修改:
1、无法导出未体检人员
2、品牌活动加了微信二维码,无法删除
This commit is contained in:
2026-05-11 14:30:37 +08:00
parent 51eb890f2e
commit d615e5972e
7 changed files with 279 additions and 46 deletions
@@ -263,10 +263,12 @@ public class HealthCheckupSingleServiceImpl extends BaseServiceImpl<HealthChecku
hcu.unitName,
hcu.unionName,
u.idcard idCard,
u.mobile
u.mobile,
hcp.name projectName
FROM
`health_checkup_user` hcu
LEFT JOIN `vw_user` u ON u.id = hcu.userId
LEFT JOIN `health_checkup_project` hcp ON hcp.id = hcu.projectId
$condition
""");
sql.setVar("projectCnd", projectId);
@@ -2,6 +2,7 @@ package com.budwk.app.zhgh.democratic.opinion.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.param.ExportTableColumns;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
@@ -9,6 +10,7 @@ import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.opinion.param.OpinionSearchParam;
import com.budwk.app.zhgh.democratic.opinion.service.OpinionSummaryService;
import com.budwk.app.zhgh.democratic.opinion.service.common.OpinionCommonService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig;
import io.swagger.annotations.Api;
@@ -26,6 +28,7 @@ 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;
@@ -47,6 +50,18 @@ public class OpinionSummaryController {
private Dao dao;
@Inject
private OpinionCommonService opinionCommonService;
@Inject
private OpinionSummaryService opinionSummaryService; //查询统计
@Inject
private OpinionSchoolAuditController opinionSchoolAuditController; //校工会审核
@Inject
private OpinionOfficeAuditController opinionUndertakeController; //两办审核
@Inject
private OpinionUnitReplyController opinionUnitReplyController; //承办答复
@Inject
private OpinionSchoolSubmitController opinionSchoolSubmitController; //校工会提交
@Inject
private OpinionFeedbackController opinionFeedbackController; //代表评价
@At("")
@Ok("beetl:/platform/zhgh/democratic/opinion/transact/summary/index.html")
@@ -59,48 +74,19 @@ public class OpinionSummaryController {
@Aop(TransAop.READ_COMMITTED)
public Result pageData(@Valid OpinionSearchParam pageForm,
@Param(value = "String") String sessionId) {
Sql sql = Sqls.create("""
SELECT
info.*,
type.name AS typeName,
tcs.fullName AS sessionName,
tcd.`name` AS delegationName,
ins.id AS instanceId,
ins.state instanceState,
ins.processDefineId instanceProcessDefineId,
t.displayName taskName
FROM
opinion_info info
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
LEFT JOIN (
SELECT
processInstanceId,
displayName,
createdAt,
ROW_NUMBER() OVER (PARTITION BY processInstanceId ORDER BY createdAt DESC) AS rn
FROM wf_process_task
) t ON t.processInstanceId = ins.id AND t.rn = 1
LEFT JOIN opinion_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 vw_user u ON u.id = info.createUserId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("ins.state", "=", ProcessInstanceStateEnum.FINISHED.getCode());
cnd.andEX("info.sessionId", "=", sessionId);
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_SECRETARY.name(), RoleConstant.SCHOOL_HEADMASTER.name())) {
// 其他校领导看自己单位
cnd.and("u.unitId", "=", SecurityUtil.getUnitId());
}
OpinionSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("info.id");
sql.setCondition(cnd);
Pagination<NutMap> pageVO = opinionCommonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pageVO);
Sql sql = opinionSummaryService.createSql(pageForm, sessionId);
return Result.success(opinionCommonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
}
@At
@Ok("void")
@SaCheckPermission("opinion.summary")
@Aop(TransAop.READ_COMMITTED)
public void exportData(@Valid OpinionSearchParam pageForm,
@Param(value = "String") String sessionId,
@Param("tableColumns") ExportTableColumns[] tableColumns,
HttpServletResponse response) {
// 导出直接复用页面查询条件和当前显示列,确保页面查什么、显示什么就导出什么。
opinionSummaryService.exportData(pageForm, sessionId, tableColumns, response);
}
}
@@ -22,6 +22,8 @@ public class OpinionSearchParam extends PageForm {
private String createUserName;
private String createUserLoginName;
private String createUserKeyword;
private String taskName;
private Boolean approval;
/**
* 构建通用查询参数
@@ -0,0 +1,30 @@
package com.budwk.app.zhgh.democratic.opinion.service;
import com.budwk.app.base.param.ExportTableColumns;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.democratic.opinion.models.OpinionInfo;
import com.budwk.app.zhgh.democratic.opinion.param.OpinionSearchParam;
import org.nutz.dao.sql.Sql;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
public interface OpinionSummaryService extends BaseService<OpinionInfo> {
/**
* 创建查询sql
* @param pageForm
* @param sessionId
*/
Sql createSql(@Valid OpinionSearchParam pageForm, String sessionId);
/**
* 导出查询统计结果
*
* @param pageForm 查询条件
* @param sessionId 教代会ID
* @param tableColumns 当前页面显示列
* @param response 响应对象
*/
void exportData(@Valid OpinionSearchParam pageForm, String sessionId, ExportTableColumns[] tableColumns, HttpServletResponse response);
}
@@ -0,0 +1,151 @@
package com.budwk.app.zhgh.democratic.opinion.service.impl;
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.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.param.ExportTableColumns;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.opinion.models.OpinionInfo;
import com.budwk.app.zhgh.democratic.opinion.param.OpinionSearchParam;
import com.budwk.app.zhgh.democratic.opinion.service.OpinionSummaryService;
import com.budwk.app.zhgh.democratic.opinion.service.OpinionWriteService;
import com.budwk.app.zhgh.democratic.teachercongress.delegation.models.Teacher_congress_delegation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class OpinionSummaryServiceImpl extends BaseServiceImpl<OpinionInfo> implements OpinionSummaryService {
public OpinionSummaryServiceImpl(Dao dao) {
super(dao);
}
@Override
public Sql createSql(OpinionSearchParam pageForm, String sessionId) {
Sql sql = Sqls.create("""
SELECT
info.*,
type.name AS typeName,
tcs.fullName AS sessionName,
tcd.`name` AS delegationName,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'待提交') curTaskName,
IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = t.taskParentId) = 'startTask', 1, 0) canRevoke,
(select MAX(id) from wf_process_task where processInstanceId = ins.id and taskName = 'startTask' AND taskState in (10,20)) AS startTaskId
FROM
opinion_info info
LEFT JOIN opinion_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 `wf_process_instance` ins ON ins.businessNo = info.id
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("t.taskName", "=", pageForm.getTaskName());
cnd.andEX("info.sessionId", "=", sessionId);
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_SECRETARY.name(), RoleConstant.SCHOOL_HEADMASTER.name())) {
// 其他校领导看自己单位
cnd.and("u.unitId", "=", SecurityUtil.getUnitId());
}
OpinionSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("info.id");
sql.setCondition(cnd);
return sql;
}
@Override
public void exportData(OpinionSearchParam pageForm, String sessionId, ExportTableColumns[] tableColumns, HttpServletResponse response) {
// 导出与列表查询复用同一套SQL,确保页面筛选结果和导出结果完全一致。
Sql sql = createSql(pageForm, sessionId);
List<NutMap> dataList = listMap(sql);
dataList.forEach(item -> {
Integer stateCode = Convert.toInt(item.get("instanceState"), null);
item.setv("instanceStateName", getProcessInstanceStateName(stateCode));
item.setv("curTaskName", StrUtil.blankToDefault(item.getString("curTaskName"), "待提交"));
});
List<ExcelExportEntity> entityList = new ArrayList<>();
// 导出列完全跟随前端当前显示列,避免页面列配置与导出列不一致。
if (tableColumns != null && tableColumns.length > 0) {
for (ExportTableColumns tableColumn : tableColumns) {
if (tableColumn == null || StrUtil.hasBlank(tableColumn.getLabel(), tableColumn.getProp())) {
continue;
}
String exportProp = tableColumn.getProp();
if ("instanceState".equals(exportProp)) {
exportProp = "instanceStateName";
}
entityList.add(new ExcelExportEntity(tableColumn.getLabel(), exportProp, 20));
}
}
// 当前端未传列配置时,兜底导出默认关键字段,避免生成空表。
if (entityList.isEmpty()) {
entityList.add(new ExcelExportEntity("意见编号", "code", 20));
entityList.add(new ExcelExportEntity("意见和建议", "name", 40));
entityList.add(new ExcelExportEntity("意见类别", "typeName", 20));
entityList.add(new ExcelExportEntity("建议人", "createUserName", 20));
entityList.add(new ExcelExportEntity("记录人", "suggestUserName", 20));
entityList.add(new ExcelExportEntity("届次", "sessionName", 25));
entityList.add(new ExcelExportEntity("当前节点", "curTaskName", 20));
entityList.add(new ExcelExportEntity("流程状态", "instanceStateName", 20));
}
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
exportParams.setSheetName("意见查询统计");
CommonDownloadUtil.download("意见查询统计.xlsx", ExcelExportUtil.exportExcel(exportParams, entityList, dataList), response);
}
/**
* 将流程状态编码转换为中文描述,便于导出表格直接查看。
*
* @param stateCode 流程状态编码
* @return 流程状态中文名称
*/
private String getProcessInstanceStateName(Integer stateCode) {
if (stateCode == null) {
return "";
}
for (ProcessInstanceStateEnum stateEnum : ProcessInstanceStateEnum.values()) {
if (stateEnum.getCode().equals(stateCode)) {
return stateEnum.getMessage();
}
}
return "";
}
}
@@ -356,9 +356,15 @@ module.exports = {
handleRemove(file, fileList) {
if (this.upload_result_category === "interval") {
// interval 模式下父组件接收的是逗号拼接字符串,删除文件后也必须同步回写,
// 否则表单仍会保留删除前的旧值,提交时会把已删除文件再次带给后端。
if (this.upload_result_type === "id") {
const resultArrayValue = fileList.map((e) => e.response.data)
this.$emit("update:value", resultArrayValue.length > 0 ? resultArrayValue.join(",") : undefined)
}
if (this.upload_result_type === "url") {
const resultArrayValue = fileList.map((e) => e.response.data)
this.$emit("update:value", resultArrayValue.length > 0 ? resultArrayValue.join(",") : undefined)
}
}
@@ -20,11 +20,24 @@ layout("/layouts/platform.html"){
<search-item label="姓名/工号">
<el-input placeholder="请输入姓名或工号" clearable v-model="pageForm.createUserKeyword"></el-input>
</search-item>
<search-item label="流程名称">
<el-select v-model="pageForm.taskName" clearable placeholder="请选择">
<el-option
v-for="item in taskNameOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool :columns.sync="tableColumns"></table-tool>
<table-tool :columns.sync="tableColumns">
<el-button type="primary" size="small" @click="exportData">导出
</el-button>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" ref="tableRef" row-key="id" style="width: 100%">
<el-table-column label="序号" width="50" type="index" :index="indexMethod" fixed="left"></el-table-column>
<el-table-column
@@ -35,6 +48,7 @@ layout("/layouts/platform.html"){
:fixed="column.fixed"
show-overflow-tooltip
v-for="column in tableColumns"
v-if="column.visible !== false"
>
<template v-if="column.prop === 'instanceState'" scope="{row}">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
@@ -78,19 +92,60 @@ layout("/layouts/platform.html"){
{label: "记录人所属讨论组", prop: "suggestUserDelegationName"},
{label: "届次", prop: "sessionName"},
{label: "代表团", prop: "delegationName"},
{label: "当前节点", prop: "curTaskName"},
{label: "流程状态", prop: "instanceState"}
],
pageForm: {},
sessionOptions: [],
taskNameOptions: [
{
value: 'schoolAudit',
label: '校工会审核'
}, {
value: 'startTask',
label: '撰写意见'
}, {
value: 'slave_reply',
label: '协办单位答复'
}, {
value: 'master_reply',
label: '主办单位答复'
}, {
value: 'feedback',
label: '代表评价'
}, {
value: 'schoolSubmit',
label: '校工会提交'
}
]
}
},
methods: {
methods: {
openView(row) {
this.$refs.guava.view(() => {
this.$refs.infoRef.onOpen(row)
})
},
exportData() {
this.$downLoad(loc() + "/exportData", {
sessionId: this.pageForm.sessionId,
name: this.pageForm.name,
createUserKeyword: this.pageForm.createUserKeyword,
taskName: this.pageForm.taskName,
pageOrderName: this.pageForm.pageOrderName,
pageOrderBy: this.pageForm.pageOrderBy,
tableColumns: JSON.stringify(this.tableColumns.filter(function (item) {
return item.visible !== false
}).map(function (item) {
return {
label: item.label,
prop: item.prop
}
}))
})
},
// 教代会
async meetingChange(val) {
this.doSearch()
@@ -108,6 +163,7 @@ layout("/layouts/platform.html"){
}
})
},
},
created() {
this.pageData()