Преглед изворни кода

“培训申请修复v1.3”

weizhong пре 11 месеци
родитељ
комит
4fbd380a07

+ 55 - 0
backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/SysUserBasicVo.java

@@ -0,0 +1,55 @@
+package org.dromara.rs.domain.vo;
+
+import lombok.Data;
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 用户基本信息VO
+ * 用于培训申请/记录中的同行人信息展示
+ * 不限制账号类型,只返回必要的基础信息
+ *
+ * @author weizzzz
+ * @date 2025-11-06
+ */
+@Data
+public class SysUserBasicVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 用户账号
+     */
+    private String userName;
+
+    /**
+     * 用户昵称(真实姓名)
+     */
+    private String nickName;
+
+    /**
+     * 账号类型(0-学生 1-教师 2-其他)
+     */
+    private String accountType;
+
+    /**
+     * 账号状态(0-正常 1-停用)
+     */
+    private String status;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+}

+ 125 - 111
backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTeacherMapper.java

@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
+import org.dromara.common.mybatis.annotation.DataColumn;
+import org.dromara.common.mybatis.annotation.DataPermission;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 import org.dromara.system.domain.SysUser;
 import org.dromara.system.domain.vo.SysTeacherVo;
@@ -28,25 +30,26 @@ public interface SysTeacherMapper extends BaseMapperPlus<SysUser, SysTeacherVo>
      * @return 教师信息分页列表
      */
     @Select("""
-        SELECT 
-            u.user_id,
-            u.user_name,
-            u.nick_name,
-            u.phonenumber,
-            u.email,
-            u.dept_id,
-            d.dept_name,
-            u.sex,
-            u.status,
-            u.create_time
-        FROM sys_user u
-        LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
-        WHERE u.del_flag = '0' 
-        AND u.account_type = '1'
-        ${ew.customSqlSegment}
-        ORDER BY u.create_time DESC
-        """)
-    Page<SysTeacherVo> selectPageTeacherList(@Param("page") Page<SysTeacherVo> page, @Param(Constants.WRAPPER) Wrapper<SysUser> wrapper);
+            SELECT
+                u.user_id,
+                u.user_name,
+                u.nick_name,
+                u.phonenumber,
+                u.email,
+                u.dept_id,
+                d.dept_name,
+                u.sex,
+                u.status,
+                u.create_time
+            FROM sys_user u
+            LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
+            WHERE u.del_flag = '0'
+            AND u.account_type = '1'
+            ${ew.customSqlSegment}
+            ORDER BY u.create_time DESC
+            """)
+    Page<SysTeacherVo> selectPageTeacherList(@Param("page") Page<SysTeacherVo> page,
+            @Param(Constants.WRAPPER) Wrapper<SysUser> wrapper);
 
     /**
      * 查询教师信息列表
@@ -55,24 +58,24 @@ public interface SysTeacherMapper extends BaseMapperPlus<SysUser, SysTeacherVo>
      * @return 教师信息列表
      */
     @Select("""
-        SELECT 
-            u.user_id,
-            u.user_name,
-            u.nick_name,
-            u.phonenumber,
-            u.email,
-            u.dept_id,
-            d.dept_name,
-            u.sex,
-            u.status,
-            u.create_time
-        FROM sys_user u
-        LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
-        WHERE u.del_flag = '0' 
-        AND u.account_type = '1'
-        ${ew.customSqlSegment}
-        ORDER BY u.create_time DESC
-        """)
+            SELECT
+                u.user_id,
+                u.user_name,
+                u.nick_name,
+                u.phonenumber,
+                u.email,
+                u.dept_id,
+                d.dept_name,
+                u.sex,
+                u.status,
+                u.create_time
+            FROM sys_user u
+            LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
+            WHERE u.del_flag = '0'
+            AND u.account_type = '1'
+            ${ew.customSqlSegment}
+            ORDER BY u.create_time DESC
+            """)
     List<SysTeacherVo> selectTeacherList(@Param(Constants.WRAPPER) Wrapper<SysUser> wrapper);
 
     /**
@@ -83,78 +86,89 @@ public interface SysTeacherMapper extends BaseMapperPlus<SysUser, SysTeacherVo>
      * @return 教师信息列表
      */
     @Select("""
-        SELECT 
-            u.user_id,
-            u.user_name,
-            u.nick_name,
-            u.phonenumber,
-            u.email,
-            u.dept_id,
-            d.dept_name,
-            u.sex,
-            u.status,
-            u.create_time
-        FROM sys_user u
-        LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
-        WHERE u.del_flag = '0' 
-        AND u.account_type = '1'
-        AND (u.nick_name LIKE CONCAT('%', #{name}, '%') 
-             OR u.user_name LIKE CONCAT('%', #{name}, '%')
-             OR u.phonenumber LIKE CONCAT('%', #{name}, '%'))
-        AND u.status = '0'
-        ORDER BY u.create_time DESC
-        LIMIT #{limit}
-        """)
+            SELECT
+                u.user_id,
+                u.user_name,
+                u.nick_name,
+                u.phonenumber,
+                u.email,
+                u.dept_id,
+                d.dept_name,
+                u.sex,
+                u.status,
+                u.create_time
+            FROM sys_user u
+            LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
+            WHERE u.del_flag = '0'
+            AND u.account_type = '1'
+            AND (u.nick_name LIKE CONCAT('%', #{name}, '%')
+                 OR u.user_name LIKE CONCAT('%', #{name}, '%')
+                 OR u.phonenumber LIKE CONCAT('%', #{name}, '%'))
+            AND u.status = '0'
+            ORDER BY u.create_time DESC
+            LIMIT #{limit}
+            """)
     List<SysTeacherVo> searchTeachersByName(@Param("name") String name, @Param("limit") Integer limit);
 
     /**
-     * 根据部门ID获取教师列表
+     * 根据部门ID获取教师列表(带数据权限控制)
+     * 
+     * 说明:添加数据权限注解后,只会返回用户有权限访问的部门的教师
      *
      * @param deptId 部门ID
      * @return 教师信息列表
      */
+    @DataPermission({
+            @DataColumn(key = "deptName", value = "u.dept_id")
+    })
     @Select("""
-        SELECT 
-            u.user_id,
-            u.user_name,
-            u.nick_name,
-            u.phonenumber,
-            u.email,
-            u.dept_id,
-            d.dept_name,
-            u.sex,
-            u.status,
-            u.create_time
-        FROM sys_user u
-        LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
-        WHERE u.del_flag = '0' 
-        AND u.account_type = '1'
-        AND u.dept_id = #{deptId}
-        AND u.status = '0'
-        ORDER BY u.create_time DESC
-        """)
+            SELECT
+                u.user_id,
+                u.user_name,
+                u.nick_name,
+                u.phonenumber,
+                u.email,
+                u.dept_id,
+                d.dept_name,
+                u.sex,
+                u.status,
+                u.create_time
+            FROM sys_user u
+            LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
+            WHERE u.del_flag = '0'
+            AND u.account_type = '1'
+            AND u.dept_id = #{deptId}
+            AND u.status = '0'
+            ORDER BY u.create_time DESC
+            """)
     List<SysTeacherVo> selectTeachersByDeptId(@Param("deptId") Long deptId);
 
     /**
-     * 获取部门树形结构(只包含有教师的部门)
+     * 获取部门树形结构(只包含有教师且用户有权限访问的部门)
+     * 
+     * 说明:添加数据权限注解后,只会返回用户有权限访问的部门
+     * 这样可以避免在同行人选择器中显示用户无权访问的部门
      *
      * @return 部门树信息集合
      */
+    @DataPermission({
+            @DataColumn(key = "deptName", value = "d.dept_id")
+    })
     @Select("""
-        SELECT DISTINCT
-            d.dept_id as id,
-            d.parent_id,
-            d.dept_name as label,
-            d.order_num
-        FROM sys_dept d
-        INNER JOIN sys_user u ON d.dept_id = u.dept_id
-        WHERE d.del_flag = '0' 
-        AND d.status = '0'
-        AND u.del_flag = '0' 
-        AND u.account_type = '1'
-        AND u.status = '0'
-        ORDER BY d.order_num
-        """)
+            SELECT DISTINCT
+                d.dept_id as id,
+                d.parent_id,
+                d.dept_name as label,
+                d.order_num
+            FROM sys_dept d
+            INNER JOIN sys_user u ON d.dept_id = u.dept_id
+            WHERE d.del_flag = '0'
+            AND d.status = '0'
+            AND u.del_flag = '0'
+            AND u.account_type = '1'
+            AND u.status = '0'
+            ORDER BY d.order_num
+            """)
     List<SysDeptTreeVo> selectDeptTreeWithTeachers();
 
     /**
@@ -164,21 +178,21 @@ public interface SysTeacherMapper extends BaseMapperPlus<SysUser, SysTeacherVo>
      * @return 父级部门信息集合
      */
     @Select("""
-        <script>
-        SELECT DISTINCT
-            dept_id as id,
-            parent_id,
-            dept_name as label,
-            order_num
-        FROM sys_dept
-        WHERE del_flag = '0' 
-        AND status = '0'
-        AND dept_id IN
-        <foreach collection="deptIds" item="deptId" open="(" separator="," close=")">
-            #{deptId}
-        </foreach>
-        ORDER BY order_num
-        </script>
-        """)
+            <script>
+            SELECT DISTINCT
+                dept_id as id,
+                parent_id,
+                dept_name as label,
+                order_num
+            FROM sys_dept
+            WHERE del_flag = '0'
+            AND status = '0'
+            AND dept_id IN
+            <foreach collection="deptIds" item="deptId" open="(" separator="," close=")">
+                #{deptId}
+            </foreach>
+            ORDER BY order_num
+            </script>
+            """)
     List<SysDeptTreeVo> selectDeptsByIds(@Param("deptIds") List<Long> deptIds);
-} 
+}

+ 59 - 4
web/src/components/TeacherMultiSelector/index.vue

@@ -64,6 +64,15 @@
         <!-- 右侧教师列表 -->
         <div class="teacher-panel">
           <div v-if="!selectedDeptId" class="empty-tip">请选择左侧部门查看教师列表</div>
+          <div v-else-if="loading" class="empty-tip">
+            <el-icon class="is-loading"><Loading /></el-icon>
+            <span style="margin-left: 8px">加载中...</span>
+          </div>
+          <div v-else-if="permissionDenied" class="permission-denied-tip">
+            <el-icon><Lock /></el-icon>
+            <p>您没有权限查看该部门的教师信息</p>
+            <p class="tip-desc">请联系管理员配置数据权限或选择其他部门</p>
+          </div>
           <div v-else>
             <div class="panel-header">
               <span>{{ selectedDeptName }} - 教师列表</span>
@@ -100,7 +109,7 @@
                   性别:{{ teacher.sex === '0' ? '男' : teacher.sex === '1' ? '女' : '未知' }} | 电话:{{ teacher.phonenumber || '暂无' }} | 邮箱:{{ teacher.email || '暂无' }}
                 </div>
               </div>
-              <div v-if="filteredTeachers.length === 0" class="empty-tip">暂无教师数据</div>
+              <div v-if="filteredTeachers.length === 0 && !permissionDenied" class="empty-tip">该部门暂无教师数据</div>
             </div>
           </div>
         </div>
@@ -118,7 +127,7 @@
 
 <script setup lang="ts">
 import { ref, computed, watch, onMounted, nextTick } from 'vue';
-import { Search } from '@element-plus/icons-vue';
+import { Search, Loading, Lock } from '@element-plus/icons-vue';
 import { getDeptTreeData, getTeachersByDeptId } from '@/api/system/teacher';
 import { TeacherVO, DeptTreeNode } from '@/api/system/teacher/types';
 import { ElMessage } from 'element-plus';
@@ -152,6 +161,8 @@ const treeData = ref<DeptTreeNode[]>([]);
 const teacherList = ref<TeacherVO[]>([]);
 const selectedDeptId = ref<number | null>(null);
 const selectedDeptName = ref('');
+const loading = ref(false); // 加载状态
+const permissionDenied = ref(false); // 权限被拒标识
 
 // 显示值计算
 const displayValue = computed(() => {
@@ -230,6 +241,8 @@ const closeDialog = () => {
     selectedDeptName.value = '';
     teacherList.value = [];
     teacherSearchText.value = '';
+    loading.value = false;
+    permissionDenied.value = false;
   }, 300);
 };
 
@@ -290,14 +303,34 @@ const handleTreeNodeClick = async (data: DeptTreeNode) => {
   selectedDeptId.value = Number(data.id);
   selectedDeptName.value = data.label;
   teacherSearchText.value = '';
+  loading.value = true;
+  permissionDenied.value = false;
   
   try {
     const response = await getTeachersByDeptId(Number(data.id));
     teacherList.value = response.data || [];
-  } catch (error) {
-    ElMessage.error('加载教师列表失败');
+    
+    // 如果返回空列表,可能是权限问题(也可能真的没有教师)
+    // 后端添加数据权限后,无权限的部门会被过滤,理论上不会出现在部门树中
+    // 但为了更好的用户体验,仍然保留这个逻辑
+    if (teacherList.value.length === 0) {
+      console.log(`部门 ${data.label} 没有教师数据`);
+    }
+  } catch (error: any) {
     console.error('加载教师列表失败:', error);
+    
+    // 根据错误信息判断是否为权限问题
+    const errorMsg = error?.msg || error?.message || '';
+    if (error?.code === 403 || errorMsg.includes('权限') || errorMsg.includes('permission')) {
+      permissionDenied.value = true;
+      console.warn(`用户没有权限访问部门 ${data.label} 的教师列表`);
+    } else {
+      ElMessage.error('加载教师列表失败');
+    }
+    
     teacherList.value = [];
+  } finally {
+    loading.value = false;
   }
 };
 
@@ -424,6 +457,28 @@ onMounted(() => {
   padding: 40px 0;
 }
 
+.permission-denied-tip {
+  text-align: center;
+  padding: 60px 20px;
+  color: #909399;
+}
+
+.permission-denied-tip .el-icon {
+  font-size: 48px;
+  color: #f56c6c;
+  margin-bottom: 16px;
+}
+
+.permission-denied-tip p {
+  margin: 8px 0;
+  font-size: 14px;
+}
+
+.permission-denied-tip .tip-desc {
+  font-size: 12px;
+  color: #c0c4cc;
+}
+
 .dialog-footer {
   display: flex;
   justify-content: flex-end;