From 1d5061e39a9fdfe58f04caebba986aeedfac080b Mon Sep 17 00:00:00 2001 From: zhangrui Date: Wed, 26 Aug 2026 13:55:37 +0800 Subject: [PATCH] change --- .../OutlayManageUnionStatisticsService.java | 31 ++++++ ...utlayManageUnionStatisticsServiceImpl.java | 104 ++++++++++++++++++ ...cherCongressDelegationCompositionMode.java | 41 +++++++ .../zhgh/club/infoManage/fixedListLayout.css | 33 ++++++ .../zhgh/club/infoManage/fixedListLayout.js | 71 ++++++++++++ .../outlayManage/union/fixedListLayout.css | 65 +++++++++++ .../outlayManage/union/fixedListLayout.js | 75 +++++++++++++ 7 files changed, 420 insertions(+) create mode 100644 src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/service/OutlayManageUnionStatisticsService.java create mode 100644 src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/service/impl/OutlayManageUnionStatisticsServiceImpl.java create mode 100644 src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegation/constant/TeacherCongressDelegationCompositionMode.java create mode 100644 src/main/resources/views/platform/zhgh/club/infoManage/fixedListLayout.css create mode 100644 src/main/resources/views/platform/zhgh/club/infoManage/fixedListLayout.js create mode 100644 src/main/resources/views/platform/zhgh/dayofficework/outlay/outlayManage/union/fixedListLayout.css create mode 100644 src/main/resources/views/platform/zhgh/dayofficework/outlay/outlayManage/union/fixedListLayout.js diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/service/OutlayManageUnionStatisticsService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/service/OutlayManageUnionStatisticsService.java new file mode 100644 index 00000000..eb1bb378 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/service/OutlayManageUnionStatisticsService.java @@ -0,0 +1,31 @@ +package com.budwk.app.zhgh.dayofficework.outlay.outlayManage.service; + +import com.budwk.app.base.service.BaseService; +import org.nutz.lang.util.NutMap; + +/** + * 分工会预算统计服务。 + * + *

统一封装分工会预算使用情况和经费收支情况的全量汇总查询, + * 汇总口径与列表查询保持一致,不受分页参数影响。

+ */ +public interface OutlayManageUnionStatisticsService extends BaseService { + + /** + * 查询分工会预算使用情况的全量合计。 + * + * @param year 统计年度 + * @param unionId 分工会主键,管理员可为空查询全部 + * @return 包含 totalQuota、usedQuota、surplusQuota 的合计数据 + */ + NutMap queryUseDetailSummary(Integer year, String unionId); + + /** + * 查询分工会经费收支使用情况的全量合计。 + * + * @param year 统计年度 + * @param unionId 分工会主键,管理员可为空查询全部 + * @return 包含各项收入、支出和余额字段的合计数据 + */ + NutMap queryIncomeExpenseSummary(Integer year, String unionId); +} diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/service/impl/OutlayManageUnionStatisticsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/service/impl/OutlayManageUnionStatisticsServiceImpl.java new file mode 100644 index 00000000..29a70f56 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/outlayManage/service/impl/OutlayManageUnionStatisticsServiceImpl.java @@ -0,0 +1,104 @@ +package com.budwk.app.zhgh.dayofficework.outlay.outlayManage.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.web.commons.auth.utils.AuthUtil; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.service.OutlayManageUnionStatisticsService; +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; + +/** + * 分工会预算统计服务实现。 + * + *

汇总查询在数据库端完成,避免只汇总当前分页数据, + * 同时复用列表的年度、分工会和权限过滤条件。

+ */ +@IocBean(args = {"refer:dao"}) +public class OutlayManageUnionStatisticsServiceImpl extends BaseServiceImpl implements OutlayManageUnionStatisticsService { + + public OutlayManageUnionStatisticsServiceImpl(Dao dao) { + super(dao); + } + + @Override + public NutMap queryUseDetailSummary(Integer year, String unionId) { + Cnd cnd = buildUnionManageCondition(year, unionId, false); + Sql sql = Sqls.create(""" + SELECT + COALESCE(SUM(totalQuota), 0) AS totalQuota, + COALESCE(SUM(usedQuota), 0) AS usedQuota, + COALESCE(SUM(COALESCE(totalQuota, 0) - COALESCE(usedQuota, 0)), 0) AS surplusQuota + FROM outlay_manage_union mu $condition + """); + sql.setCondition(cnd); + return executeMap(sql); + } + + @Override + public NutMap queryIncomeExpenseSummary(Integer year, String unionId) { + Cnd cnd = buildUnionManageCondition(year, unionId, true); + Sql sql = Sqls.create(""" + SELECT + COALESCE(SUM(t.beginMoney), 0) AS beginMoney, + COALESCE(SUM(t.feeJanApr), 0) AS feeJanApr, + COALESCE(SUM(t.feeMayAug), 0) AS feeMayAug, + COALESCE(SUM(t.feeSepDec), 0) AS feeSepDec, + COALESCE(SUM(t.awardMoney), 0) AS awardMoney, + COALESCE(SUM(t.usedMoney), 0) AS usedMoney, + COALESCE(SUM(t.remainMoney), 0) AS remainMoney + FROM ( + SELECT + mu.unionId, + COALESCE(SUM(CASE WHEN au.quarterly = 1 THEN au.allocateHeadMoney ELSE 0 END), 0) AS beginMoney, + COALESCE(SUM(CASE WHEN au.quarterly = 1 THEN au.allocateMoney ELSE 0 END), 0) AS feeJanApr, + COALESCE(SUM(CASE WHEN au.quarterly = 2 THEN au.allocateMoney ELSE 0 END), 0) AS feeMayAug, + COALESCE(SUM(CASE WHEN au.quarterly = 3 THEN au.allocateMoney ELSE 0 END), 0) AS feeSepDec, + COALESCE(SUM(CASE WHEN au.quarterly = 4 THEN au.allocateMoney ELSE 0 END), 0) AS awardMoney, + COALESCE(mu.usedQuota, 0) AS usedMoney, + COALESCE(mu.totalQuota, 0) - COALESCE(mu.usedQuota, 0) AS remainMoney + FROM outlay_manage_union mu + LEFT JOIN outlay_allocate_union au ON au.year = mu.year + AND au.unionId = mu.unionId + AND au.delFlag = 0 + LEFT JOIN sys_union su ON su.id = mu.unionId + $condition + GROUP BY mu.year, mu.unionId, mu.usedQuota, mu.totalQuota + ) t + """); + sql.setCondition(cnd); + return executeMap(sql); + } + + /** + * 构造两个列表共用的预算主表过滤条件,保证汇总和分页列表权限口径一致。 + */ + private Cnd buildUnionManageCondition(Integer year, String unionId, boolean includeDelFlag) { + Cnd cnd = Cnd.NEW(); + if (includeDelFlag) { + cnd.and("mu.delFlag", "=", false); + } + cnd.and("mu.year", "=", year == null ? DateUtil.thisYear() : year); + cnd.and("mu.totalQuota", "IS NOT", null); + cnd.andEX("mu.unionId", "=", unionId); + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + cnd.and("mu.unionId", "=", SecurityUtil.getUnionId()); + } + return cnd; + } + + /** + * 执行单行统计查询,并在无数据时返回各字段为零的结果,方便前端直接展示。 + */ + private NutMap executeMap(Sql sql) { + sql.setCallback(Sqls.callback.map()); + dao().execute(sql); + NutMap result = (NutMap) sql.getResult(); + return result == null ? NutMap.NEW() : result; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegation/constant/TeacherCongressDelegationCompositionMode.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegation/constant/TeacherCongressDelegationCompositionMode.java new file mode 100644 index 00000000..ef0f6995 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/delegation/constant/TeacherCongressDelegationCompositionMode.java @@ -0,0 +1,41 @@ +package com.budwk.app.zhgh.democratic.teachercongress.delegation.constant; + +/** + * 代表团组成模式。 + * + *

系统参数 {@code dbtzc} 为 {@code unit} 时按单位组成, + * 为 {@code union} 时按分工会组成;空值或非法值统一兼容为单位模式。

+ */ +public enum TeacherCongressDelegationCompositionMode { + UNIT("unit", "单位"), + UNION("union", "分工会"); + + private final String code; + private final String label; + + TeacherCongressDelegationCompositionMode(String code, String label) { + this.code = code; + this.label = label; + } + + public String getCode() { + return code; + } + + public String getLabel() { + return label; + } + + /** + * 将系统参数转换为组成模式,非法配置按历史默认行为使用单位模式。 + * + * @param value 系统参数值 + * @return 有效组成模式 + */ + public static TeacherCongressDelegationCompositionMode from(String value) { + if (UNION.code.equalsIgnoreCase(value)) { + return UNION; + } + return UNIT; + } +} diff --git a/src/main/resources/views/platform/zhgh/club/infoManage/fixedListLayout.css b/src/main/resources/views/platform/zhgh/club/infoManage/fixedListLayout.css new file mode 100644 index 00000000..93457bdc --- /dev/null +++ b/src/main/resources/views/platform/zhgh/club/infoManage/fixedListLayout.css @@ -0,0 +1,33 @@ +/* 社团信息维护列表页固定占满平台内容区,禁止页面主体产生纵向滚动条。 */ +#app .club-info-fixed-list-page { + box-sizing: border-box; + display: flex; + flex-direction: column; + height: calc(100vh - 82px); + min-height: 0; + overflow: hidden; +} + +#app .club-info-fixed-query-card { + flex: 0 0 auto; +} + +#app .club-info-fixed-list-card { + flex: 1 1 auto; + min-height: 0; + overflow: hidden; +} + +#app .club-info-fixed-list-card > .el-card__body { + box-sizing: border-box; + display: flex; + flex-direction: column; + height: 100%; + min-height: 0; + overflow: hidden; +} + +#app .club-info-fixed-list-card .ele-table-tool, +#app .club-info-fixed-list-card .el-pagination-container { + flex: 0 0 auto; +} diff --git a/src/main/resources/views/platform/zhgh/club/infoManage/fixedListLayout.js b/src/main/resources/views/platform/zhgh/club/infoManage/fixedListLayout.js new file mode 100644 index 00000000..e50173d4 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/club/infoManage/fixedListLayout.js @@ -0,0 +1,71 @@ +/* + * 社团信息维护列表页共用高度计算逻辑。 + * 查询卡片保持原始高度,列表表格占用工具栏与分页之外的全部剩余空间。 + */ +const CLUB_INFO_FIXED_LIST_MIXIN = { + data() { + return { + tableHeight: 300, + clubInfoListResizeObserver: null, + clubInfoListResizeHandler: null, + } + }, + methods: { + updateClubInfoListTableHeight() { + this.$nextTick(() => { + const page = this.$el.querySelector('.club-info-fixed-list-page') + const cardBody = this.$el.querySelector('.club-info-fixed-list-card > .el-card__body') + if (!page || !cardBody || cardBody.clientHeight <= 0) { + return + } + const getOuterHeight = element => { + if (!element) { + return 0 + } + const style = window.getComputedStyle(element) + return element.offsetHeight + + (parseFloat(style.marginTop) || 0) + + (parseFloat(style.marginBottom) || 0) + } + const bodyStyle = window.getComputedStyle(cardBody) + const bodyPadding = (parseFloat(bodyStyle.paddingTop) || 0) + + (parseFloat(bodyStyle.paddingBottom) || 0) + const tableTool = cardBody.querySelector('.ele-table-tool') + const pagination = cardBody.querySelector('.el-pagination-container') + const reservedHeight = bodyPadding + getOuterHeight(tableTool) + getOuterHeight(pagination) + const nextHeight = Math.max(1, Math.floor(cardBody.clientHeight - reservedHeight)) + if (this.tableHeight !== nextHeight) { + this.tableHeight = nextHeight + } + this.$nextTick(() => { + if (this.$refs.tableRef) { + this.$refs.tableRef.doLayout() + } + }) + }) + }, + }, + mounted() { + this.updateClubInfoListTableHeight() + const page = this.$el.querySelector('.club-info-fixed-list-page') + if (window.ResizeObserver && page) { + this.clubInfoListResizeObserver = new ResizeObserver(() => { + this.updateClubInfoListTableHeight() + }) + this.clubInfoListResizeObserver.observe(page) + } else { + this.clubInfoListResizeHandler = () => { + this.updateClubInfoListTableHeight() + } + window.addEventListener('resize', this.clubInfoListResizeHandler) + } + }, + beforeDestroy() { + if (this.clubInfoListResizeObserver) { + this.clubInfoListResizeObserver.disconnect() + } + if (this.clubInfoListResizeHandler) { + window.removeEventListener('resize', this.clubInfoListResizeHandler) + } + }, +} diff --git a/src/main/resources/views/platform/zhgh/dayofficework/outlay/outlayManage/union/fixedListLayout.css b/src/main/resources/views/platform/zhgh/dayofficework/outlay/outlayManage/union/fixedListLayout.css new file mode 100644 index 00000000..88ac5843 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/dayofficework/outlay/outlayManage/union/fixedListLayout.css @@ -0,0 +1,65 @@ +/* + * 分工会预算列表页统一布局:页面固定占满平台内容区, + * 查询区和工具栏保持原高度,超出的数据仅在表格内部滚动。 + */ +#app .outlay-union-fixed-list-page { + box-sizing: border-box; + display: flex; + flex-direction: column; + height: calc(100vh - 82px); + min-height: 0; + overflow: hidden; +} + +#app .outlay-union-fixed-query-card { + flex: 0 0 auto; +} + +#app .outlay-union-fixed-list-card { + flex: 1 1 auto; + min-height: 0; + overflow: hidden; +} + +#app .outlay-union-fixed-list-card > .el-card__body { + box-sizing: border-box; + display: flex; + flex-direction: column; + height: 100%; + min-height: 0; + overflow: hidden; +} + +#app .outlay-union-fixed-list-card .ele-table-tool, +#app .outlay-union-fixed-list-card .el-pagination-container, +#app .outlay-union-fixed-list-card .outlay-union-summary-table { + flex: 0 0 auto; +} + +#app .outlay-union-fixed-list-card .outlay-union-summary-table { + margin-top: 0; +} + +#app .outlay-union-fixed-list-card .outlay-union-summary-table .el-table__body-wrapper { + overflow: hidden !important; +} + +/* 合计行单独展示时不显示单元格网格线,避免与上方数据表格的边框重复。 */ +#app .outlay-union-fixed-list-card .outlay-union-summary-table, +#app .outlay-union-fixed-list-card .outlay-union-summary-table::before, +#app .outlay-union-fixed-list-card .outlay-union-summary-table::after, +#app .outlay-union-fixed-list-card .outlay-union-summary-table .el-table__body, +#app .outlay-union-fixed-list-card .outlay-union-summary-table .el-table__body tr, +#app .outlay-union-fixed-list-card .outlay-union-summary-table .el-table__body td { + border: none !important; +} + +/* 合计标签单元格保留完整边框,突出汇总行的起始位置。 */ +#app .outlay-union-fixed-list-card .outlay-union-summary-table .el-table__body td:first-child { + border: 1px solid #ebeef5 !important; +} + +#app .outlay-union-fixed-list-card .outlay-union-fixed-table { + flex: 1 1 auto; + min-height: 0; +} diff --git a/src/main/resources/views/platform/zhgh/dayofficework/outlay/outlayManage/union/fixedListLayout.js b/src/main/resources/views/platform/zhgh/dayofficework/outlay/outlayManage/union/fixedListLayout.js new file mode 100644 index 00000000..97d7ff44 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/dayofficework/outlay/outlayManage/union/fixedListLayout.js @@ -0,0 +1,75 @@ +/* + * 分工会预算列表页统一高度逻辑:表格高度按列表卡片剩余空间动态计算, + * 窗口尺寸或平台内容区变化时重新布局,保证分页始终可见。 + */ +const OUTLAY_UNION_FIXED_LIST_MIXIN = { + data() { + return { + tableHeight: 300, + outlayUnionResizeObserver: null, + outlayUnionResizeHandler: null, + } + }, + methods: { + updateOutlayUnionTableHeight() { + this.$nextTick(() => { + const page = this.$el.querySelector('.outlay-union-fixed-list-page') + const cardBody = this.$el.querySelector('.outlay-union-fixed-list-card > .el-card__body') + if (!page || !cardBody || cardBody.clientHeight <= 0) { + return + } + const getOuterHeight = (element) => { + if (!element) { + return 0 + } + const style = window.getComputedStyle(element) + return element.offsetHeight + + (parseFloat(style.marginTop) || 0) + + (parseFloat(style.marginBottom) || 0) + } + const bodyStyle = window.getComputedStyle(cardBody) + const bodyPadding = (parseFloat(bodyStyle.paddingTop) || 0) + + (parseFloat(bodyStyle.paddingBottom) || 0) + const tableTool = cardBody.querySelector('.ele-table-tool') + const summaryTable = cardBody.querySelector('.outlay-union-summary-table') + const pagination = cardBody.querySelector('.el-pagination-container') + const reservedHeight = bodyPadding + + getOuterHeight(tableTool) + + getOuterHeight(summaryTable) + + getOuterHeight(pagination) + const nextHeight = Math.max(1, Math.floor(cardBody.clientHeight - reservedHeight)) + if (this.tableHeight !== nextHeight) { + this.tableHeight = nextHeight + } + this.$nextTick(() => { + if (this.$refs.tableRef) { + this.$refs.tableRef.doLayout() + } + }) + }) + }, + }, + mounted() { + this.updateOutlayUnionTableHeight() + const page = this.$el.querySelector('.outlay-union-fixed-list-page') + if (window.ResizeObserver && page) { + this.outlayUnionResizeObserver = new ResizeObserver(() => { + this.updateOutlayUnionTableHeight() + }) + this.outlayUnionResizeObserver.observe(page) + } else { + this.outlayUnionResizeHandler = () => { + this.updateOutlayUnionTableHeight() + } + window.addEventListener('resize', this.outlayUnionResizeHandler) + } + }, + beforeDestroy() { + if (this.outlayUnionResizeObserver) { + this.outlayUnionResizeObserver.disconnect() + } + if (this.outlayUnionResizeHandler) { + window.removeEventListener('resize', this.outlayUnionResizeHandler) + } + }, +}