瀏覽代碼

角色管理:新增web课程菜单路由权限配置

liyanbo 3 周之前
父節點
當前提交
bf47e4081b

+ 11 - 0
src/api/system/permission/index.ts

@@ -38,6 +38,11 @@ export interface PermissionAssignRoleWebScopeReqVO {
   dataScopeBlocklyIds: number[]
   dataScopeAiCourseIds: number[]
 }
+export interface PermissionAssignRoleWebMenuScopeReqVO {
+  roleId: number
+  dataScopeWebRoute: String[]
+  dataScopeWebMenuRoute: String[]
+}
 
 // 查询角色拥有的菜单权限
 export const getRoleMenuList = async (roleId: number) => {
@@ -74,6 +79,12 @@ export const assignRoleWebScope = async (data: PermissionAssignRoleWebScopeReqVO
   return await request.post({ url: '/system/permission/assign-role-web-scope', data })
 }
 
+// 赋予角色Web菜单权限
+export const assignRoleWebMenuScope = async (data: PermissionAssignRoleWebMenuScopeReqVO) => {
+  return await request.post({ url: '/system/permission/assign-role-web-menu-scope', data })
+}
+
+
 // 查询用户拥有的角色数组
 export const getUserRoleList = async (userId: number) => {
   return await request.get({ url: '/system/permission/list-user-roles?userId=' + userId })

+ 1 - 0
src/api/system/role/index.ts

@@ -13,6 +13,7 @@ export interface RoleVO {
   dataScopeBlocklyIds: number[]
   dataScopeAiCourseIds: number[]
   dataScopeWebRoute: String[]
+  dataScopeWebCourseMenuRoute: String[]
   createTime: Date
 }
 

+ 16 - 0
src/types/auto-components.d.ts

@@ -45,13 +45,19 @@ declare module 'vue' {
     Draggable: typeof import('./../components/Draggable/index.vue')['default']
     Echart: typeof import('./../components/Echart/src/Echart.vue')['default']
     Editor: typeof import('./../components/Editor/src/Editor.vue')['default']
+    ElAside: typeof import('element-plus/es')['ElAside']
+    ElAutoResizer: typeof import('element-plus/es')['ElAutoResizer']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCard: typeof import('element-plus/es')['ElCard']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
+    ElCheckboxButton: typeof import('element-plus/es')['ElCheckboxButton']
     ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
     ElCol: typeof import('element-plus/es')['ElCol']
+    ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition']
+    ElContainer: typeof import('element-plus/es')['ElContainer']
+    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
     ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
     ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
     ElDialog: typeof import('element-plus/es')['ElDialog']
@@ -68,14 +74,18 @@ declare module 'vue' {
     ElementOtherConfig: typeof import('./../components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue')['default']
     ElementProperties: typeof import('./../components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue')['default']
     ElementTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/ElementTask.vue')['default']
+    ElEmpty: typeof import('element-plus/es')['ElEmpty']
+    ElFooter: typeof import('element-plus/es')['ElFooter']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
+    ElHeader: typeof import('element-plus/es')['ElHeader']
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElImage: typeof import('element-plus/es')['ElImage']
     ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
     ElLink: typeof import('element-plus/es')['ElLink']
+    ElMain: typeof import('element-plus/es')['ElMain']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
     ElPopover: typeof import('element-plus/es')['ElPopover']
@@ -88,12 +98,18 @@ declare module 'vue' {
     ElSegmented: typeof import('element-plus/es')['ElSegmented']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
+    ElSlider: typeof import('element-plus/es')['ElSlider']
+    ElSpace: typeof import('element-plus/es')['ElSpace']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTableV2: typeof import('element-plus/es')['ElTableV2']
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
+    ElText: typeof import('element-plus/es')['ElText']
+    ElTimeline: typeof import('element-plus/es')['ElTimeline']
+    ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
     ElTree: typeof import('element-plus/es')['ElTree']
     ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']

+ 13 - 13
src/views/bjdx/coursetype/index.vue

@@ -138,7 +138,7 @@
   <!-- 表单弹窗:添加/修改 -->
   <CourseTypeForm ref="formRef" @success="getList" />
   <!-- AI生成弹窗 -->
-  <AiGenerate ref="aiGenerateRef" :visible="aiGenerateVisible" @update:visible="(value) => aiGenerateVisible = value" @save="handleAiGenerateSave" />
+  <!-- <AiGenerate ref="aiGenerateRef" :visible="aiGenerateVisible" @update:visible="(value) => aiGenerateVisible = value" @save="handleAiGenerateSave" /> -->
 </template>
 
 <script setup lang="ts">
@@ -149,7 +149,7 @@ import CourseTypeForm from './CourseTypeForm.vue'
 import { ElButton } from 'element-plus'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 import { getTenantId } from '@/utils/auth'
-import AiGenerate from './aiGenerate/aiGenerate.vue'
+// import AiGenerate from './aiGenerate/aiGenerate.vue'
 
 /** 课程-类型 列表 */
 defineOptions({ name: 'CourseType' })
@@ -168,8 +168,8 @@ const queryParams = reactive({
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
 // AI生成相关
-const aiGenerateVisible = ref(false)
-const aiGenerateRef = ref()
+// const aiGenerateVisible = ref(false)
+// const aiGenerateRef = ref()
 
 /** 查询列表 */
 const getList = async () => {
@@ -239,17 +239,17 @@ const toggleExpandAll = async () => {
 }
 
 /** 打开AI生成弹窗 */
-const openAiGenerate = () => {
-  aiGenerateVisible.value = true
-}
+// const openAiGenerate = () => {
+//   aiGenerateVisible.value = true
+// }
 
 /** 处理AI生成保存 */
-const handleAiGenerateSave = (scriptData, replacedUrls) => {
-  // 这里可以添加保存逻辑
-  console.log('AI生成保存', scriptData, replacedUrls)
-  // 保存成功后刷新列表
-  getList()
-}
+// const handleAiGenerateSave = (scriptData, replacedUrls) => {
+//   // 这里可以添加保存逻辑
+//   console.log('AI生成保存', scriptData, replacedUrls)
+//   // 保存成功后刷新列表
+//   getList()
+// }
 
 /** 初始化 **/
 onMounted(() => {

+ 144 - 0
src/views/system/role/RoleWebMenuPermissionForm.vue

@@ -0,0 +1,144 @@
+<template>
+  <Dialog v-model="dialogVisible" title="web端角色菜单权限配置" width="800">
+    <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>
+
+      <!-- Web菜单权限 -->
+      <el-form-item label="Web菜单权限">
+        <el-select
+          v-model="formData.dataScopeWebMenuRoute"
+          class="!w-full"
+          clearable
+          multiple
+          filterable
+          placeholder="请选择Web菜单权限"
+          value-key="value"
+          collapse-tags-tooltip
+        >
+          <template #header>
+            <el-checkbox v-model="selectAll" @change="handleSelectAll">全选</el-checkbox>
+          </template>
+          <el-option
+            v-for="dict in webMenuRouteOptions"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+    </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 { DICT_TYPE, getDictOptions } from '@/utils/dict'
+import * as RoleApi from '@/api/system/role'
+import * as PermissionApi from '@/api/system/permission'
+
+defineOptions({ name: 'SystemRoleWebMenuPermissionForm' })
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const routeTypeOptions = ref(getDictOptions(DICT_TYPE.WEB_ROLE_ROUTE)) // 路由类型字典选项
+const webMenuRouteOptions = ref(getDictOptions('web_course_menu_route')) // Web菜单权限字典选项
+const selectAll = ref(false) // 全选状态
+const formData = reactive({
+  id: undefined,
+  name: '',
+  code: '',
+  dataScopeWebRoute: [],
+  dataScopeWebMenuRoute: []
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+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.dataScopeWebMenuRoute = row.dataScopeWebCourseMenuRoute || []
+}
+
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success'])
+const submitForm = async () => {
+  formLoading.value = true
+  try {
+    const data: any = {
+      roleId: formData.id,
+      dataScopeWebRoute: formData.dataScopeWebRoute,
+      dataScopeWebMenuRoute: formData.dataScopeWebMenuRoute
+    }
+
+    console.log('提交的数据:', data)
+    await PermissionApi.assignRoleWebMenuScope(data)
+    message.success(t('common.updateSuccess'))
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  // 重置表单数据
+  formData.id = undefined
+  formData.name = ''
+  formData.code = ''
+  formData.dataScopeWebRoute = []
+  formData.dataScopeWebMenuRoute = []
+  selectAll.value = false
+
+  formRef.value?.resetFields()
+}
+
+/** 处理全选 */
+const handleSelectAll = (val: boolean) => {
+  if (val) {
+    // 全选:将所有选项的 value 添加到 dataScopeWebMenuRoute
+    formData.dataScopeWebMenuRoute = webMenuRouteOptions.value.map(option => option.value)
+  } else {
+    // 取消全选:清空 dataScopeWebMenuRoute
+    formData.dataScopeWebMenuRoute = []
+  }
+}
+</script>

+ 19 - 0
src/views/system/role/index.vue

@@ -155,6 +155,16 @@
           >
             Web权限
           </el-button>
+          <el-button
+            v-hasPermi="['system:permission:assign-role-data-scope']"
+            link
+            preIcon="ep:coin"
+            title="Web菜单权限"
+            type="primary"
+            @click="openWebMenuPermissionForm(scope.row)"
+          >
+            Web菜单权限
+          </el-button>
           <el-button
             v-hasPermi="['system:permission:assign-role-data-scope']"
             link
@@ -203,6 +213,8 @@
   <RoleDataPermissionForm ref="dataPermissionFormRef" @success="getList" />
   <!-- 表单弹窗:Web权限 -->
   <RoleWebPermissionForm ref="webPermissionFormRef" @success="getList" />
+  <!-- 表单弹窗:Web菜单权限 -->
+  <RoleWebMenuPermissionForm ref="webMenuPermissionFormRef" @success="getList" />
   <!-- 表单弹窗:课程权限 -->
   <RoleCoursePermissionForm ref="coursePermissionFormRef" @success="getList" />
   <!-- 表单弹窗:blockly课程权限 -->
@@ -222,6 +234,7 @@ import RoleCoursePermissionForm from './RoleCoursePermissionForm.vue'
 import RoleBlocklyPermissionForm from './RoleBlocklyPermissionForm.vue'
 import RoleAiCoursePermissionForm from './RoleAiCoursePermissionForm.vue'
 import RoleWebPermissionForm from './RoleWebPermissionForm.vue'
+import RoleWebMenuPermissionForm from './RoleWebMenuPermissionForm.vue'
 
 
 defineOptions({ name: 'SystemRole' })
@@ -285,6 +298,12 @@ const openWebPermissionForm = async (row: RoleApi.RoleVO) => {
   webPermissionFormRef.value.open(row)
 }
 
+/** Web菜单权限操作 */
+const webMenuPermissionFormRef = ref()
+const openWebMenuPermissionForm = async (row: RoleApi.RoleVO) => {
+  webMenuPermissionFormRef.value.open(row)
+}
+
 /** 课程权限操作 */
 const coursePermissionFormRef = ref()
 const openCoursePermissionForm = async (row: RoleApi.RoleVO) => {