This commit is contained in:
zlwu committed 2025-11-14 00:11:31 +08:00
1 parent 4d48300a1d
commit 4c8434595a
3 files changed
+292 -9

No files matched your search

@@ -205,7 +205,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND sm.major_id IS NOT NULL AND sm.major_id IS NOT NULL
AND sm.class_id IS NOT NULL AND sm.class_id IS NOT NULL
GROUP BY c.grade, sm.college_id, sm.college_name, sm.major_id, sm.major_name, sm.class_id, sm.class_name GROUP BY c.grade, sm.college_id, sm.college_name, sm.major_id, sm.major_name, sm.class_id, sm.class_name
ORDER BY c.grade DESC, sm.college_name, sm.major_name, sm.class_name, sm.create_time DESC ORDER BY c.grade DESC, sm.college_name, sm.major_name, sm.class_name, MAX(sm.create_time) DESC
</select> </select>
<!-- 查询二级学院下的学院、专业、班级学生统计信息(按床位分配统计) --> <!-- 查询二级学院下的学院、专业、班级学生统计信息(按床位分配统计) -->
@@ -255,7 +255,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND sm.major_id IS NOT NULL AND sm.major_id IS NOT NULL
AND sm.class_id IS NOT NULL AND sm.class_id IS NOT NULL
GROUP BY c.grade, sm.college_id, sm.college_name, sm.major_id, sm.major_name, sm.class_id, sm.class_name GROUP BY c.grade, sm.college_id, sm.college_name, sm.major_id, sm.major_name, sm.class_id, sm.class_name
ORDER BY c.grade DESC, sm.college_name, sm.major_name, sm.class_name, sm.create_time DESC ORDER BY c.grade DESC, sm.college_name, sm.major_name, sm.class_name, MAX(sm.create_time) DESC
</select> </select>
<!-- 查询二级学院下的学院床位分配统计信息(基于学生表统计需求,基于床位分配表统计已分配) --> <!-- 查询二级学院下的学院床位分配统计信息(基于学生表统计需求,基于床位分配表统计已分配) -->
@@ -301,7 +301,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND (d.parent_id = #{secondaryCollegeId} OR d.dept_id = #{secondaryCollegeId} OR FIND_IN_SET(#{secondaryCollegeId}, d.ancestors)) AND (d.parent_id = #{secondaryCollegeId} OR d.dept_id = #{secondaryCollegeId} OR FIND_IN_SET(#{secondaryCollegeId}, d.ancestors))
AND sm.college_id IS NOT NULL AND sm.college_id IS NOT NULL
GROUP BY sm.college_id, sm.college_name GROUP BY sm.college_id, sm.college_name
ORDER BY sm.college_name, sm.create_time DESC ORDER BY sm.college_name, MAX(sm.create_time) DESC
</select> </select>
</mapper> </mapper>
@@ -536,7 +536,7 @@
</template> </template>
<script setup name="JcbtPovertyApplication"> <script setup name="JcbtPovertyApplication">
import { getCurrentInstance, ref, reactive, onMounted, nextTick, computed } from 'vue'; import { getCurrentInstance, ref, reactive, onMounted, nextTick, computed, toRefs } from 'vue';
import { getToken } from '@/utils/auth'; import { getToken } from '@/utils/auth';
import { import {
listPovertyApplication, listPovertyApplication,
@@ -563,7 +563,7 @@ import { ElMessageBox, ElMessage } from 'element-plus';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
// 字典:资助类型、家庭经济情况 // 字典:资助类型、家庭经济情况
const { jcbt_assistance_type, jcbt_family_economy_status, jcbt_academic_year, jcbt_status } = toRefs<any>( const { jcbt_assistance_type, jcbt_family_economy_status, jcbt_academic_year, jcbt_status } = toRefs(
proxy?.useDict('jcbt_assistance_type', 'jcbt_family_economy_status', 'jcbt_academic_year', 'jcbt_status') || {} proxy?.useDict('jcbt_assistance_type', 'jcbt_family_economy_status', 'jcbt_academic_year', 'jcbt_status') || {}
); );
@@ -805,7 +805,7 @@ function handleRecognitionTypeChange(value) {
/** 处理申请资助类型选择变化 */ /** 处理申请资助类型选择变化 */
function handleAssistanceTypeChange(value) { function handleAssistanceTypeChange(value) {
const selected = (jcbt_assistance_type?.value || []).find((item: any) => item.value === value); const selected = (jcbt_assistance_type?.value || []).find((item) => item.value === value);
form.value.assistanceTypeNames = selected ? selected.label : null; form.value.assistanceTypeNames = selected ? selected.label : null;
} }
@@ -177,15 +177,38 @@
<!-- 收集范围设置对话框 --> <!-- 收集范围设置对话框 -->
<el-dialog title="设置收集范围" v-model="scopeDialog.visible" width="500px" append-to-body @close="handleScopeDialogClose"> <el-dialog title="设置收集范围" v-model="scopeDialog.visible" width="500px" append-to-body @close="handleScopeDialogClose">
<div class="scope-content"> <div class="scope-content">
<!-- 过滤按钮区域 -->
<div class="filter-buttons">
<el-radio-group v-model="classStatusFilter" size="small" @change="handleFilterChange">
<el-radio-button label="all">全部</el-radio-button>
<el-radio-button label="normal">正常</el-radio-button>
</el-radio-group>
</div>
<el-tree <el-tree
ref="scopeTreeRef" ref="scopeTreeRef"
:data="deptTreeData" :data="filteredTreeData"
show-checkbox show-checkbox
default-expand-all default-expand-all
node-key="id" node-key="id"
:props="{ children: 'children', label: 'label' }" :props="{ children: 'children', label: 'label' }"
:check-strictly="false" :check-strictly="false"
/> >
<template #default="{ data }">
<span class="scope-node">
<span class="scope-label">{{ data.label }}</span>
<dict-tag
v-if="isClassNode(data) && data.classStatus !== undefined && data.classStatus !== null && data.classStatus !== ''"
:options="classStatusOptions"
:value="data.classStatus"
class="scope-tag"
/>
<!-- 临时调试信息(开发时可见,生产环境可删除) -->
<!-- <span v-if="isClassNode(data)" style="font-size: 10px; color: #999; margin-left: 4px;">
[{{ data.deptCategory || '无category' }}|{{ data.classStatus || '无状态' }}]
</span> -->
</span>
</template>
</el-tree>
</div> </div>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@@ -253,13 +276,27 @@
</template> </template>
<script setup name="StuColBatch" lang="ts"> <script setup name="StuColBatch" lang="ts">
import { ref, reactive, computed, toRefs, getCurrentInstance, onMounted, nextTick } from 'vue';
import { listStuColBatch, getStuColBatch, delStuColBatch, addStuColBatch, updateStuColBatch } from '@/api/xg/xsjcxx/XgStuColBatch/stuColBatch'; import { listStuColBatch, getStuColBatch, delStuColBatch, addStuColBatch, updateStuColBatch } from '@/api/xg/xsjcxx/XgStuColBatch/stuColBatch';
import { StuColBatchVO, StuColBatchQuery, StuColBatchForm } from '@/api/xg/xsjcxx/XgStuColBatch/stuColBatch/types'; import { StuColBatchVO, StuColBatchQuery, StuColBatchForm } from '@/api/xg/xsjcxx/XgStuColBatch/stuColBatch/types';
import { setScope, getScope, getScopeDetail } from '@/api/xg/xsjcxx/stuColBatchClass/stuColBatchClass'; import { setScope, getScope, getScopeDetail } from '@/api/xg/xsjcxx/stuColBatchClass/stuColBatchClass';
import { listClassMsg } from '@/api/xg/xsjcxx/classMsg/classMsg';
import type { ClassMsgQuery, ClassMsgVO } from '@/api/xg/xsjcxx/classMsg/types';
import request from '@/utils/request'; import request from '@/utils/request';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { xg_class_status } = toRefs<any>(proxy?.useDict('xg_class_status') || {});
const classStatusOptions = computed(() => {
const options = xg_class_status?.value || [];
// 调试日志:检查字典数据是否加载
if (options.length === 0) {
console.warn('[班级状态] 字典数据未加载,xg_class_status 为空');
} else {
console.log(`[班级状态] 字典数据已加载,共 ${options.length} 个选项`);
}
return options;
});
const stuColBatchList = ref<StuColBatchVO[]>([]); const stuColBatchList = ref<StuColBatchVO[]>([]);
const buttonLoading = ref(false); const buttonLoading = ref(false);
@@ -285,8 +322,14 @@ const scopeDialog = reactive({
const scopeButtonLoading = ref(false); const scopeButtonLoading = ref(false);
const currentBatch = ref<StuColBatchVO | null>(null); const currentBatch = ref<StuColBatchVO | null>(null);
const deptTreeData = ref<any[]>([]); const deptTreeData = ref<any[]>([]);
const originalTreeData = ref<any[]>([]); // 原始树数据(未过滤)
const checkedDeptIds = ref<number[]>([]); const checkedDeptIds = ref<number[]>([]);
const scopeTreeRef = ref(); const scopeTreeRef = ref();
// 班级状态过滤:'all' 全部, 'normal' 正常
const classStatusFilter = ref<'all' | 'normal'>('all');
// 班级状态缓存
const classStatusCache = new Map<string, string>();
let classStatusLoaded = false;
// 查看详细弹窗相关数据 // 查看详细弹窗相关数据
const viewDialog = reactive({ const viewDialog = reactive({
@@ -436,6 +479,8 @@ const handleScope = async (row: StuColBatchVO) => {
currentBatch.value = row; currentBatch.value = row;
// 先清空勾选状态 // 先清空勾选状态
checkedDeptIds.value = []; checkedDeptIds.value = [];
// 重置过滤状态为"全部"
classStatusFilter.value = 'all';
await loadDeptTree(); await loadDeptTree();
await loadCurrentScope(row.id); await loadCurrentScope(row.id);
@@ -448,10 +493,128 @@ const handleScope = async (row: StuColBatchVO) => {
} }
}; };
/** 加载班级状态缓存 */
const ensureClassStatusCache = async () => {
if (classStatusLoaded) {
console.log('[班级状态] 缓存已加载,跳过');
return;
}
try {
const params: ClassMsgQuery = {
pageNum: 1,
pageSize: 9999,
params: {}
};
const res = await listClassMsg(params);
const rows: ClassMsgVO[] = res?.rows || [];
console.log(`[班级状态] 加载班级数据,共 ${rows.length} 条`);
// 建立缓存映射:sys_dept.dept_id = xg_class_msg.id -> class_status
let cacheCount = 0;
rows.forEach((item) => {
const status = item?.classStatus;
if (status === null || status === undefined || status === '') {
return;
}
const statusStr = String(status);
// 关联关系:sys_dept.dept_id = xg_class_msg.id
// 所以使用 xg_class_msg.id 作为 key,对应 sys_dept.dept_id
if (item.id !== undefined && item.id !== null) {
classStatusCache.set(`deptId:${String(item.id)}`, statusStr);
cacheCount++;
}
});
classStatusLoaded = true;
console.log(`[班级状态] 缓存建立完成,共 ${cacheCount} 条有效状态数据`);
} catch (error) {
console.error('加载班级状态失败:', error);
ElMessage.warning('加载班级状态失败,班级状态标签可能无法显示');
}
};
/** 判断是否是班级节点 */
const isClassNode = (data: any) => {
// 方法1:通过 deptCategory 字段判断(优先)
const category = data?.deptCategory ?? data?.dept_category ?? data?.params?.deptCategory;
if (category && typeof category === 'string' && category.toLowerCase().startsWith('bj-')) {
return true;
}
// 方法2:通过名称模式判断(备用方案)
// 如果节点是叶子节点(没有children或children为空),且名称符合"名称+3位数字"格式,则认为是班级节点
const isLeaf = !data?.children || data.children.length === 0;
if (isLeaf && data?.label && typeof data.label === 'string') {
// 匹配类似"西餐运营251"、"中烹252"这种格式(名称+3位数字结尾)
const nameMatch = /^.+?\d{3}$/.test(data.label);
if (nameMatch) {
return true;
}
}
return false;
};
/** 解析节点班级状态 */
const resolveClassStatus = (node: any): string | undefined => {
// 判断是否是班级节点
if (!isClassNode(node)) {
return undefined;
}
// 通过 sys_dept.dept_id 关联 xg_class_msg.id 获取 class_status
// 关联关系:sys_dept.dept_id = xg_class_msg.id
const deptId = node.id ?? node.deptId;
if (deptId === undefined || deptId === null) {
return undefined;
}
const key = `deptId:${String(deptId)}`;
if (classStatusCache.has(key)) {
const status = classStatusCache.get(key);
return status;
}
return undefined;
};
/** 增强部门树节点(添加班级状态) */
const enhanceDeptNodes = (nodes: any[]): any[] => {
return (nodes || []).map((node) => {
const enhanced = { ...node };
const category = enhanced.deptCategory ?? enhanced.dept_category ?? enhanced.params?.deptCategory;
if (category) {
enhanced.deptCategory = category;
}
// 判断是否是班级节点并获取状态
const isClass = isClassNode(enhanced);
const status = resolveClassStatus(enhanced);
if (status) {
enhanced.classStatus = status;
// 调试日志:记录成功添加状态的节点
console.log(`[班级状态] 为节点添加状态: id=${enhanced.id}, label=${enhanced.label}, category=${category || '名称模式'}, status=${status}`);
} else if (isClass) {
// 调试日志:记录是班级但没有状态的节点
console.warn(
`[班级状态] 班级节点未找到状态: id=${enhanced.id}, label=${enhanced.label}, category=${category || '名称模式'}, deptId=${enhanced.id}`
);
}
if (Array.isArray(enhanced.children) && enhanced.children.length > 0) {
enhanced.children = enhanceDeptNodes(enhanced.children);
}
return enhanced;
});
};
/** 加载部门树数据 */ /** 加载部门树数据 */
const loadDeptTree = async () => { const loadDeptTree = async () => {
try { try {
loading.value = true; loading.value = true;
// 先加载班级状态缓存
await ensureClassStatusCache();
const response = await request({ const response = await request({
url: '/system/user/deptTree', url: '/system/user/deptTree',
method: 'get', method: 'get',
@@ -460,7 +623,45 @@ const loadDeptTree = async () => {
} }
}); });
if (response && response.code === 200) { if (response && response.code === 200) {
deptTreeData.value = response.data || []; const rawData = response.data || [];
console.log(`[部门树] 加载原始数据,共 ${rawData.length} 个根节点`);
// 调试:收集所有叶子节点(可能是班级节点)
function collectLeafNodes(nodes: any[], path = '', depth = 0): any[] {
const results: any[] = [];
nodes.forEach((node) => {
const currentPath = path ? `${path} > ${node.label}` : node.label;
const isLeaf = !node.children || node.children.length === 0;
if (isLeaf) {
results.push({
path: currentPath,
depth: depth,
id: node.id,
label: node.label,
deptCategory: node.deptCategory,
dept_category: node.dept_category,
params: node.params
});
}
if (node.children && node.children.length > 0) {
results.push(...collectLeafNodes(node.children, currentPath, depth + 1));
}
});
return results;
}
const leafNodes = collectLeafNodes(rawData);
console.log(`[部门树] 找到 ${leafNodes.length} 个叶子节点(可能是班级)`);
console.log('[部门树] 前10个叶子节点示例:', leafNodes.slice(0, 10));
// 增强节点数据(添加班级状态)
const enhancedTree = enhanceDeptNodes(rawData);
// 保存原始树数据
originalTreeData.value = enhancedTree;
deptTreeData.value = enhancedTree;
console.log('[部门树] 节点增强完成');
} }
} catch (error) { } catch (error) {
console.error('获取部门树数据失败:', error); console.error('获取部门树数据失败:', error);
@@ -544,12 +745,69 @@ const handleScopeDialogClose = () => {
// 清空相关状态 // 清空相关状态
currentBatch.value = null; currentBatch.value = null;
checkedDeptIds.value = []; checkedDeptIds.value = [];
// 重置过滤状态为"全部"
classStatusFilter.value = 'all';
// 清空树形组件的勾选状态 // 清空树形组件的勾选状态
if (scopeTreeRef.value) { if (scopeTreeRef.value) {
scopeTreeRef.value.setCheckedKeys([]); scopeTreeRef.value.setCheckedKeys([]);
} }
}; };
/** 过滤后的树数据(计算属性) */
const filteredTreeData = computed(() => {
if (classStatusFilter.value === 'all') {
return originalTreeData.value;
} else if (classStatusFilter.value === 'normal') {
// 只显示状态为"1"(正常)的班级节点及其父节点
return filterTreeByClassStatus(originalTreeData.value, '1');
}
return originalTreeData.value;
});
/** 根据班级状态过滤树数据 */
const filterTreeByClassStatus = (nodes: any[], targetStatus: string): any[] => {
if (!nodes || nodes.length === 0) {
return [];
}
return nodes
.map((node) => {
const isClass = isClassNode(node);
// 如果是班级节点,检查状态
if (isClass) {
const nodeStatus = node.classStatus;
// 如果状态匹配,保留该节点
if (nodeStatus === targetStatus) {
return { ...node };
}
// 状态不匹配,返回null(将被过滤掉)
return null;
}
// 非班级节点,递归处理子节点
const filteredChildren = node.children && node.children.length > 0 ? filterTreeByClassStatus(node.children, targetStatus) : [];
// 如果有子节点被保留,则保留当前节点
if (filteredChildren.length > 0) {
return {
...node,
children: filteredChildren
};
}
// 没有符合条件的子节点,过滤掉当前节点
return null;
})
.filter((node) => node !== null);
};
/** 处理过滤条件变化 */
const handleFilterChange = () => {
// 过滤逻辑由计算属性 filteredTreeData 自动处理
// 这里可以添加额外的逻辑,比如重置展开状态等
};
/** 查看详细按钮操作 */ /** 查看详细按钮操作 */
const handleView = async (row: StuColBatchVO) => { const handleView = async (row: StuColBatchVO) => {
try { try {
@@ -609,4 +867,29 @@ onMounted(() => {
.mb-3 { .mb-3 {
margin-bottom: 12px; margin-bottom: 12px;
} }
.scope-content {
display: flex;
flex-direction: column;
}
.filter-buttons {
margin-bottom: 12px;
padding-top: 8px;
}
.scope-node {
display: inline-flex;
align-items: center;
gap: 6px;
}
.scope-label {
display: inline-flex;
align-items: center;
}
.scope-tag :deep(.el-tag) {
margin-left: 0;
}
</style> </style>