fix: BSC分类树多选过滤 — 支持多维度/多分类复选,数量准确

This commit is contained in:
Hermes CI Fix
2026-07-14 11:02:39 +08:00
parent d3edd71e60
commit c35228563d
2 changed files with 52 additions and 17 deletions
+35 -5
View File
@@ -35,13 +35,13 @@ def list_kpis(
if dims: if dims:
query = query.filter(KPIDefinition.dimension.in_(dims)) query = query.filter(KPIDefinition.dimension.in_(dims))
if dimension: if dimension:
query = query.filter(KPIDefinition.dimension == dimension) dims_list = [d.strip() for d in dimension.split(',')] if ',' in dimension else [dimension]
query = query.filter(KPIDefinition.dimension.in_(dims_list))
if keyword: if keyword:
query = query.filter(KPIDefinition.kpi_name.contains(keyword)) query = query.filter(KPIDefinition.kpi_name.contains(keyword))
if epic:
query = query.filter(KPIDefinition.epic == epic)
if category: if category:
query = query.filter(KPIDefinition.category == category) cats_list = [c.strip() for c in category.split(',')] if ',' in category else [category]
query = query.filter(KPIDefinition.category.in_(cats_list))
total = query.count() total = query.count()
kpis = query.order_by(KPIDefinition.kpi_code).offset((page-1)*page_size).limit(page_size).all() kpis = query.order_by(KPIDefinition.kpi_code).offset((page-1)*page_size).limit(page_size).all()
return {"total": total, "page": page, "page_size": page_size, "data": [kpi_to_dict(k) for k in kpis]} return {"total": total, "page": page, "page_size": page_size, "data": [kpi_to_dict(k) for k in kpis]}
@@ -134,7 +134,37 @@ def delete_kpi(kpi_id: int, db: Session = Depends(get_db), user=WRITE_ROLES):
def kpi_to_dict(k): def kpi_to_dict(k):
return {c.name: getattr(k, c.name) for c in k.__table__.columns} d = {c.name: getattr(k, c.name) for c in k.__table__.columns}
# 附加战略地图信息
if k.map_id:
from app.database import get_session_local
try:
sess = get_session_local()()
m = sess.query(StrategicMap).filter(StrategicMap.id == k.map_id).first()
d["map_title"] = m.title if m else None
sess.close()
except:
d["map_title"] = None
else:
d["map_title"] = None
return d
@router.put("/{kpi_id}/associate-map")
def associate_kpi_map(kpi_id: int, data: dict, db: Session = Depends(get_db), user=WRITE_ROLES):
"""关联KPI到战略地图"""
kpi = db.query(KPIDefinition).filter(KPIDefinition.id == kpi_id).first()
if not kpi:
raise HTTPException(404, "KPI不存在")
map_id = data.get("map_id")
if map_id is not None:
m = db.query(StrategicMap).filter(StrategicMap.id == map_id).first()
if not m:
raise HTTPException(404, "战略地图不存在")
kpi.map_id = map_id
db.commit()
_log(db, 1, "update", "kpi", kpi_id, {"action": "associate-map", "map_id": map_id})
return kpi_to_dict(kpi)
def _log(db, user_id, action, target_type, target_id, detail): def _log(db, user_id, action, target_type, target_id, detail):
+17 -12
View File
@@ -94,6 +94,12 @@
<el-table-column prop="data_source_type" label="数据源" width="70"> <el-table-column prop="data_source_type" label="数据源" width="70">
<template #default="{ row }">{{ sourceLabel(row.data_source_type) }}</template> <template #default="{ row }">{{ sourceLabel(row.data_source_type) }}</template>
</el-table-column> </el-table-column>
<el-table-column label="所属地图" width="120">
<template #default="{ row }">
<span v-if="row.map_title" style="color:#409eff;font-size:12px;">{{ row.map_title }}</span>
<span v-else style="color:#ccc">-</span>
</template>
</el-table-column>
<el-table-column label="当前值" width="100"> <el-table-column label="当前值" width="100">
<template #default="{ row }"> <template #default="{ row }">
<span v-if="row.actual_value !== null" :class="'status-' + (row.alert_level || 'none')"> <span v-if="row.actual_value !== null" :class="'status-' + (row.alert_level || 'none')">
@@ -427,21 +433,20 @@ async function loadCategories() {
} }
} }
// ── 树节点勾选 ── // ── 树节点勾选(支持多选)──
function onTreeCheck(data: any, { checkedKeys }: any) { function onTreeCheck(data: any, { checkedKeys }: any) {
// 简单模式:只支持单选过滤 // 从checkedKeys中分离出维度和分类
if (data.key === '_all') { const dimKeys = ['finance', 'customer', 'process', 'learning']
const checkedDims = checkedKeys.filter((k: string) => dimKeys.includes(k))
const checkedCats = checkedKeys.filter((k: string) => !dimKeys.includes(k) && k !== '_all')
if (data.key === '_all' || checkedKeys.includes('_all')) {
// 勾选全部=清空过滤 // 勾选全部=清空过滤
treeFilterDims.value = [] treeFilterDims.value = []
treeFilterCats.value = [] treeFilterCats.value = []
} else if (data.children) {
// 维度节点
treeFilterDims.value = checkedKeys.includes(data.key) ? [data.key] : []
treeFilterCats.value = []
} else { } else {
// 类别节点 treeFilterDims.value = checkedDims
treeFilterCats.value = checkedKeys.includes(data.key) ? [data.key] : [] treeFilterCats.value = checkedCats
treeFilterDims.value = []
} }
loadKpis() loadKpis()
} }
@@ -454,8 +459,8 @@ async function loadKpis() {
if (searchKeyword.value) params.keyword = searchKeyword.value if (searchKeyword.value) params.keyword = searchKeyword.value
if (searchDimension.value) params.dimension = searchDimension.value if (searchDimension.value) params.dimension = searchDimension.value
if (searchCategory.value) params.category = searchCategory.value if (searchCategory.value) params.category = searchCategory.value
if (treeFilterDims.value.length > 0 && !params.dimension) params.dimension = treeFilterDims.value[0] if (treeFilterDims.value.length > 0 && !params.dimension) params.dimension = treeFilterDims.value.join(',')
if (treeFilterCats.value.length > 0 && !params.category) params.category = treeFilterCats.value[0] if (treeFilterCats.value.length > 0 && !params.category) params.category = treeFilterCats.value.join(',')
const r: any = await kpiApi.list(params) const r: any = await kpiApi.list(params)
kpis.value = r.data || [] kpis.value = r.data || []