提交
This commit is contained in:
1 parent
4d48300a1d
commit
4c8434595a
3 files changed
+292
-9
No files matched your search
+3
-3
@@ -205,7 +205,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND sm.major_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
|
||||
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>
|
||||
|
||||
<!-- 查询二级学院下的学院、专业、班级学生统计信息(按床位分配统计) -->
|
||||
@@ -255,7 +255,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND sm.major_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
|
||||
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>
|
||||
|
||||
<!-- 查询二级学院下的学院床位分配统计信息(基于学生表统计需求,基于床位分配表统计已分配) -->
|
||||
@@ -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 sm.college_id IS NOT NULL
|
||||
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>
|
||||
|
||||
</mapper>
|
||||
@@ -536,7 +536,7 @@
|
||||
</template>
|
||||
|
||||
<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 {
|
||||
listPovertyApplication,
|
||||
@@ -563,7 +563,7 @@ import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
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') || {}
|
||||
);
|
||||
|
||||
@@ -805,7 +805,7 @@ function handleRecognitionTypeChange(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,15 +177,38 @@
|
||||
<!-- 收集范围设置对话框 -->
|
||||
<el-dialog title="设置收集范围" v-model="scopeDialog.visible" width="500px" append-to-body @close="handleScopeDialogClose">
|
||||
<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
|
||||
ref="scopeTreeRef"
|
||||
:data="deptTreeData"
|
||||
:data="filteredTreeData"
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:props="{ children: 'children', label: 'label' }"
|
||||
: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>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -253,13 +276,27 @@
|
||||
</template>
|
||||
|
||||
<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 { StuColBatchVO, StuColBatchQuery, StuColBatchForm } from '@/api/xg/xsjcxx/XgStuColBatch/stuColBatch/types';
|
||||
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 { ElMessage } from 'element-plus';
|
||||
|
||||
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 buttonLoading = ref(false);
|
||||
@@ -285,8 +322,14 @@ const scopeDialog = reactive({
|
||||
const scopeButtonLoading = ref(false);
|
||||
const currentBatch = ref<StuColBatchVO | null>(null);
|
||||
const deptTreeData = ref<any[]>([]);
|
||||
const originalTreeData = ref<any[]>([]); // 原始树数据(未过滤)
|
||||
const checkedDeptIds = ref<number[]>([]);
|
||||
const scopeTreeRef = ref();
|
||||
// 班级状态过滤:'all' 全部, 'normal' 正常
|
||||
const classStatusFilter = ref<'all' | 'normal'>('all');
|
||||
// 班级状态缓存
|
||||
const classStatusCache = new Map<string, string>();
|
||||
let classStatusLoaded = false;
|
||||
|
||||
// 查看详细弹窗相关数据
|
||||
const viewDialog = reactive({
|
||||
@@ -436,6 +479,8 @@ const handleScope = async (row: StuColBatchVO) => {
|
||||
currentBatch.value = row;
|
||||
// 先清空勾选状态
|
||||
checkedDeptIds.value = [];
|
||||
// 重置过滤状态为"全部"
|
||||
classStatusFilter.value = 'all';
|
||||
|
||||
await loadDeptTree();
|
||||
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 () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
// 先加载班级状态缓存
|
||||
await ensureClassStatusCache();
|
||||
|
||||
const response = await request({
|
||||
url: '/system/user/deptTree',
|
||||
method: 'get',
|
||||
@@ -460,7 +623,45 @@ const loadDeptTree = async () => {
|
||||
}
|
||||
});
|
||||
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) {
|
||||
console.error('获取部门树数据失败:', error);
|
||||
@@ -544,12 +745,69 @@ const handleScopeDialogClose = () => {
|
||||
// 清空相关状态
|
||||
currentBatch.value = null;
|
||||
checkedDeptIds.value = [];
|
||||
// 重置过滤状态为"全部"
|
||||
classStatusFilter.value = 'all';
|
||||
// 清空树形组件的勾选状态
|
||||
if (scopeTreeRef.value) {
|
||||
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) => {
|
||||
try {
|
||||
@@ -609,4 +867,29 @@ onMounted(() => {
|
||||
.mb-3 {
|
||||
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>
|
||||
Reference in new issue
Block a user