diff --git a/admin/src/app/api/auth.ts b/admin/src/app/api/auth.ts index 9d53a76e..5db065f7 100644 --- a/admin/src/app/api/auth.ts +++ b/admin/src/app/api/auth.ts @@ -45,3 +45,27 @@ export function getLoginConfig() { export function getVersions() { return request.get(`sys/info`) } + +/** + * 获取UniApp菜单权限列表 + */ +export function getUniappMenus() { + return request.get('uniapp_auth/menus') +} + +/** + * 获取角色的UniApp菜单权限 + * @param roleId 角色ID + */ +export function getRoleUniappMenus(roleId: number) { + return request.get(`uniapp_auth/role_menus/${roleId}`) +} + +/** + * 设置角色的UniApp菜单权限 + * @param roleId 角色ID + * @param menuIds 菜单ID数组 + */ +export function setRoleUniappMenus(roleId: number, menuIds: number[]) { + return request.post(`uniapp_auth/set_role_menus/${roleId}`, { menu_ids: menuIds }) +} diff --git a/admin/src/app/views/auth/components/edit-role.vue b/admin/src/app/views/auth/components/edit-role.vue index f43e6e80..51b0671c 100644 --- a/admin/src/app/views/auth/components/edit-role.vue +++ b/admin/src/app/views/auth/components/edit-role.vue @@ -141,7 +141,7 @@ import { getWithDepartmentsList} from '@/app/api/departments' import type {FormInstance} from 'element-plus' import {addRole, editRole, getRoleInfo} from '@/app/api/sys' -import {getAuthMenus} from '@/app/api/auth' +import {getAuthMenus, getUniappMenus, getRoleUniappMenus, setRoleUniappMenus} from '@/app/api/auth' import {useDictionary} from '@/app/api/dict' import {debounce} from '@/utils/common' @@ -163,16 +163,9 @@ getAuthMenus({is_button: 0}).then((res) => { // 获取移动端菜单(UniApp菜单权限) const getMobileMenus = async () => { try { - // 使用专门的UniApp权限接口 - const response = await fetch('http://localhost:20080/adminapi/uniapp_auth/menus', { - headers: { - 'Content-Type': 'application/json', - 'token': localStorage.getItem('token') || '' - } - }) - const result = await response.json() - if (result.code === 1) { - mobileMenus.value = result.data.map(item => ({ + const res = await getUniappMenus() + if (res.data) { + mobileMenus.value = res.data.map(item => ({ menu_key: item.menu_key, menu_name: item.menu_name, id: item.id, @@ -195,10 +188,12 @@ const selectAllPC = ref(false) const checkStrictlyPC = ref(false) const treeRefPC: Record | null = ref(null) watch(selectAllPC, () => { - if (selectAllPC.value) { - treeRefPC.value.setCheckedNodes(toRaw(menus.value)) - } else { - treeRefPC.value.setCheckedNodes([]) + if (treeRefPC.value) { + if (selectAllPC.value) { + treeRefPC.value.setCheckedNodes(toRaw(menus.value)) + } else { + treeRefPC.value.setCheckedNodes([]) + } } }) @@ -207,10 +202,12 @@ const selectAllMobile = ref(false) const checkStrictlyMobile = ref(false) const treeRefMobile: Record | null = ref(null) watch(selectAllMobile, () => { - if (selectAllMobile.value) { - treeRefMobile.value.setCheckedNodes(toRaw(mobileMenus.value)) - } else { - treeRefMobile.value.setCheckedNodes([]) + if (treeRefMobile.value) { + if (selectAllMobile.value) { + treeRefMobile.value.setCheckedNodes(toRaw(mobileMenus.value)) + } else { + treeRefMobile.value.setCheckedNodes([]) + } } }) @@ -230,7 +227,9 @@ getRolekeyDictList() // PC端权限处理函数 const handleCheckChangePC = debounce((e) => { - formData.rules = treeRefPC.value.getCheckedKeys() + if (treeRefPC.value) { + formData.rules = treeRefPC.value.getCheckedKeys() + } }) const menuActionPC = () => { @@ -245,7 +244,9 @@ const menuActionPC = () => { // 移动端权限处理函数 const handleCheckChangeMobile = debounce((e) => { - formData.mobile_rules = treeRefMobile.value.getCheckedKeys() + if (treeRefMobile.value) { + formData.mobile_rules = treeRefMobile.value.getCheckedKeys() + } }) const menuActionMobile = () => { @@ -260,36 +261,52 @@ const menuActionMobile = () => { // PC端全部展开 const unFoldAllPC = (data: any) => { - Object.keys(data).forEach((key: string | any) => { - treeRefPC.value.store.nodesMap[data[key].menu_key].expanded = true - if (data[key].children && data[key].children.length > 0) - unFoldAllPC(data[key].children) - }) + if (treeRefPC.value && treeRefPC.value.store) { + Object.keys(data).forEach((key: string | any) => { + if (treeRefPC.value.store.nodesMap[data[key].menu_key]) { + treeRefPC.value.store.nodesMap[data[key].menu_key].expanded = true + } + if (data[key].children && data[key].children.length > 0) + unFoldAllPC(data[key].children) + }) + } } // PC端全部折叠 const collapseAllPC = (data: any) => { - Object.keys(data).forEach((key: string | any) => { - treeRefPC.value.store.nodesMap[data[key].menu_key].expanded = false - if (data[key].children && data[key].children.length > 0) - collapseAllPC(data[key].children) - }) + if (treeRefPC.value && treeRefPC.value.store) { + Object.keys(data).forEach((key: string | any) => { + if (treeRefPC.value.store.nodesMap[data[key].menu_key]) { + treeRefPC.value.store.nodesMap[data[key].menu_key].expanded = false + } + if (data[key].children && data[key].children.length > 0) + collapseAllPC(data[key].children) + }) + } } // 移动端全部展开 const unFoldAllMobile = (data: any) => { - Object.keys(data).forEach((key: string | any) => { - treeRefMobile.value.store.nodesMap[data[key].menu_key].expanded = true - if (data[key].children && data[key].children.length > 0) - unFoldAllMobile(data[key].children) - }) + if (treeRefMobile.value && treeRefMobile.value.store) { + Object.keys(data).forEach((key: string | any) => { + if (treeRefMobile.value.store.nodesMap[data[key].menu_key]) { + treeRefMobile.value.store.nodesMap[data[key].menu_key].expanded = true + } + if (data[key].children && data[key].children.length > 0) + unFoldAllMobile(data[key].children) + }) + } } // 移动端全部折叠 const collapseAllMobile = (data: any) => { - Object.keys(data).forEach((key: string | any) => { - treeRefMobile.value.store.nodesMap[data[key].menu_key].expanded = false - if (data[key].children && data[key].children.length > 0) - collapseAllMobile(data[key].children) - }) + if (treeRefMobile.value && treeRefMobile.value.store) { + Object.keys(data).forEach((key: string | any) => { + if (treeRefMobile.value.store.nodesMap[data[key].menu_key]) { + treeRefMobile.value.store.nodesMap[data[key].menu_key].expanded = false + } + if (data[key].children && data[key].children.length > 0) + collapseAllMobile(data[key].children) + }) + } } /** * 表单数据 @@ -342,7 +359,9 @@ const confirm = async (formEl: FormInstance | undefined) => { const data = Object.assign({}, formData) // PC端权限处理 - data.rules = data.rules.concat(treeRefPC.value.getHalfCheckedKeys()) + if (treeRefPC.value) { + data.rules = data.rules.concat(treeRefPC.value.getHalfCheckedKeys()) + } // 移动端权限处理 if (treeRefMobile.value) { @@ -351,20 +370,7 @@ const confirm = async (formEl: FormInstance | undefined) => { // 调用UniApp权限设置API if (data.role_id && data.mobile_rules.length >= 0) { try { - const mobileResponse = await fetch(`http://localhost:20080/adminapi/uniapp_auth/set_role_menus/${data.role_id}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'token': localStorage.getItem('token') || '' - }, - body: JSON.stringify({ - menu_ids: data.mobile_rules - }) - }) - const mobileResult = await mobileResponse.json() - if (mobileResult.code !== 1) { - console.error('移动端权限设置失败:', mobileResult.msg) - } + await setRoleUniappMenus(data.role_id, data.mobile_rules) } catch (error) { console.error('设置移动端权限失败:', error) } @@ -399,15 +405,9 @@ const setFormData = async (row: any = null) => { // 获取移动端权限数据 let mobileRulesData = [] try { - const mobileResponse = await fetch(`http://localhost:20080/adminapi/uniapp_auth/role_menus/${row.role_id}`, { - headers: { - 'Content-Type': 'application/json', - 'token': localStorage.getItem('token') || '' - } - }) - const mobileResult = await mobileResponse.json() - if (mobileResult.code === 1) { - mobileRulesData = mobileResult.data.map(item => item.menu_key) + const mobileRes = await getRoleUniappMenus(row.role_id) + if (mobileRes.data) { + mobileRulesData = mobileRes.data.map(item => item.menu_key) } } catch (error) { console.error('获取移动端权限失败:', error) diff --git a/admin/yarn.lock b/admin/yarn.lock index 78f03853..b2d349fe 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -226,10 +226,10 @@ resolved "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.10.tgz" integrity sha512-ygEZ1mwPjcPo/OulhzLE7mtDrQBWI8vZzEWSNB2W/RNCRjoQGwbaK4N8lV4rid7Ts4qvySU3njMN7YCiSlSaTQ== -"@esbuild/linux-x64@0.16.17": +"@esbuild/darwin-x64@0.16.17": version "0.16.17" - resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz" - integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw== + resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz" + integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg== "@eslint/eslintrc@^1.4.1": version "1.4.1" @@ -2040,6 +2040,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz" diff --git a/niucloud/app/service/admin/personnel/PersonnelService.php b/niucloud/app/service/admin/personnel/PersonnelService.php index 9107de5c..0a40c4ff 100644 --- a/niucloud/app/service/admin/personnel/PersonnelService.php +++ b/niucloud/app/service/admin/personnel/PersonnelService.php @@ -49,7 +49,7 @@ class PersonnelService extends BaseAdminService $search_model = $this->model->withSearch(["name", "gender", "phone", "address", "education", "employee_number", "status", "create_time"], $where)->field($field)->order($order); $list = $this->pageQuery($search_model, function ($item) { $CampusPersonRole = new CampusPersonRole(); - $item['is_role'] = 1; + $item['is_role'] = 1; // 始终允许角色设置 $role = $CampusPersonRole->where(['person_id' => $item['id']])->find(); if($role){ $item['role_data'] = [ @@ -57,9 +57,10 @@ class PersonnelService extends BaseAdminService 'campus_id' => $role['campus_id'], 'person_id' => $item['id'], 'role_id' => $role['role_id'], - 'dept_id' => 0 + 'dept_id' => $role['dept_id'] ]; - $item['is_role'] = $role['dept_id'] == 0 ? 1 : 0; + // 移除dept_id限制,始终允许角色设置 + $item['is_role'] = 1; }else{ $item['role_data'] = [ 'id' => '', diff --git a/niucloud/app/service/admin/sys/MenuService.php b/niucloud/app/service/admin/sys/MenuService.php index 2b701ebb..4afc0c05 100644 --- a/niucloud/app/service/admin/sys/MenuService.php +++ b/niucloud/app/service/admin/sys/MenuService.php @@ -159,6 +159,7 @@ class MenuService extends BaseAdminService function() use ($menu_keys, $is_tree, $addon) { $where = [ [ 'menu_key', 'in', $menu_keys ], + [ 'app_type', '=', 'admin' ] // 只获取管理端菜单,排除UniApp菜单 ]; $addons = get_site_addons(); $addons[] = ''; @@ -187,9 +188,6 @@ class MenuService extends BaseAdminService $where[] = [ 'menu_key', 'not in', $delete_intersect ]; } } - if (!empty($app_type)) { - $where[] = [ 'app_type', '=', $app_type ]; - } return ( new SysMenu() )->where($where)->order('sort', 'desc')->select()->toArray(); }, self::$cache_tag_name @@ -218,7 +216,8 @@ class MenuService extends BaseAdminService $cache_name, function() use ($status, $is_tree, $is_button) { $where = [ - [ 'addon', 'in', array_merge([ '' ], get_site_addons()) ] + [ 'addon', 'in', array_merge([ '' ], get_site_addons()) ], + [ 'app_type', '=', 'admin' ] // 只获取管理端菜单,排除UniApp菜单 ]; if ($status != 'all') { $where[] = [ 'status', '=', $status ]; diff --git a/niucloud/app/service/admin/uniapp/UniappAuthService.php b/niucloud/app/service/admin/uniapp/UniappAuthService.php index 85ae5191..18eb21e4 100644 --- a/niucloud/app/service/admin/uniapp/UniappAuthService.php +++ b/niucloud/app/service/admin/uniapp/UniappAuthService.php @@ -26,19 +26,22 @@ class UniappAuthService extends BaseAdminService */ public function getAllMenus() { - $sql = "SELECT id, menu_key, menu_name, menu_icon, menu_path, menu_params, sort_order, status, description - FROM sys_menus - WHERE status = 1 - ORDER BY sort_order ASC, id ASC"; + // 从UniApp员工端功能页面整理文档获取菜单数据,基于school_sys_menu表结构 + $sql = "SELECT id, menu_key, menu_name, icon as menu_icon, router_path as menu_path, + '' as menu_params, sort, status, menu_short_name as description + FROM school_sys_menu + WHERE status = 1 AND app_type = 'uniapp' + ORDER BY sort ASC, id ASC"; $list = Db::query($sql); - // 处理参数 + // 处理参数和数据转换 foreach ($list as &$item) { - if (!empty($item['menu_params'])) { - $item['menu_params'] = json_decode($item['menu_params'], true) ?? []; - } else { - $item['menu_params'] = []; - } + // 设置空的menu_params + $item['menu_params'] = []; + // 确保字段存在 + $item['menu_icon'] = $item['menu_icon'] ?? ''; + $item['menu_path'] = $item['menu_path'] ?? ''; + $item['sort_order'] = $item['sort']; } return $list; @@ -51,20 +54,21 @@ class UniappAuthService extends BaseAdminService */ public function getRoleMenus(int $roleId) { - $sql = "SELECT m.id, m.menu_key, m.menu_name, m.menu_icon, m.menu_path, m.menu_params, m.sort_order + $sql = "SELECT m.id, m.menu_key, m.menu_name, m.icon as menu_icon, + m.router_path as menu_path, '' as menu_params, m.sort as sort_order FROM role_menu_permissions rmp - LEFT JOIN sys_menus m ON rmp.menu_id = m.id + LEFT JOIN school_sys_menu m ON rmp.menu_id = m.id WHERE rmp.role_id = ? AND rmp.is_enabled = 1 - ORDER BY m.sort_order ASC"; + ORDER BY m.sort ASC"; $menuList = Db::query($sql, [$roleId]); - // 处理参数 + // 处理参数和数据转换 foreach ($menuList as &$item) { - if (!empty($item['menu_params'])) { - $item['menu_params'] = json_decode($item['menu_params'], true) ?? []; - } else { - $item['menu_params'] = []; - } + // 设置空的menu_params + $item['menu_params'] = []; + // 确保字段存在 + $item['menu_icon'] = $item['menu_icon'] ?? ''; + $item['menu_path'] = $item['menu_path'] ?? ''; } return $menuList; @@ -73,10 +77,10 @@ class UniappAuthService extends BaseAdminService /** * 设置角色的UniApp菜单权限 * @param int $roleId - * @param array $menuIds + * @param array $menuKeys 菜单键名数组(如:pages-common/my_attendance) * @return bool */ - public function setRoleMenus(int $roleId, array $menuIds) + public function setRoleMenus(int $roleId, array $menuKeys) { Db::startTrans(); try { @@ -84,16 +88,35 @@ class UniappAuthService extends BaseAdminService Db::execute("DELETE FROM role_menu_permissions WHERE role_id = ?", [$roleId]); // 添加新权限 - if (!empty($menuIds)) { - $values = []; - $params = []; - foreach ($menuIds as $menuId) { - $values[] = "(?, ?, 1, NOW(), NOW())"; - $params[] = $roleId; - $params[] = $menuId; + if (!empty($menuKeys)) { + // 将menu_key转换为对应的数据库ID + $menuIds = []; + $sql = "SELECT id, menu_key FROM school_sys_menu WHERE app_type = 'uniapp' AND status = 1"; + $menuList = Db::query($sql); + $menuKeyToId = []; + foreach ($menuList as $menu) { + $menuKeyToId[$menu['menu_key']] = $menu['id']; + } + + // 过滤出有效的菜单ID + foreach ($menuKeys as $menuKey) { + if (isset($menuKeyToId[$menuKey])) { + $menuIds[] = $menuKeyToId[$menuKey]; + } + } + + // 插入新的权限数据 + if (!empty($menuIds)) { + $values = []; + $params = []; + foreach ($menuIds as $menuId) { + $values[] = "(?, ?, 1, NOW(), NOW())"; + $params[] = $roleId; + $params[] = $menuId; + } + $sql = "INSERT INTO role_menu_permissions (role_id, menu_id, is_enabled, created_at, updated_at) VALUES " . implode(', ', $values); + Db::execute($sql, $params); } - $sql = "INSERT INTO role_menu_permissions (role_id, menu_id, is_enabled, created_at, updated_at) VALUES " . implode(', ', $values); - Db::execute($sql, $params); } Db::commit(); diff --git a/uniapp/pages/common/home/index.vue b/uniapp/pages/common/home/index.vue index dd2d0332..ec82cac4 100644 --- a/uniapp/pages/common/home/index.vue +++ b/uniapp/pages/common/home/index.vue @@ -26,7 +26,7 @@ @@ -45,18 +45,81 @@ data() { return { userInfo: {}, - gridItems: [], - defaultGridItems: [ + userPermissions: [], // 用户权限列表 + // 固定定义所有可能的按钮,根据权限控制显示/隐藏 + allGridItems: [ + { + title: '客户资源', + icon: 'person-filled', + path: '/pages/market/clue/index' + }, + { + title: '添加资源', + icon: 'plus-filled', + path: '/pages/market/clue/add_clues' + }, + { + title: '数据统计', + icon: 'bars', + path: '/pages/market/data/statistics' + }, + { + title: '个人中心', + icon: 'person', + path: '/pages/market/my/index' + }, + { + title: '首页', + icon: 'home', + path: '/pages/market/home/index' + }, + { + title: '课程安排', + icon: 'calendar-filled', + path: '/pages-market/clue/class_arrangement' + }, + { + title: '学员管理', + icon: 'person', + path: '/pages-coach/coach/student/student_list' + }, + { + title: '课程查询', + icon: 'list', + path: '/pages-coach/coach/schedule/schedule_table' + }, { title: '我的数据', icon: 'bars', path: '/pages/common/dashboard/webview', params: { type: 'my_data' } }, + { + title: '部门数据', + icon: 'home', + path: '/pages/common/dashboard/webview', + params: { type: 'dept_data' } + }, + { + title: '校区数据', + icon: 'location', + path: '/pages/common/dashboard/webview', + params: { type: 'campus_data' } + }, { title: '我的消息', icon: 'chat-filled', - path: '/pages-common/my_message' + path: '/pages/common/my/my_message' + }, + { + title: '报销管理', + icon: 'wallet-filled', + path: '/pages-market/reimbursement/list' + }, + { + title: '资料库', + icon: 'folder-add', + path: '/pages-coach/coach/my/teaching_management' } ] } @@ -69,6 +132,14 @@ // 每次显示页面时重新加载,确保菜单数据最新 this.loadMenuList(); }, + computed: { + // 根据权限过滤可见的按钮 + visibleGridItems() { + return this.allGridItems.filter(item => { + return this.userPermissions.includes(item.path); + }); + } + }, methods: { loadUserInfo() { // 从本地存储获取用户信息 @@ -83,13 +154,15 @@ console.log('从本地存储获取的菜单列表:', menuList); if (menuList && Array.isArray(menuList) && menuList.length > 0) { - // 使用登录时返回的动态菜单 - this.gridItems = menuList; + // 提取权限path列表 + this.userPermissions = menuList.map(item => item.path); } else { - // 使用默认菜单作为兜底 - this.gridItems = this.defaultGridItems; - console.log('使用默认菜单'); + // 默认权限(基础功能) + this.userPermissions = ['/pages/common/dashboard/webview', '/pages-common/my_message']; + console.log('使用默认权限'); } + + console.log('用户权限路径:', this.userPermissions); }, handleGridClick(item) { console.log('点击功能按钮:', item.title, item.path);