|
@@ -0,0 +1,552 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <Dialog v-model="dialogVisible" title="web端角色路由配置" width="1200">
|
|
|
|
|
+ <el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="120px">
|
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="角色名称">
|
|
|
|
|
+ <el-tag>{{ formData.name }}</el-tag>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="角色标识">
|
|
|
|
|
+ <el-tag>{{ formData.code }}</el-tag>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <el-divider content-position="left">web路由数据权限</el-divider>
|
|
|
|
|
+ <br/>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 字典数据选择 -->
|
|
|
|
|
+ <el-form-item label="路由类型">
|
|
|
|
|
+ <el-checkbox-group v-model="formData.dataScopeWebRoute" size="large">
|
|
|
|
|
+ <el-checkbox-button v-for="dict in routeTypeOptions" :key="dict.value" :label="dict.value">
|
|
|
|
|
+ {{ dict.label }}
|
|
|
|
|
+ </el-checkbox-button>
|
|
|
|
|
+ </el-checkbox-group>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 标签页 -->
|
|
|
|
|
+ <el-tabs v-model="activeTab" type="border-card">
|
|
|
|
|
+ <!-- 课程权限 -->
|
|
|
|
|
+ <el-tab-pane label="课程权限" name="course">
|
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <!-- 通识课权限 -->
|
|
|
|
|
+ <el-form-item label="通识课">
|
|
|
|
|
+ <el-card class="w-full h-300px !overflow-y-scroll" shadow="never">
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ 全选/全不选:
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="courseTabs.generalTreeNodeAll"
|
|
|
|
|
+ active-text="是"
|
|
|
|
|
+ inactive-text="否"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ @change="handleCheckedTreeNodeAll('course', 'general')"
|
|
|
|
|
+ />
|
|
|
|
|
+ 全部展开/折叠:
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="courseTabs.generalDeptExpand"
|
|
|
|
|
+ active-text="展开"
|
|
|
|
|
+ inactive-text="折叠"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ @change="handleCheckedTreeExpand('course', 'general')"
|
|
|
|
|
+ />
|
|
|
|
|
+ 父子联动:
|
|
|
|
|
+ <el-switch v-model="courseTabs.generalCheckStrictly" active-text="是" inactive-text="否" inline-prompt />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <el-tree
|
|
|
|
|
+ ref="generalTreeRef"
|
|
|
|
|
+ :check-strictly="!courseTabs.generalCheckStrictly"
|
|
|
|
|
+ :data="courseTabs.generalCourseOptions"
|
|
|
|
|
+ :props="defaultProps"
|
|
|
|
|
+ default-expand-all
|
|
|
|
|
+ empty-text="加载中,请稍后"
|
|
|
|
|
+ node-key="id"
|
|
|
|
|
+ show-checkbox
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <!-- 实操课权限 -->
|
|
|
|
|
+ <el-form-item label="实操课">
|
|
|
|
|
+ <el-card class="w-full h-300px !overflow-y-scroll" shadow="never">
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ 全选/全不选:
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="courseTabs.practicalTreeNodeAll"
|
|
|
|
|
+ active-text="是"
|
|
|
|
|
+ inactive-text="否"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ @change="handleCheckedTreeNodeAll('course', 'practical')"
|
|
|
|
|
+ />
|
|
|
|
|
+ 全部展开/折叠:
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="courseTabs.practicalDeptExpand"
|
|
|
|
|
+ active-text="展开"
|
|
|
|
|
+ inactive-text="折叠"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ @change="handleCheckedTreeExpand('course', 'practical')"
|
|
|
|
|
+ />
|
|
|
|
|
+ 父子联动:
|
|
|
|
|
+ <el-switch v-model="courseTabs.practicalCheckStrictly" active-text="是" inactive-text="否" inline-prompt />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <el-tree
|
|
|
|
|
+ ref="practicalTreeRef"
|
|
|
|
|
+ :check-strictly="!courseTabs.practicalCheckStrictly"
|
|
|
|
|
+ :data="courseTabs.practicalCourseOptions"
|
|
|
|
|
+ :props="defaultProps"
|
|
|
|
|
+ default-expand-all
|
|
|
|
|
+ empty-text="加载中,请稍后"
|
|
|
|
|
+ node-key="id"
|
|
|
|
|
+ show-checkbox
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- blockly课程权限 -->
|
|
|
|
|
+ <el-tab-pane label="blockly课程权限" name="blockly">
|
|
|
|
|
+ <el-form-item label="blockly课程权限">
|
|
|
|
|
+ <el-card class="w-full h-300px !overflow-y-scroll" shadow="never">
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ 全选/全不选:
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="blocklyTabs.generalTreeNodeAll"
|
|
|
|
|
+ active-text="是"
|
|
|
|
|
+ inactive-text="否"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ @change="handleCheckedTreeNodeAll('blockly', 'general')"
|
|
|
|
|
+ />
|
|
|
|
|
+ 全部展开/折叠:
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="blocklyTabs.generalDeptExpand"
|
|
|
|
|
+ active-text="展开"
|
|
|
|
|
+ inactive-text="折叠"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ @change="handleCheckedTreeExpand('blockly', 'general')"
|
|
|
|
|
+ />
|
|
|
|
|
+ 父子联动:
|
|
|
|
|
+ <el-switch v-model="blocklyTabs.generalCheckStrictly" active-text="是" inactive-text="否" inline-prompt />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <el-tree
|
|
|
|
|
+ ref="blocklyTreeRef"
|
|
|
|
|
+ :check-strictly="!blocklyTabs.generalCheckStrictly"
|
|
|
|
|
+ :data="blocklyTabs.generalBlocklyOptions"
|
|
|
|
|
+ :props="defaultProps"
|
|
|
|
|
+ default-expand-all
|
|
|
|
|
+ empty-text="加载中,请稍后"
|
|
|
|
|
+ node-key="id"
|
|
|
|
|
+ show-checkbox
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- AI实验课权限 -->
|
|
|
|
|
+ <el-tab-pane label="AI实验课权限" name="aiCourse">
|
|
|
|
|
+ <el-form-item label="AI实验课权限">
|
|
|
|
|
+ <el-card class="w-full h-300px !overflow-y-scroll" shadow="never">
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ 全选/全不选:
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="aiCourseTabs.generalTreeNodeAll"
|
|
|
|
|
+ active-text="是"
|
|
|
|
|
+ inactive-text="否"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ @change="handleCheckedTreeNodeAll('aiCourse', 'general')"
|
|
|
|
|
+ />
|
|
|
|
|
+ 全部展开/折叠:
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="aiCourseTabs.generalDeptExpand"
|
|
|
|
|
+ active-text="展开"
|
|
|
|
|
+ inactive-text="折叠"
|
|
|
|
|
+ inline-prompt
|
|
|
|
|
+ @change="handleCheckedTreeExpand('aiCourse', 'general')"
|
|
|
|
|
+ />
|
|
|
|
|
+ 父子联动:
|
|
|
|
|
+ <el-switch v-model="aiCourseTabs.generalCheckStrictly" active-text="是" inactive-text="否" inline-prompt />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <el-tree
|
|
|
|
|
+ ref="aiCourseTreeRef"
|
|
|
|
|
+ :check-strictly="!aiCourseTabs.generalCheckStrictly"
|
|
|
|
|
+ :data="aiCourseTabs.generalAiCourseOptions"
|
|
|
|
|
+ :props="defaultProps"
|
|
|
|
|
+ default-expand-all
|
|
|
|
|
+ empty-text="加载中,请稍后"
|
|
|
|
|
+ node-key="id"
|
|
|
|
|
+ show-checkbox
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+ </el-tabs>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
|
|
|
|
+ <el-button @click="dialogVisible = false">取 消</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </Dialog>
|
|
|
|
|
+</template>
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+import { defaultProps } from '@/utils/tree'
|
|
|
|
|
+import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
|
|
|
|
+import * as RoleApi from '@/api/system/role'
|
|
|
|
|
+import { CourseApi } from '@/api/bjdx/course'
|
|
|
|
|
+import { AiCourseTypeApi } from '@/api/aiCourse/aiCourseType'
|
|
|
|
|
+import { BlocklyTypeApi } from '@/api/blockly/blocklyType'
|
|
|
|
|
+import * as PermissionApi from '@/api/system/permission'
|
|
|
|
|
+
|
|
|
|
|
+defineOptions({ name: 'SystemRoleWebPermissionForm' })
|
|
|
|
|
+
|
|
|
|
|
+const { t } = useI18n() // 国际化
|
|
|
|
|
+const message = useMessage() // 消息弹窗
|
|
|
|
|
+
|
|
|
|
|
+const dialogVisible = ref(false) // 弹窗的是否展示
|
|
|
|
|
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
|
|
|
|
+const activeTab = ref('course') // 当前激活的标签页
|
|
|
|
|
+const routeTypeOptions = ref(getDictOptions(DICT_TYPE.WEB_ROLE_ROUTE)) // 路由类型字典选项
|
|
|
|
|
+console.log('routeTypeOptions', routeTypeOptions)
|
|
|
|
|
+const formData = reactive({
|
|
|
|
|
+ id: undefined,
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ code: '',
|
|
|
|
|
+ dataScopeCourseIds: [],
|
|
|
|
|
+ dataScopeAiCourseIds: [],
|
|
|
|
|
+ dataScopeBlocklyIds: [],
|
|
|
|
|
+ dataScopeWebRoute: []
|
|
|
|
|
+})
|
|
|
|
|
+const formRef = ref() // 表单 Ref
|
|
|
|
|
+
|
|
|
|
|
+// 课程权限相关变量
|
|
|
|
|
+const generalTreeRef = ref() // 通识课菜单树组件 Ref
|
|
|
|
|
+const practicalTreeRef = ref() // 实操课菜单树组件 Ref
|
|
|
|
|
+const courseTabs = reactive({
|
|
|
|
|
+ generalCourseOptions: [] as any[], // 通识课树形结构
|
|
|
|
|
+ generalDeptExpand: true, // 通识课展开/折叠
|
|
|
|
|
+ generalTreeNodeAll: false, // 通识课全选/全不选
|
|
|
|
|
+ generalCheckStrictly: true, // 通识课是否严格模式
|
|
|
|
|
+ practicalCourseOptions: [] as any[], // 实操课树形结构
|
|
|
|
|
+ practicalDeptExpand: true, // 实操课展开/折叠
|
|
|
|
|
+ practicalTreeNodeAll: false, // 实操课全选/全不选
|
|
|
|
|
+ practicalCheckStrictly: true, // 实操课是否严格模式
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// AI实验课权限相关变量
|
|
|
|
|
+const aiCourseTreeRef = ref() // AI实验课菜单树组件 Ref
|
|
|
|
|
+const aiCourseTabs = reactive({
|
|
|
|
|
+ generalAiCourseOptions: [] as any[], // AI实验课树形结构
|
|
|
|
|
+ generalDeptExpand: true, // AI实验课展开/折叠
|
|
|
|
|
+ generalTreeNodeAll: false, // AI实验课全选/全不选
|
|
|
|
|
+ generalCheckStrictly: true, // AI实验课是否严格模式
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// blockly课程权限相关变量
|
|
|
|
|
+const blocklyTreeRef = ref() // blockly课程菜单树组件 Ref
|
|
|
|
|
+const blocklyTabs = reactive({
|
|
|
|
|
+ generalBlocklyOptions: [] as any[], // blockly课程树形结构
|
|
|
|
|
+ generalDeptExpand: true, // blockly课程展开/折叠
|
|
|
|
|
+ generalTreeNodeAll: false, // blockly课程全选/全不选
|
|
|
|
|
+ generalCheckStrictly: true, // blockly课程是否严格模式
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+/** 打开弹窗 */
|
|
|
|
|
+const open = async (row: RoleApi.RoleVO) => {
|
|
|
|
|
+ dialogVisible.value = true
|
|
|
|
|
+ resetForm()
|
|
|
|
|
+
|
|
|
|
|
+ // 设置数据
|
|
|
|
|
+ formData.id = row.id
|
|
|
|
|
+ formData.name = row.name
|
|
|
|
|
+ formData.code = row.code
|
|
|
|
|
+ formData.dataScopeWebRoute = row.dataScopeWebRoute || []
|
|
|
|
|
+ // formData.dataScopeWebRoute = ["course","aiCourse","blockly"]
|
|
|
|
|
+
|
|
|
|
|
+ // 加载各类型数据
|
|
|
|
|
+ await Promise.all([
|
|
|
|
|
+ loadCourseData(row),
|
|
|
|
|
+ loadAiCourseData(row),
|
|
|
|
|
+ loadBlocklyData(row)
|
|
|
|
|
+ ])
|
|
|
|
|
+
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+
|
|
|
|
|
+ // 兼容旧数据格式
|
|
|
|
|
+ // if (formData.dataScopeWebRoute.length === 0) {
|
|
|
|
|
+ // if (row.dataScopeCourseIds && row.dataScopeCourseIds.length > 0) {
|
|
|
|
|
+ // formData.dataScopeWebRoute.push('course')
|
|
|
|
|
+ // }
|
|
|
|
|
+ // if (row.dataScopeAiCourseIds && row.dataScopeAiCourseIds.length > 0) {
|
|
|
|
|
+ // formData.dataScopeWebRoute.push('aiCourse')
|
|
|
|
|
+ // }
|
|
|
|
|
+ // if (row.dataScopeBlocklyIds && row.dataScopeBlocklyIds.length > 0) {
|
|
|
|
|
+ // formData.dataScopeWebRoute.push('blockly')
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ // 默认选择第一个可用的标签页
|
|
|
|
|
+ if (formData.dataScopeWebRoute.length > 0) {
|
|
|
|
|
+ activeTab.value = formData.dataScopeWebRoute[0]
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
|
|
|
+
|
|
|
|
|
+/** 加载课程数据 */
|
|
|
|
|
+async function loadCourseData(row: RoleApi.RoleVO) {
|
|
|
|
|
+ const allCourses = await CourseApi.getCourseTypeTree()
|
|
|
|
|
+
|
|
|
|
|
+ courseTabs.generalCourseOptions = filterCoursesByType(allCourses, '通识课')
|
|
|
|
|
+ courseTabs.practicalCourseOptions = filterCoursesByType(allCourses, '实操课')
|
|
|
|
|
+
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+
|
|
|
|
|
+ // 设置选中状态
|
|
|
|
|
+ if (row.dataScopeCourseIds && row.dataScopeCourseIds.length > 0) {
|
|
|
|
|
+ row.dataScopeCourseIds.forEach((courseId: number): void => {
|
|
|
|
|
+ generalTreeRef.value?.setChecked(courseId * 100, true, false)
|
|
|
|
|
+ practicalTreeRef.value?.setChecked(courseId * 100, true, false)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 加载AI实验课数据 */
|
|
|
|
|
+async function loadAiCourseData(row: RoleApi.RoleVO) {
|
|
|
|
|
+ const allCourses = await AiCourseTypeApi.getAiCourseTypeTree()
|
|
|
|
|
+ aiCourseTabs.generalAiCourseOptions = filterCoursesByType(allCourses, "aiCourseType")
|
|
|
|
|
+
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+
|
|
|
|
|
+ // 设置选中状态
|
|
|
|
|
+ if (row.dataScopeAiCourseIds && row.dataScopeAiCourseIds.length > 0) {
|
|
|
|
|
+ row.dataScopeAiCourseIds.forEach((courseId: number): void => {
|
|
|
|
|
+ aiCourseTreeRef.value?.setChecked(courseId, true, false)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 加载blockly课程数据 */
|
|
|
|
|
+async function loadBlocklyData(row: RoleApi.RoleVO) {
|
|
|
|
|
+ const allCourses = await BlocklyTypeApi.getBlocklyTypeTree()
|
|
|
|
|
+ blocklyTabs.generalBlocklyOptions = filterCoursesByType(allCourses, "blocklyType")
|
|
|
|
|
+
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+
|
|
|
|
|
+ // 设置选中状态
|
|
|
|
|
+ if (row.dataScopeBlocklyIds && row.dataScopeBlocklyIds.length > 0) {
|
|
|
|
|
+ row.dataScopeBlocklyIds.forEach((courseId: number): void => {
|
|
|
|
|
+ blocklyTreeRef.value?.setChecked(courseId, true, false)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 根据课程类型筛选课程,并保留完整的树结构
|
|
|
|
|
+ * @param courses 原始课程树数据
|
|
|
|
|
+ * @param type 课程类型
|
|
|
|
|
+ * @returns 筛选后的课程树数据
|
|
|
|
|
+ */
|
|
|
|
|
+function filterCoursesByType(courses: any[], type: string): any[] {
|
|
|
|
|
+ // 深拷贝原始数据,避免修改原始数组
|
|
|
|
|
+ const filteredCourses = JSON.parse(JSON.stringify(courses))
|
|
|
|
|
+
|
|
|
|
|
+ // 递归处理树结构,只保留指定类型的节点及其父节点
|
|
|
|
|
+ function filterTreeNodes(nodes: any[]): any[] {
|
|
|
|
|
+ return nodes
|
|
|
|
|
+ .map(node => {
|
|
|
|
|
+ // 复制当前节点
|
|
|
|
|
+ const newNode = { ...node }
|
|
|
|
|
+
|
|
|
|
|
+ // 递归处理子节点
|
|
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
|
|
+ const filteredChildren = filterTreeNodes(node.children)
|
|
|
|
|
+ if (filteredChildren.length > 0) {
|
|
|
|
|
+ newNode.children = filteredChildren
|
|
|
|
|
+ return newNode
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果当前节点是指定类型,或者有符合条件的子节点,则保留
|
|
|
|
|
+ if (node.type === type) {
|
|
|
|
|
+ return newNode
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return null
|
|
|
|
|
+ })
|
|
|
|
|
+ .filter(node => node !== null)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return filterTreeNodes(filteredCourses)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 提交表单 */
|
|
|
|
|
+const emit = defineEmits(['success'])
|
|
|
|
|
+const submitForm = async () => {
|
|
|
|
|
+ formLoading.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const data: any = {
|
|
|
|
|
+ roleId: formData.id,
|
|
|
|
|
+ dataScopeWebRoute: formData.dataScopeWebRoute
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理课程权限
|
|
|
|
|
+ const generalCheckedKeys = generalTreeRef.value?.getCheckedKeys(false) || []
|
|
|
|
|
+ const practicalCheckedKeys = practicalTreeRef.value?.getCheckedKeys(false) || []
|
|
|
|
|
+ const allCheckedKeys = [...generalCheckedKeys, ...practicalCheckedKeys]
|
|
|
|
|
+ const allCourseOptions = [...courseTabs.generalCourseOptions, ...courseTabs.practicalCourseOptions]
|
|
|
|
|
+ // 课程类型的叶子节点类型是'通识课'或'实操课',所以不需要过滤叶子节点类型
|
|
|
|
|
+ data.dataScopeCourseIds = allCheckedKeys.filter(key => {
|
|
|
|
|
+ // 只保留实际存在于树结构中的节点ID
|
|
|
|
|
+ const nodeMap = new Map<number, boolean>()
|
|
|
|
|
+ function traverseTree(nodes: any[]) {
|
|
|
|
|
+ nodes.forEach(node => {
|
|
|
|
|
+ nodeMap.set(node.id, true)
|
|
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
|
|
+ traverseTree(node.children)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ traverseTree(allCourseOptions)
|
|
|
|
|
+ return nodeMap.has(key)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 处理AI实验课权限
|
|
|
|
|
+ const aiCourseCheckedKeys = aiCourseTreeRef.value?.getCheckedKeys(false) || []
|
|
|
|
|
+ data.dataScopeAiCourseIds = filterChildNodeKeys(aiCourseCheckedKeys, aiCourseTabs.generalAiCourseOptions, "aiCourseType")
|
|
|
|
|
+
|
|
|
|
|
+ // 处理blockly课程权限
|
|
|
|
|
+ const blocklyCheckedKeys = blocklyTreeRef.value?.getCheckedKeys(false) || []
|
|
|
|
|
+ data.dataScopeBlocklyIds = filterChildNodeKeys(blocklyCheckedKeys, blocklyTabs.generalBlocklyOptions, "blocklyType")
|
|
|
|
|
+
|
|
|
|
|
+ console.log('提交的数据:', data)
|
|
|
|
|
+ await PermissionApi.assignRoleWebScope(data)
|
|
|
|
|
+ message.success(t('common.updateSuccess'))
|
|
|
|
|
+ dialogVisible.value = false
|
|
|
|
|
+ // 发送操作成功的事件
|
|
|
|
|
+ emit('success')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ formLoading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 过滤只包含子节点的ID
|
|
|
|
|
+ * @param checkedKeys 所有选中的节点ID
|
|
|
|
|
+ * @param treeData 树结构数据
|
|
|
|
|
+ * @param leafType 叶子节点类型
|
|
|
|
|
+ * @returns 只包含子节点的ID数组
|
|
|
|
|
+ */
|
|
|
|
|
+function filterChildNodeKeys(checkedKeys: number[], treeData: any[], leafType: string): number[] {
|
|
|
|
|
+ // 创建一个Map存储所有节点ID及其是否为叶子节点的状态
|
|
|
|
|
+ const nodeMap = new Map<number, boolean>()
|
|
|
|
|
+
|
|
|
|
|
+ // 递归遍历树结构,标记每个节点是否为叶子节点
|
|
|
|
|
+ function traverseTree(nodes: any[]) {
|
|
|
|
|
+ nodes.forEach(node => {
|
|
|
|
|
+ nodeMap.set(node.id, node.type === leafType)
|
|
|
|
|
+
|
|
|
|
|
+ // 递归处理子节点
|
|
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
|
|
+ traverseTree(node.children)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 执行遍历
|
|
|
|
|
+ traverseTree(treeData)
|
|
|
|
|
+
|
|
|
|
|
+ // 过滤出只包含叶子节点的ID
|
|
|
|
|
+ return checkedKeys.filter(key => {
|
|
|
|
|
+ // 如果节点存在于nodeMap中且是叶子节点,则保留
|
|
|
|
|
+ return nodeMap.has(key) && nodeMap.get(key)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 重置表单 */
|
|
|
|
|
+const resetForm = () => {
|
|
|
|
|
+
|
|
|
|
|
+ // 重置课程权限选项
|
|
|
|
|
+ courseTabs.generalCourseOptions = []
|
|
|
|
|
+ courseTabs.generalDeptExpand = true
|
|
|
|
|
+ courseTabs.generalTreeNodeAll = false
|
|
|
|
|
+ courseTabs.generalCheckStrictly = true
|
|
|
|
|
+ generalTreeRef.value?.setCheckedNodes([])
|
|
|
|
|
+
|
|
|
|
|
+ courseTabs.practicalCourseOptions = []
|
|
|
|
|
+ courseTabs.practicalDeptExpand = true
|
|
|
|
|
+ courseTabs.practicalTreeNodeAll = false
|
|
|
|
|
+ courseTabs.practicalCheckStrictly = true
|
|
|
|
|
+ practicalTreeRef.value?.setCheckedNodes([])
|
|
|
|
|
+
|
|
|
|
|
+ // 重置AI实验课选项
|
|
|
|
|
+ aiCourseTabs.generalAiCourseOptions = []
|
|
|
|
|
+ aiCourseTabs.generalDeptExpand = true
|
|
|
|
|
+ aiCourseTabs.generalTreeNodeAll = false
|
|
|
|
|
+ aiCourseTabs.generalCheckStrictly = true
|
|
|
|
|
+ aiCourseTreeRef.value?.setCheckedNodes([])
|
|
|
|
|
+
|
|
|
|
|
+ // 重置blockly课程选项
|
|
|
|
|
+ blocklyTabs.generalBlocklyOptions = []
|
|
|
|
|
+ blocklyTabs.generalDeptExpand = true
|
|
|
|
|
+ blocklyTabs.generalTreeNodeAll = false
|
|
|
|
|
+ blocklyTabs.generalCheckStrictly = true
|
|
|
|
|
+ blocklyTreeRef.value?.setCheckedNodes([])
|
|
|
|
|
+
|
|
|
|
|
+ // 重置表单数据
|
|
|
|
|
+ formData.id = undefined
|
|
|
|
|
+ formData.name = ''
|
|
|
|
|
+ formData.code = ''
|
|
|
|
|
+ formData.dataScopeCourseIds = []
|
|
|
|
|
+ formData.dataScopeAiCourseIds = []
|
|
|
|
|
+ formData.dataScopeBlocklyIds = []
|
|
|
|
|
+ formData.dataScopeWebRoute = []
|
|
|
|
|
+
|
|
|
|
|
+ formRef.value?.resetFields()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 处理全选/全不选 */
|
|
|
|
|
+function handleCheckedTreeNodeAll(tabType: string, treeType: string) {
|
|
|
|
|
+ if (tabType === 'course') {
|
|
|
|
|
+ if (treeType === 'general') {
|
|
|
|
|
+ generalTreeRef.value?.setCheckedNodes(courseTabs.generalTreeNodeAll ? courseTabs.generalCourseOptions : [])
|
|
|
|
|
+ } else if (treeType === 'practical') {
|
|
|
|
|
+ practicalTreeRef.value?.setCheckedNodes(courseTabs.practicalTreeNodeAll ? courseTabs.practicalCourseOptions : [])
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (tabType === 'aiCourse') {
|
|
|
|
|
+ aiCourseTreeRef.value?.setCheckedNodes(aiCourseTabs.generalTreeNodeAll ? aiCourseTabs.generalAiCourseOptions : [])
|
|
|
|
|
+ } else if (tabType === 'blockly') {
|
|
|
|
|
+ blocklyTreeRef.value?.setCheckedNodes(blocklyTabs.generalTreeNodeAll ? blocklyTabs.generalBlocklyOptions : [])
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 处理展开/折叠全部 */
|
|
|
|
|
+function handleCheckedTreeExpand(tabType: string, treeType: string) {
|
|
|
|
|
+ if (tabType === 'course') {
|
|
|
|
|
+ const treeRef = treeType === 'general' ? generalTreeRef.value : practicalTreeRef.value
|
|
|
|
|
+ const expandValue = treeType === 'general' ? courseTabs.generalDeptExpand : courseTabs.practicalDeptExpand
|
|
|
|
|
+ expandTreeNodes(treeRef, expandValue)
|
|
|
|
|
+ } else if (tabType === 'aiCourse') {
|
|
|
|
|
+ expandTreeNodes(aiCourseTreeRef.value, aiCourseTabs.generalDeptExpand)
|
|
|
|
|
+ } else if (tabType === 'blockly') {
|
|
|
|
|
+ expandTreeNodes(blocklyTreeRef.value, blocklyTabs.generalDeptExpand)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 展开/折叠树节点 */
|
|
|
|
|
+function expandTreeNodes(treeRef: any, expand: boolean) {
|
|
|
|
|
+ const nodes = treeRef?.store.nodesMap
|
|
|
|
|
+ if (nodes) {
|
|
|
|
|
+ for (let node in nodes) {
|
|
|
|
|
+ if (nodes[node].expanded === expand) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ nodes[node].expanded = expand
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|