param(['title' => '']); $menus = SysMenuModel::field([ 'id', 'pid', 'title' ]) ->with(['menu_ability_permission']) ->withSearch(['title'], [ 'title' => $params['title'] ?? null ]) ->enabled() ->order(['sort' => 'asc', 'id' => 'desc']) ->select() ->hidden(['menu_ability_permission.menu_id']); return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid')); } // 分页数据 public function index() { $params = request()->param(['title' => '']); $menus = SysMenuModel::field([ 'id', 'pid', 'icon', 'title', 'path', 'component', 'sort', 'hidden', 'status' ]) ->withSearch(['title'], [ 'title' => $params['title'] ?? null ]) ->order(['sort' => 'asc', 'id' => 'desc']) ->select(); return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid')); } // 详情数据 public function read() { $id = request()->param('id'); $menu = SysMenuModel::withoutField([ 'created_at', 'updated_at', 'deleted_at' ]) ->with(['menu_ability_permission']) ->bypk($id) ->find() ->hidden(['menu_ability_permission.menu_id']); if (empty($menu)) { return error('菜单不存在'); } return success('获取成功', $menu); } // 新增数据 public function save() { $post = request()->post([ 'pid' => 0, 'title', 'name', 'path', 'icon', 'redirect', 'component', 'hidden' => 0, 'actived' => 0, 'keep_alive' => 0, 'sort' => 0, 'status' => 1, // $[*].ability_name 能力名称 // $[*].permission 权限标志 // $[*].sort 排序 'menu_ability_permission' => '[]' ]); $menu_ability_permission = []; if (!empty($post['menu_ability_permission'])) { $menu_ability_permission = json_decode($post['menu_ability_permission'], true); unset($post['menu_ability_permission']); } $validate = new SysMenuValidate; $check_data = array_merge($post, ['menu_ability_permission' => $menu_ability_permission]); if (!$validate->scene('create')->check($check_data)) { return error($validate->getError()); } SysMenuModel::startTrans(); try { // 新增菜单 $menu = SysMenuModel::create($post); if ($menu->isEmpty()) { throw new InvalidOperateException('新增菜单失败'); } // 新增菜单能力权限 if (!empty($menu_ability_permission)) { foreach ($menu_ability_permission as &$item) { $item['menu_id'] = $menu->id; } unset($item); $permission = $menu->menuAbilityPermission()->saveAll($menu_ability_permission); if (empty($permission)) { throw new InvalidOperateException('新增菜单能力权限失败'); } } SysMenuModel::commit(); } catch (InvalidOperateException $e) { SysMenuModel::rollback(); return error($e->getMessage()); } catch (\Throwable $th) { SysMenuModel::rollback(); Log::error($th->getMessage()); return error('操作失败'); } return success('操作成功'); } // 更新数据 public function update() { $id = request()->param('id'); $put = request()->put([ 'pid' => 0, 'title', 'name', 'path', 'icon', 'redirect', 'component', 'hidden' => 0, 'actived' => 0, 'keep_alive' => 0, 'sort' => 0, 'status' => 1, // $[*].ability_name 能力名称 // $[*].permission 权限标志 // $[*].sort 排序 'menu_ability_permission' => '[]' ]); $menu_ability_permission = []; if (!empty($put['menu_ability_permission'])) { $menu_ability_permission = json_decode($put['menu_ability_permission'], true); unset($put['menu_ability_permission']); } $validate = new SysMenuValidate; $check_data = array_merge($put, ['id' => $id, 'menu_ability_permission' => $menu_ability_permission]); if (!$validate->check($check_data)) { return error($validate->getError()); } SysMenuModel::startTrans(); try { $menu = SysMenuModel::bypk($id)->find(); if ($menu->isEmpty()) { throw new InvalidOperateException('请确认操作对象是否存在'); } // 更新菜单 if (!$menu->save($put)) { throw new InvalidOperateException('更新菜单失败'); } // 更新菜单能力权限 $exists = SysMenuAbilityPermissionModel::menuId($menu->id)->count(); if ($exists) { $deleted = SysMenuAbilityPermissionModel::menuId($menu->id)->delete(); if (!$deleted) { throw new InvalidOperateException('删除旧菜单能力权限失败'); } } if (!empty($menu_ability_permission)) { foreach ($menu_ability_permission as &$item) { $item['menu_id'] = $menu->id; } unset($item); $permission = (new SysMenuAbilityPermissionModel)->saveAll($menu_ability_permission); if (empty($permission)) { throw new InvalidOperateException('更新菜单能力权限失败'); } } SysMenuModel::commit(); } catch (InvalidOperateException $e) { SysMenuModel::rollback(); return error($e->getMessage()); } catch (\Throwable $th) { SysMenuModel::rollback(); Log::error($th->getMessage()); return error('操作失败'); } return success('操作成功'); } // 导入数据 public function import() { // 获取上传文件 $file = request()->file('file'); if (empty($file)) { return error('请上传文件'); } // 读取文件 $keys_map = [ 'A' => 'id', 'B' => 'pid', 'C' => 'title', 'D' => 'name', 'E' => 'path', 'F' => 'icon', 'G' => 'redirect', 'H' => 'component', 'I' => 'hidden', 'J' => 'actived', 'K' => 'keep_alive', 'L' => 'sort', 'M' => 'status', 'N' => 'menu_ability_permission' ]; $xlsx_data = xlsx_reader($file->getRealPath(), 2, $keys_map); if (empty($xlsx_data)) { return success('操作成功'); } // 处理行序号,防止后续被打乱 foreach ($xlsx_data as $row => &$item) { $item['seq_no'] = $row; } unset($item); // 数据组装成树形结构,方便处理上下级关系 $xlsx_data_tree = array_to_tree($xlsx_data, 0, 'pid'); // 处理导入菜单数据 $handle_errors = $this->handleImportData($xlsx_data_tree); if (!empty($handle_errors)) { return error(implode(PHP_EOL, $handle_errors)); } return success('操作成功'); } // 处理导入数据 private function handleImportData($menus) { $errors = []; $menu_model = new SysMenuModel; $chunks = array_chunk($menus, 100, true); foreach ($chunks as $chunk) { // 执行保存 if (!$this->executeReplaceMenu($chunk)) { $errors[] = sprintf('第【%s】行保存失败', implode(',', array_column($chunk, 'seq_no'))); continue; } // 获取该批次菜单,以获取新增情况的菜单的id $menus_map = $menu_model->whereIn('name', array_column($chunk, 'name'))->column('id', 'name'); foreach ($chunk as &$it) { // 更改菜单的子菜单pid值为新值 if (!empty($it['children'])) { foreach ($it['children'] as &$child) { if (isset($menus_map[$child['name']])) { $child['pid'] = $menus_map[$child['name']]; } } unset($child); } // 更改菜单能力权限的menu_id if (!empty($it['menu_ability_permission'])) { $menu_ability_permission = json_decode($it['menu_ability_permission'], true); foreach ($menu_ability_permission as &$permission) { if (isset($menus_map[$it['name']])) { $permission['menu_id'] = $menus_map[$it['name']]; } } unset($permission); $it['menu_ability_permission'] = $menu_ability_permission; } } unset($it); // 处理子菜单 $childrens = array_reduce(array_column($chunk, 'children'), 'array_merge', []); $handle_errors = $this->handleImportData($childrens); if (!empty($handler_ret)) { $errors = array_merge($errors, $handle_errors); } // 更新菜单能力权限 $menu_ability_permissions = array_reduce(array_column($chunk, 'menu_ability_permission'), 'array_merge', []); if (!empty($menu_ability_permissions)) { // 菜单能力权限模型实例 $ability_permission_model = new SysMenuAbilityPermissionModel; // 删除旧的菜单能力权限 $menus_id = array_column($menu_ability_permissions, 'menu_id'); $exists = $ability_permission_model->whereIn('menu_id', $menus_id)->count(); if ($exists) { $deleted = $ability_permission_model->whereIn('menu_id', $menus_id)->delete(); if (!$deleted) { $errors[] = sprintf('第【%s】行删除旧菜单能力权限失败', implode(',', array_column($chunk, 'seq_no'))); continue; } } // 保存新的菜单能力权限 $ability_updated = $ability_permission_model->saveAll($menu_ability_permissions); if ($ability_updated->isEmpty()) { $errors[] = sprintf('第【%s】行保存新菜单能力权限失败', implode(',', array_column($chunk, 'seq_no'))); continue; } } } return $errors; } // 组装repalce语句的values private function buildReplaceMenuValues($menu) { return sprintf( '(%d, %d, "%s", "%s", "%s", "%s", "%s", "%s", %d, %d, %d, %d, %d)', $menu['id'], $menu['pid'], $menu['title'], $menu['name'], $menu['path'], $menu['icon'], $menu['redirect'], $menu['component'], $menu['hidden'], $menu['actived'], $menu['keep_alive'], $menu['sort'], $menu['status'] ); } // 执行repalce语句 private function executeReplaceMenu($menus) { // 获取已存在的菜单 $menus_map = SysMenuModel::whereIn('name', array_column($menus, 'name'))->column('id', 'name'); // 组装保存sql语句 $values = []; foreach ($menus as &$it) { if (isset($menus_map[$it['name']])) { $it['id'] = $menus_map[$it['name']]; } if ($it['level'] == 1) { $it['pid'] = 0; } $values[] = $this->buildReplaceMenuValues($it); } unset($it); return Db::execute( sprintf( 'REPLACE INTO %s ( `id`, `pid`, `title`, `name`, `path`, `icon`, `redirect`, `component`, `hidden`, `actived`, `keep_alive`, `sort`, `status` ) VALUES %s;', (new SysMenuModel)->getTable(), implode(',', $values) ) ); } // 导出数据 public function export() { $schema = [ 'id' => '菜单ID', 'pid' => '父级ID', 'title' => '菜单标题', 'name' => '菜单名称', 'path' => '菜单路径', 'icon' => '菜单图标', 'redirect' => '菜单重定向路径', 'component' => '菜单组件路径', 'hidden' => '是否隐藏', 'actived' => '是否激活', 'keep_alive' => '是否缓存', 'sort' => '排序', 'status' => '状态', 'menu_ability_permission' => '菜单能力权限' ]; // 获取导出数据 $data = $this->getExportMenuData(); // 导出 return xlsx_writer($data, $schema, '菜单列表' . date('YmdHis'), 'php://output', 'file'); } private function getExportMenuData() { $param = request()->param(['title' => '']); $menus = SysMenuModel::withoutField([ 'created_at', 'updated_at', 'deleted_at' ]) ->with(['menu_ability_permission']) ->withSearch(['title'], [ 'title' => $param['title'] ?? null ]) ->order(['id' => 'asc']) ->select() ->hidden(['menu_ability_permission.menu_id']); if (!$menus->isEmpty()) { $menus->each(function ($item) { $item->menu_ability_permission = json_encode($item->menu_ability_permission); }); } return $menus; } // 设置排序值 public function sort() { $id = request()->param('id'); $sort = request()->post('sort'); $menu = SysMenuModel::bypk($id)->find(); if ($menu->isEmpty()) { return error('请确认操作对象是否存在'); } $menu->sort = $sort; if (!$menu->save()) { return error('操作失败'); } return success('操作成功'); } // 删除数据 public function delete() { $id = request()->param('id'); SysMenuModel::startTrans(); try { $menu = SysMenuModel::bypk($id)->find(); if ($menu->isEmpty()) { throw new InvalidOperateException('请确认操作对象是否存在'); } if (!$menu->delete()) { throw new InvalidOperateException('删除菜单失败'); } SysMenuModel::commit(); } catch (InvalidOperateException $e) { SysMenuModel::rollback(); return error($e->getMessage()); } catch (\Throwable $th) { SysMenuModel::rollback(); Log::error($th->getMessage()); return error('操作失败'); } return success('操作成功'); } }