feat: 实现部门积分层级继承逻辑 - 一级汇总/二级管理/三级共享

This commit is contained in:
Your Name 2026-01-07 11:01:02 +08:00
parent 1c4e042c4b
commit a6997c55f4

View File

@ -568,21 +568,54 @@
<template v-else-if="column.key === 'score'">
<div class="score-display">
<div class="score-main">
<span class="score-value">{{ record.scoreBalance ?? 0 }}</span>
<span class="score-unit">分</span>
</div>
<div class="score-details">
<span class="score-detail-item">
<span class="detail-label">充值</span>
<span class="detail-value">{{ record.totalRecharge ?? 0 }}</span>
</span>
<span class="detail-separator">|</span>
<span class="score-detail-item">
<span class="detail-label">消耗</span>
<span class="detail-value">{{ record.totalConsume ?? 0 }}</span>
</span>
</div>
<!-- 一级部门有子部门:显示汇总 -->
<template v-if="getDepartmentLevel(record) === 1 && hasChildDepartments(record)">
<div class="score-main">
<span class="score-value">{{ getLevel1DeptTotalScore(record) }}</span>
<span class="score-unit">分</span>
<a-tag size="small" color="blue" style="margin-left: 4px;">汇总</a-tag>
</div>
<div class="score-details">
<span class="score-detail-item">
<span class="detail-label">充值</span>
<span class="detail-value">{{ getLevel1DeptTotalRecharge(record) }}</span>
</span>
<span class="detail-separator">|</span>
<span class="score-detail-item">
<span class="detail-label">消耗</span>
<span class="detail-value">{{ getLevel1DeptTotalConsume(record) }}</span>
</span>
</div>
</template>
<!-- 三级部门:显示共享自父部门 -->
<template v-else-if="getDepartmentLevel(record) === 3">
<div class="score-main">
<span class="score-value">{{ getParentDeptScore(record) }}</span>
<span class="score-unit">分</span>
<a-tag size="small" color="purple" style="margin-left: 4px;">共享</a-tag>
</div>
<div class="score-details" style="font-size: 11px; color: #888;">
共享自「{{ getParentDeptNameById(record.parentId) }}」
</div>
</template>
<!-- 二级部门或无子部门的一级部门:正常显示 -->
<template v-else>
<div class="score-main">
<span class="score-value">{{ record.scoreBalance ?? 0 }}</span>
<span class="score-unit">分</span>
</div>
<div class="score-details">
<span class="score-detail-item">
<span class="detail-label">充值</span>
<span class="detail-value">{{ record.totalRecharge ?? 0 }}</span>
</span>
<span class="detail-separator">|</span>
<span class="score-detail-item">
<span class="detail-label">消耗</span>
<span class="detail-value">{{ record.totalConsume ?? 0 }}</span>
</span>
</div>
</template>
</div>
</template>
@ -1793,74 +1826,121 @@
<!-- 积分池管理弹窗 -->
<a-modal
v-model:open="modals.scorePool"
title="积分池管理"
:title="scorePoolModalTitle"
width="700px"
ok-text="OK"
cancel-text="Cancel"
ok-text="关闭"
:cancel-button-props="{ style: { display: 'none' } }"
@ok="modals.scorePool = false"
@cancel="modals.scorePool = false"
>
<div v-if="currentScorePool">
<!-- 提示信息:根据层级显示不同说明 -->
<a-alert
v-if="currentDeptScoreLevel === 1 && hasChildDepartments(currentDept)"
type="info"
show-icon
style="margin-bottom: 16px;"
>
<template #message>
<span>一级部门积分为下属二级部门积分的汇总,不支持直接充值。请前往具体二级部门进行充值。</span>
</template>
</a-alert>
<a-alert
v-else-if="currentDeptScoreLevel === 3"
type="info"
show-icon
style="margin-bottom: 16px;"
>
<template #message>
<span>三级部门共享上级二级部门「{{ getParentDeptName(currentDept) }}」的积分,不支持单独设置。</span>
</template>
</a-alert>
<a-descriptions :column="2" bordered>
<a-descriptions-item label="部门名称">
{{ currentScorePool.deptName }}
<a-tag v-if="currentDeptScoreLevel === 1 && hasChildDepartments(currentDept)" color="blue" style="margin-left: 8px;">汇总</a-tag>
<a-tag v-else-if="currentDeptScoreLevel === 3" color="purple" style="margin-left: 8px;">共享</a-tag>
<a-tag v-else color="green" style="margin-left: 8px;">独立管理</a-tag>
</a-descriptions-item>
<a-descriptions-item label="当前余额">
<span class="balance-display">
{{ currentScorePool.balance }} 分
{{ getDisplayScoreBalance() }} 分
</span>
</a-descriptions-item>
<a-descriptions-item label="累计充值">
{{ currentScorePool.totalRecharge || 0 }} 分
{{ getDisplayTotalRecharge() }} 分
</a-descriptions-item>
<a-descriptions-item label="累计消耗">
{{ currentScorePool.totalConsume || 0 }} 分
{{ getDisplayTotalConsume() }} 分
</a-descriptions-item>
<a-descriptions-item label="累计获得" v-if="currentScorePool.totalEarn">
{{ currentScorePool.totalEarn }} 分
</a-descriptions-item>
</a-descriptions>
<a-divider>消耗限制设置</a-divider>
<a-descriptions :column="2" bordered>
<a-descriptions-item label="限制状态">
<a-tag :color="currentScorePool.limitEnabled ? 'green' : 'default'">
{{ currentScorePool.limitEnabled ? '已启用' : '未启用' }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="限制周期" v-if="currentScorePool.limitEnabled">
{{ currentScorePool.limitPeriod === 'DAILY' ? '每日' :
currentScorePool.limitPeriod === 'WEEKLY' ? '每周' :
currentScorePool.limitPeriod === 'MONTHLY' ? '每月' : '未知' }}
</a-descriptions-item>
<a-descriptions-item label="周期限额" v-if="currentScorePool.limitEnabled">
{{ currentScorePool.periodLimit || 0 }} 分
</a-descriptions-item>
<a-descriptions-item label="已使用" v-if="currentScorePool.limitEnabled">
{{ currentScorePool.periodUsed || 0 }} 分
<span v-if="currentScorePool.periodLimit">
({{ Math.round((currentScorePool.periodUsed || 0) / currentScorePool.periodLimit * 100) }}%)
</span>
</a-descriptions-item>
</a-descriptions>
<!-- 一级部门:显示子部门积分明细 -->
<template v-if="currentDeptScoreLevel === 1 && hasChildDepartments(currentDept)">
<a-divider>下属二级部门积分明细</a-divider>
<a-table
:columns="childDeptScoreColumns"
:data-source="getChildDepartmentScores()"
:pagination="false"
size="small"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'actions'">
<a-button type="link" size="small" @click="showScorePoolModal(record)">
管理积分
</a-button>
</template>
</template>
</a-table>
</template>
<a-divider>充值积分</a-divider>
<a-form layout="inline">
<a-form-item label="充值金额">
<a-input-number
v-model:value="rechargeAmount"
:min="1"
:precision="0"
style="width: 200px"
placeholder="请输入充值金额"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleRecharge">确认充值</a-button>
</a-form-item>
</a-form>
<!-- 消耗限制设置:只对可管理积分的部门显示 -->
<template v-if="canManageScore">
<a-divider>消耗限制设置</a-divider>
<a-descriptions :column="2" bordered>
<a-descriptions-item label="限制状态">
<a-tag :color="currentScorePool.limitEnabled ? 'green' : 'default'">
{{ currentScorePool.limitEnabled ? '已启用' : '未启用' }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="限制周期" v-if="currentScorePool.limitEnabled">
{{ currentScorePool.limitPeriod === 'DAILY' ? '每日' :
currentScorePool.limitPeriod === 'WEEKLY' ? '每周' :
currentScorePool.limitPeriod === 'MONTHLY' ? '每月' : '未知' }}
</a-descriptions-item>
<a-descriptions-item label="周期限额" v-if="currentScorePool.limitEnabled">
{{ currentScorePool.periodLimit || 0 }} 分
</a-descriptions-item>
<a-descriptions-item label="已使用" v-if="currentScorePool.limitEnabled">
{{ currentScorePool.periodUsed || 0 }} 分
<span v-if="currentScorePool.periodLimit">
({{ Math.round((currentScorePool.periodUsed || 0) / currentScorePool.periodLimit * 100) }}%)
</span>
</a-descriptions-item>
</a-descriptions>
<a-divider>充值积分</a-divider>
<a-form layout="inline">
<a-form-item label="充值金额">
<a-input-number
v-model:value="rechargeAmount"
:min="1"
:precision="0"
style="width: 200px"
placeholder="请输入充值金额"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleRecharge">确认充值</a-button>
</a-form-item>
</a-form>
</template>
</div>
<a-empty v-else description="加载中..." />
</a-modal>
@ -2497,6 +2577,180 @@ const getDepartmentLevel = (record: DepartmentOverview): number => {
return 3
}
// 当前部门的积分管理层级
const currentDeptScoreLevel = computed(() => {
if (!currentDept.value) return 0
return getDepartmentLevel(currentDept.value)
})
// 积分池弹窗标题
const scorePoolModalTitle = computed(() => {
if (!currentDept.value) return '积分池管理'
const level = currentDeptScoreLevel.value
if (level === 1 && hasChildDepartments(currentDept.value)) {
return '积分池管理 - 汇总视图'
} else if (level === 3) {
return '积分池管理 - 共享视图'
}
return '积分池管理'
})
// 判断部门是否有子部门
const hasChildDepartments = (dept: DepartmentOverview | null): boolean => {
if (!dept?.id) return false
return departments.value.some(d => d.parentId === dept.id)
}
// 获取父部门名称
const getParentDeptName = (dept: DepartmentOverview | null): string => {
if (!dept?.parentId) return ''
const parent = departments.value.find(d => d.id === dept.parentId)
return parent?.deptName || ''
}
// 获取父部门(二级部门)的积分信息
const getParentDeptScoreInfo = () => {
if (!currentDept.value?.parentId) return null
const parent = departments.value.find(d => d.id === currentDept.value?.parentId)
return parent || null
}
// 是否可以管理积分(充值等操作)
const canManageScore = computed(() => {
if (!currentDept.value) return false
const level = currentDeptScoreLevel.value
// 三级部门不能管理积分
if (level === 3) return false
// 一级部门:如果有子部门,则不能直接管理(显示汇总)
if (level === 1 && hasChildDepartments(currentDept.value)) return false
// 二级部门可以管理
// 没有子部门的一级部门也可以管理
return true
})
// 获取显示的积分余额(考虑层级继承)
const getDisplayScoreBalance = (): number => {
if (!currentDept.value) return 0
const level = currentDeptScoreLevel.value
// 三级部门:显示父部门(二级)的积分
if (level === 3) {
const parent = getParentDeptScoreInfo()
return parent?.scoreBalance || 0
}
// 一级部门且有子部门:计算所有二级部门的积分汇总
if (level === 1 && hasChildDepartments(currentDept.value)) {
const childDepts = departments.value.filter(d => d.parentId === currentDept.value?.id)
return childDepts.reduce((sum, d) => sum + (d.scoreBalance || 0), 0)
}
// 其他情况:显示本部门积分
return currentScorePool.value?.balance || 0
}
// 获取显示的累计充值(考虑层级继承)
const getDisplayTotalRecharge = (): number => {
if (!currentDept.value) return 0
const level = currentDeptScoreLevel.value
// 三级部门:显示父部门的数据
if (level === 3) {
const parent = getParentDeptScoreInfo()
return parent?.totalRecharge || 0
}
// 一级部门且有子部门:计算汇总
if (level === 1 && hasChildDepartments(currentDept.value)) {
const childDepts = departments.value.filter(d => d.parentId === currentDept.value?.id)
return childDepts.reduce((sum, d) => sum + (d.totalRecharge || 0), 0)
}
return currentScorePool.value?.totalRecharge || 0
}
// 获取显示的累计消耗(考虑层级继承)
const getDisplayTotalConsume = (): number => {
if (!currentDept.value) return 0
const level = currentDeptScoreLevel.value
// 三级部门:显示父部门的数据
if (level === 3) {
const parent = getParentDeptScoreInfo()
return parent?.totalConsume || 0
}
// 一级部门且有子部门:计算汇总
if (level === 1 && hasChildDepartments(currentDept.value)) {
const childDepts = departments.value.filter(d => d.parentId === currentDept.value?.id)
return childDepts.reduce((sum, d) => sum + (d.totalConsume || 0), 0)
}
return currentScorePool.value?.totalConsume || 0
}
// 获取子部门积分列表(用于一级部门汇总视图)
const getChildDepartmentScores = () => {
if (!currentDept.value?.id) return []
return departments.value
.filter(d => d.parentId === currentDept.value?.id)
.map(d => ({
...d,
id: d.id,
deptName: d.deptName,
scoreBalance: d.scoreBalance || 0,
totalRecharge: d.totalRecharge || 0,
totalConsume: d.totalConsume || 0
}))
}
// 子部门积分明细表格列
const childDeptScoreColumns = [
{ title: '二级部门', dataIndex: 'deptName', key: 'deptName' },
{ title: '当前余额', dataIndex: 'scoreBalance', key: 'scoreBalance' },
{ title: '累计充值', dataIndex: 'totalRecharge', key: 'totalRecharge' },
{ title: '累计消耗', dataIndex: 'totalConsume', key: 'totalConsume' },
{ title: '操作', key: 'actions', width: 100 }
]
// 获取一级部门的汇总积分(所有二级部门积分之和)
const getLevel1DeptTotalScore = (dept: DepartmentOverview): number => {
if (!dept.id) return 0
const childDepts = departments.value.filter(d => d.parentId === dept.id)
return childDepts.reduce((sum, d) => sum + (d.scoreBalance || 0), 0)
}
// 获取一级部门的汇总充值
const getLevel1DeptTotalRecharge = (dept: DepartmentOverview): number => {
if (!dept.id) return 0
const childDepts = departments.value.filter(d => d.parentId === dept.id)
return childDepts.reduce((sum, d) => sum + (d.totalRecharge || 0), 0)
}
// 获取一级部门的汇总消耗
const getLevel1DeptTotalConsume = (dept: DepartmentOverview): number => {
if (!dept.id) return 0
const childDepts = departments.value.filter(d => d.parentId === dept.id)
return childDepts.reduce((sum, d) => sum + (d.totalConsume || 0), 0)
}
// 获取父部门(二级部门)的积分(用于三级部门显示)
const getParentDeptScore = (dept: DepartmentOverview): number => {
if (!dept.parentId) return 0
const parent = departments.value.find(d => d.id === dept.parentId)
return parent?.scoreBalance || 0
}
// 根据ID获取父部门名称
const getParentDeptNameById = (parentId: number | null | undefined): string => {
if (!parentId) return ''
const parent = departments.value.find(d => d.id === parentId)
return parent?.deptName || ''
}
// 部门树形选择器数据 - 用于调整用户部门
const departmentTreeSelectData = computed(() => {
// 构建树形结构