From 1200bfb1bfd5c7bf3e563c3059a8fc365c7fd8a9 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Sat, 23 Mar 2024 22:01:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E7=A1=80=E6=9D=83=E9=99=90=E6=8E=A7?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Admin/Components.php | 10 +- app/Admin/Controllers/AdminRoleController.php | 50 ---- .../System/AdminMenuController.php | 128 ++++++++++ .../System/AdminPermissionController.php | 224 ++++++++++++++++++ .../System/AdminRoleController.php | 164 +++++++++++++ .../{ => System}/AdminUserController.php | 19 +- .../{ => System}/KeywordController.php | 12 +- app/Admin/Services/AdminMenuService.php | 94 ++++++++ app/Admin/Services/AdminPermissionService.php | 116 +++++++++ app/Admin/Services/AdminRoleService.php | 99 ++++++++ app/Admin/routes.php | 31 ++- app/Models/AdminRole.php | 2 +- lang/zh_CN/admin.php | 3 + 13 files changed, 879 insertions(+), 73 deletions(-) delete mode 100644 app/Admin/Controllers/AdminRoleController.php create mode 100644 app/Admin/Controllers/System/AdminMenuController.php create mode 100644 app/Admin/Controllers/System/AdminPermissionController.php create mode 100644 app/Admin/Controllers/System/AdminRoleController.php rename app/Admin/Controllers/{ => System}/AdminUserController.php (85%) rename app/Admin/Controllers/{ => System}/KeywordController.php (85%) create mode 100644 app/Admin/Services/AdminMenuService.php create mode 100644 app/Admin/Services/AdminPermissionService.php create mode 100644 app/Admin/Services/AdminRoleService.php diff --git a/app/Admin/Components.php b/app/Admin/Components.php index d631b1b..a9d9492 100644 --- a/app/Admin/Components.php +++ b/app/Admin/Components.php @@ -14,7 +14,7 @@ class Components extends BaseRenderer public function parentControl($apiUrl = null, $name = 'parent_id', $label = null, $labelField = 'name', $valueField = 'id') { return amisMake()->TreeSelectControl()->source($apiUrl) - ->name($name)->label($label ?? __('admin.components.parent_select')) + ->name($name)->label($label ?? __('admin.parent')) ->showIcon(false) ->labelField($labelField) ->valueField($valueField); @@ -135,10 +135,10 @@ class Components extends BaseRenderer if ($color) { $tag = amisMake()->Tag()->label($label ?? __('admin.components.tag')) ->displayMode('rounded')->style([ - 'color' => '#fff', - 'backgroundColor' => $color, - 'borderColor' => $color, - ]); + 'color' => '#fff', + 'backgroundColor' => $color, + 'borderColor' => $color, + ]); } else { $tag = amisMake()->Tag()->label($label ?? __('admin.components.tag')) ->displayMode('rounded')->color('inactive'); diff --git a/app/Admin/Controllers/AdminRoleController.php b/app/Admin/Controllers/AdminRoleController.php deleted file mode 100644 index 363b1aa..0000000 --- a/app/Admin/Controllers/AdminRoleController.php +++ /dev/null @@ -1,50 +0,0 @@ -baseCRUD() - ->headerToolbar([ - $this->createButton(true), - ...$this->baseHeaderToolBar(), - ]) - ->filterTogglable(false) - ->itemCheckableOn('${id !== 1}') - ->columns([ - amis()->TableColumn()->label('ID')->name('id')->sortable(), - amis()->TableColumn()->label(__('admin.admin_role.name'))->name('name'), - amis()->TableColumn()->label(__('admin.admin_role.slug'))->name('slug')->type('tag'), - amis()->TableColumn() - ->label(__('admin.created_at')) - ->name('created_at') - ->type('datetime') - ->sortable(true), - amis()->TableColumn() - ->label(__('admin.updated_at')) - ->name('updated_at') - ->type('datetime') - ->sortable(true), - amis()->Operation()->label(__('admin.actions'))->buttons([ - $this->setPermission()->visibleOn('${slug != "administrator"}'), - $this->rowEditButton(true)->visibleOn('${slug != "administrator"}'), - $this->rowDeleteButton()->visibleOn('${slug != "administrator"}'), - ]), - ]); - - return $this->baseList($crud)->css([ - '.tree-full' => [ - 'overflow' => 'hidden !important', - ], - '.cxd-TreeControl > .cxd-Tree' => [ - 'height' => '100% !important', - 'max-height' => '100% !important', - ], - ]); - } -} diff --git a/app/Admin/Controllers/System/AdminMenuController.php b/app/Admin/Controllers/System/AdminMenuController.php new file mode 100644 index 0000000..7cc5b6f --- /dev/null +++ b/app/Admin/Controllers/System/AdminMenuController.php @@ -0,0 +1,128 @@ +baseCRUD() + ->loadDataOnce() + ->syncLocation(false) + ->footerToolbar([]) + ->headerToolbar([ + $this->createButton(true, 'lg')->visible(Admin::user()->can('admin.system.admin_menus.create')), + ...$this->baseHeaderToolBar(), + ]) + ->filterTogglable(false) + ->footerToolbar(['statistics']) + ->bulkActions([ + $this->bulkDeleteButton()->reload('window')->visible(Admin::user()->can('admin.system.admin_menus.delete')), + ]) + ->columns([ + amis()->TableColumn('id', 'ID')->sortable(), + amis()->TableColumn('title', __('admin.admin_menu.title')), + amis()->TableColumn('icon', __('admin.admin_menu.icon')) + ->type('flex') + ->justify('start') + ->items([ + amis()->SvgIcon()->icon('${icon}')->className('mr-2 text-lg'), + '${icon}', + ]), + amis()->TableColumn('url', __('admin.admin_menu.url')), + amis()->TableColumn('order', __('admin.admin_menu.order'))->quickEdit( + amis()->NumberControl()->min(0)->saveImmediately(true) + ), + amis()->TableColumn('visible', __('admin.admin_menu.visible'))->quickEdit( + amis()->SwitchControl()->mode('inline')->saveImmediately(true) + ), + amis()->TableColumn('is_home', __('admin.admin_menu.is_home'))->quickEdit( + amis()->SwitchControl()->mode('inline')->saveImmediately(true) + ), + $this->rowActions([ + $this->rowEditButton(true, 'lg')->visible(Admin::user()->can('admin.system.admin_menus.update')), + $this->rowDeleteButton()->visible(Admin::user()->can('admin.system.admin_menus.delete')), + ]), + ]); + + return $this->baseList($crud); + } + + public function form(): Form + { + return $this->baseForm()->body([ + amis()->GroupControl()->body([ + amis()->TextControl('title', __('admin.admin_menu.title'))->required(), + amis()->TextControl('icon', __('admin.admin_menu.icon')) + ->description( + __('admin.admin_menu.icon_description'). + ' https://icones.js.org' + ), + ]), + amis()->GroupControl()->body([ + amis()->TreeSelectControl('parent_id', __('admin.admin_menu.parent_id')) + ->labelField('title') + ->valueField('id') + ->showIcon(false) + ->value(0) + ->source('/system/admin_menus?_action=getData'), + amis() + ->TextControl('component', __('admin.admin_menu.component')) + ->description(__('admin.admin_menu.component_desc')) + ->value('amis'), + ]), + amis()->TextControl('url', __('admin.admin_menu.url')) + ->required() + ->validateOnChange() + ->validations(['matchRegexp' => '/^(http(s)?\:\/)?(\/)+/']) + ->validationErrors(['matchRegexp' => __('admin.need_start_with_slash')]) + ->placeholder('eg: /admin_menus'), + amis()->NumberControl('order', __('admin.admin_menu.order')) + ->required() + ->displayMode('enhance') + ->description(__('admin.order_asc')) + ->min(0) + ->value(0), + amis()->ListControl('url_type', __('admin.admin_menu.type')) + ->options(Admin::adminMenuModel()::getType()) + ->value(Admin::adminMenuModel()::TYPE_ROUTE), + amis()->SwitchControl('visible', __('admin.admin_menu.visible')) + ->onText(__('admin.admin_menu.show')) + ->offText(__('admin.admin_menu.hide')) + ->value(1), + amis()->SwitchControl('is_home', __('admin.admin_menu.is_home')) + ->onText(__('admin.yes')) + ->offText(__('admin.no')) + ->description(__('admin.admin_menu.is_home_description')) + ->value(0), + amis()->SwitchControl('is_full', __('admin.admin_menu.is_full')) + ->onText(__('admin.yes')) + ->offText(__('admin.no')) + ->description(__('admin.admin_menu.is_full_description')) + ->value(0), + ])->onEvent([ + 'submitSucc' => [ + 'actions' => [ + 'actionType' => 'custom', + 'script' => 'window.location.reload()', + ], + ], + ]); + } + + public function detail(): Form + { + return $this->baseDetail()->body([]); + } +} diff --git a/app/Admin/Controllers/System/AdminPermissionController.php b/app/Admin/Controllers/System/AdminPermissionController.php new file mode 100644 index 0000000..8d28826 --- /dev/null +++ b/app/Admin/Controllers/System/AdminPermissionController.php @@ -0,0 +1,224 @@ +baseCRUD() + ->loadDataOnce() + ->filterTogglable(false) + ->footerToolbar([]) + ->headerToolbar([ + $this->createButton(true, 'lg')->visible(Admin::user()->can('admin.system.admin_permissions.create')), + 'bulkActions', + amis('reload')->align('right'), + amis('filter-toggler')->align('right'), + ]) + ->bulkActions([ + $this->bulkDeleteButton()->visible(Admin::user()->can('admin.system.admin_permissions.delete')), + ]) + ->columns([ + amis()->TableColumn('id', 'ID')->sortable(), + amis()->TableColumn('name', __('admin.admin_permission.name')), + amis()->TableColumn('slug', __('admin.admin_permission.slug')), + amis()->TableColumn('http_method', __('admin.admin_permission.http_method')) + ->type('each') + ->items( + Tag::make()->label('${item}')->className('my-1') + ) + ->placeholder(Tag::make()->label('ANY')), + amis()->TableColumn('http_path', __('admin.admin_permission.http_path')) + ->type('each') + ->items( + Tag::make()->label('${item}')->className('my-1') + ), + $this->rowActions([ + $this->rowEditButton(true, 'lg')->visible(Admin::user()->can('admin.system.admin_permissions.update')), + $this->rowDeleteButton()->visible(Admin::user()->can('admin.system.admin_permissions.delete')), + ]), + ]); + + return $this->baseList($crud); + } + + public function form(): Form + { + return $this->baseForm()->body([ + amis()->TextControl('name', __('admin.admin_permission.name'))->required(), + amis()->TextControl('slug', __('admin.admin_permission.slug'))->required(), + amis()->TreeSelectControl('parent_id', __('admin.parent')) + ->labelField('name') + ->valueField('id') + ->value(0) + ->options($this->service->getTree()), + amis()->CheckboxesControl('http_method', __('admin.admin_permission.http_method')) + ->options($this->getHttpMethods()) + ->description(__('admin.admin_permission.http_method_description')) + ->joinValues(false) + ->extractValue(), + amis()->NumberControl('order', __('admin.order')) + ->required() + ->labelRemark(__('admin.order_asc')) + ->displayMode('enhance') + ->min(0) + ->value(0), + amis()->ArrayControl('http_path', __('admin.admin_permission.http_path')) + ->items(amis()->TextControl()->options($this->getRoutes())->required()), + amis()->TreeSelectControl('menus', __('admin.menus')) + ->searchable() + ->multiple() + ->showIcon(false) + ->options(AdminMenuService::make()->getTree()) + ->labelField('title') + ->valueField('id') + ->autoCheckChildren(false) + ->joinValues(false) + ->extractValue(), + ]); + } + + public function detail(): Form + { + return $this->baseDetail()->body([]); + } + + private function getHttpMethods(): array + { + return collect(Admin::adminPermissionModel()::$httpMethods)->map(fn ($method) => [ + 'value' => $method, + 'label' => $method, + ])->toArray(); + } + + public function getRoutes(): array + { + $prefix = (string) Admin::config('admin.route.prefix'); + + $container = collect(); + + return collect(app('router')->getRoutes())->map(function ($route) use ($prefix, $container) { + if (! Str::startsWith($uri = $route->uri(), $prefix) && $prefix && $prefix !== '/') { + return null; + } + if (! Str::contains($uri, '{')) { + if ($prefix !== '/') { + $route = Str::replaceFirst($prefix, '', $uri.'*'); + } else { + $route = $uri.'*'; + } + + $route !== '*' && $container->push($route); + } + $path = preg_replace('/{.*}+/', '*', $uri); + $prefix !== '/' && $path = Str::replaceFirst($prefix, '', $path); + + return $path; + })->merge($container)->filter()->unique()->map(function ($method) { + return [ + 'value' => $method, + 'label' => $method, + ]; + })->values()->all(); + } + + public function autoGenerate() + { + $menus = Admin::adminMenuModel()::query()->get()->toArray(); + $slugMap = Admin::adminPermissionModel()::query()->get(['id', 'slug'])->keyBy('id')->toArray(); + $slugCache = []; + $permissions = []; + foreach ($menus as $menu) { + $_httpPath = + $menu['url_type'] == Admin::adminMenuModel()::TYPE_ROUTE ? $this->getHttpPath($menu['url']) : ''; + + $menuTitle = $menu['title']; + + // 避免名称重复 + if (in_array($menuTitle, data_get($permissions, '*.name', []))) { + $menuTitle = sprintf('%s(%s)', $menuTitle, $menu['id']); + } + + if ($_httpPath) { + $slug = Str::of(explode('?', $_httpPath)[0])->trim('/')->replace('/', '.')->replace('*', '')->value(); + } else { + $slug = Str::uuid(); + } + + if (in_array($slug, $slugCache)) { + $slug = $slug.'.'.$menu['id']; + } + $slugCache[] = $slug; + + $permissions[] = [ + 'id' => $menu['id'], + 'name' => $menuTitle, + 'slug' => data_get($slugMap, $menu['id'].'.slug') ?: $slug, + 'http_path' => json_encode($_httpPath ? [$_httpPath] : ''), + 'order' => $menu['order'], + 'parent_id' => $menu['parent_id'], + 'created_at' => $menu['created_at'], + 'updated_at' => $menu['updated_at'], + ]; + } + + Admin::adminPermissionModel()::query()->truncate(); + Admin::adminPermissionModel()::query()->insert($permissions); + + $permissionClass = Admin::adminPermissionModel(); + $pivotTable = (new $permissionClass)->menus()->getTable(); + + DB::table($pivotTable)->truncate(); + foreach ($permissions as $item) { + $query = DB::table($pivotTable); + $query->insert([ + 'permission_id' => $item['id'], + 'menu_id' => $item['id'], + ]); + + $_id = $item['id']; + while ($item['parent_id'] != 0) { + $query->clone()->insert([ + 'permission_id' => $_id, + 'menu_id' => $item['parent_id'], + ]); + + $item = Admin::adminMenuModel()::query()->find($item['parent_id']); + } + } + + return $this->response()->successMessage( + __('admin.successfully_message', ['attribute' => __('admin.admin_permission.auto_generate')]) + ); + } + + private function getHttpPath($uri) + { + $excepts = ['/', '', '-']; + if (in_array($uri, $excepts)) { + return ''; + } + + if (! str_starts_with($uri, '/')) { + $uri = '/'.$uri; + } + + return $uri.'*'; + } +} diff --git a/app/Admin/Controllers/System/AdminRoleController.php b/app/Admin/Controllers/System/AdminRoleController.php new file mode 100644 index 0000000..239b9c2 --- /dev/null +++ b/app/Admin/Controllers/System/AdminRoleController.php @@ -0,0 +1,164 @@ +baseCRUD() + ->headerToolbar([ + $this->createButton(true) + ->visible(Admin::user()->can('admin.system.admin_roles.create')), + amis('reload')->align('right'), + amis('filter-toggler')->align('right'), + ]) + ->filterTogglable(false) + ->itemCheckableOn('${id !== 1}') + ->columns([ + amis()->TableColumn()->label('ID')->name('id')->sortable(), + amis()->TableColumn()->label(__('admin.admin_role.name'))->name('name'), + amis()->TableColumn()->label(__('admin.admin_role.slug'))->name('slug')->type('tag'), + amis()->TableColumn() + ->label(__('admin.created_at')) + ->name('created_at') + ->type('datetime') + ->sortable(true), + amis()->TableColumn() + ->label(__('admin.updated_at')) + ->name('updated_at') + ->type('datetime') + ->sortable(true), + amis()->Operation()->label(__('admin.actions'))->buttons([ + $this->setMenu() + ->visible(Admin::user()->can('admin.system.admin_roles.set_menus')) + ->visibleOn('${slug != "administrator"}'), + $this->setPermission() + ->visible(Admin::user()->can('admin.system.admin_roles.set_permissions')) + ->visibleOn('${slug != "administrator"}'), + $this->rowEditButton(true) + ->visible(Admin::user()->can('admin.system.admin_roles.update')) + ->visibleOn('${slug != "administrator"}'), + $this->rowDeleteButton() + ->visible(Admin::user()->can('admin.system.admin_roles.delete')) + ->visibleOn('${slug != "administrator"}'), + ]), + ]); + + return $this->baseList($crud)->css([ + '.tree-full' => [ + 'overflow' => 'hidden !important', + ], + '.cxd-TreeControl > .cxd-Tree' => [ + 'height' => '100% !important', + 'max-height' => '100% !important', + ], + ]); + } + + protected function setPermission() + { + return amis()->DrawerAction()->label(__('admin.admin_role.set_permissions'))->icon('fa-solid fa-gear')->level('link')->drawer( + amis()->Drawer()->title(__('admin.admin_role.set_permissions'))->resizable()->closeOnOutside()->closeOnEsc()->body([ + amis() + ->Form() + ->api(admin_url('system/admin_roles/${id}/permissions')) + ->mode('normal') + ->data(['id' => '${id}']) + ->body([ + amis()->TreeControl() + ->name('permissions') + ->label() + ->multiple() + ->options(AdminPermissionService::make()->getTree()) + ->searchable() + ->cascade() + ->value('${permission_ids}') + + ->joinValues(false) + ->extractValue() + ->size('full') + ->className('h-full b-none') + ->inputClassName('h-full tree-full') + ->labelField('name') + ->valueField('id'), + ]), + ]) + ); + } + + public function savePermissions() + { + $this->service->savePermissions(request('id'), request('permissions')); + + return $this->autoResponse('success', __('admin.save')); + } + + protected function setMenu() + { + return amis()->DrawerAction()->label(__('admin.admin_role.set_menus'))->icon('fa-solid fa-gear')->level('link')->drawer( + amis()->Drawer()->title(__('admin.admin_role.set_menus'))->resizable()->closeOnOutside()->closeOnEsc()->body([ + amis() + ->Form() + ->api(admin_url(admin_url('system/admin_roles/${id}/menus'))) + ->mode('normal') + ->data(['id' => '${id}']) + ->body([ + amis()->TreeControl() + ->name('menus') + ->label() + ->multiple() + ->options(AdminMenuService::make()->getTree()) + ->searchable() + ->cascade() + ->value('${menu_ids}') + + ->joinValues(false) + ->extractValue() + ->size('full') + ->className('h-full b-none') + ->inputClassName('h-full tree-full') + ->labelField('title') + ->valueField('id'), + ]), + ]) + ); + } + + public function saveMenus() + { + $this->service->saveMenus(request('id'), request('menus')); + + return $this->autoResponse('success', __('admin.save')); + } + + public function form(): Form + { + return $this->baseForm()->body([ + amis()->TextControl()->label(__('admin.admin_role.name'))->name('name')->required(), + amis()->TextControl() + ->label(__('admin.admin_role.slug')) + ->name('slug') + ->description(__('admin.admin_role.slug_description')) + ->required(), + ]); + } + + public function detail(): Form + { + return $this->baseDetail()->body([]); + } +} diff --git a/app/Admin/Controllers/AdminUserController.php b/app/Admin/Controllers/System/AdminUserController.php similarity index 85% rename from app/Admin/Controllers/AdminUserController.php rename to app/Admin/Controllers/System/AdminUserController.php index 714d20a..6452f53 100644 --- a/app/Admin/Controllers/AdminUserController.php +++ b/app/Admin/Controllers/System/AdminUserController.php @@ -1,8 +1,9 @@ baseCRUD() ->headerToolbar([ - $this->createButton(true), + $this->createButton(true)->visible(Admin::user()->can('admin.system.admin_users.create')), ...$this->baseHeaderToolBar(), ]) ->filter($this->baseFilter()->body( @@ -29,7 +30,6 @@ class AdminUserController extends AdminController ->size('md') ->placeholder(__('admin.admin_user.search_username')) )) - ->quickSaveItemApi(admin_url('quick-edit/admin_users/$id')) ->itemCheckableOn('${id !== 1}') ->columns([ amisMake()->TableColumn('id', 'ID')->sortable(), @@ -42,10 +42,15 @@ class AdminUserController extends AdminController amisMake()->TableColumn('lock', __('admin.admin_user.lock'))->quickEdit(SwitchControl::make()->saveImmediately(true)->mode('inline')->disabledOn('${id === 1}')), amisMake()->TableColumn('created_at', __('admin.created_at'))->type('datetime')->sortable(true), Operation::make()->label(__('admin.actions'))->buttons([ - $this->rowEditButton(true)->visibleOn('${id != 1}'), - //单独修改密码 - $this->editPassword()->visibleOn('${id != 1}'), - $this->rowDeleteButton()->visibleOn('${id != 1}'), + $this->editPassword() + ->visible(Admin::user()->can('admin.system.admin_users.change_password')) + ->visibleOn('${id != 1}'), + $this->rowEditButton(true) + ->visible(Admin::user()->can('admin.system.admin_users.update')) + ->visibleOn('${id != 1}'), + $this->rowDeleteButton() + ->visible(Admin::user()->can('admin.system.admin_users.delete')) + ->visibleOn('${id != 1}'), ]), ]); diff --git a/app/Admin/Controllers/KeywordController.php b/app/Admin/Controllers/System/KeywordController.php similarity index 85% rename from app/Admin/Controllers/KeywordController.php rename to app/Admin/Controllers/System/KeywordController.php index 713dd0d..1f24b19 100644 --- a/app/Admin/Controllers/KeywordController.php +++ b/app/Admin/Controllers/System/KeywordController.php @@ -1,16 +1,20 @@ footerToolbar([]) //去掉分页-end ->headerToolbar([ - $this->createButton(true), + $this->createButton(true)->visible(Admin::user()->can('admin.system.keywords.create')), amis('reload')->align('right'), amis('filter-toggler')->align('right'), ]) @@ -44,8 +48,8 @@ class KeywordController extends AdminController TableColumn::make()->name('sort')->label('排序'), TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true), amisMake()->Operation()->label(__('admin.actions'))->buttons([ - $this->rowEditButton(true), - $this->rowDeleteButton(), + $this->rowEditButton(true)->visible(Admin::user()->can('admin.system.keywords.update')), + $this->rowDeleteButton()->visible(Admin::user()->can('admin.system.keywords.delete')), ]), ]); diff --git a/app/Admin/Services/AdminMenuService.php b/app/Admin/Services/AdminMenuService.php new file mode 100644 index 0000000..e7d3278 --- /dev/null +++ b/app/Admin/Services/AdminMenuService.php @@ -0,0 +1,94 @@ +modelName = Admin::adminMenuModel(); + } + + public function getTree(): array + { + $list = $this->query()->orderBy('order')->get()->toArray(); + + return array2tree($list); + } + + public function parentIsChild($id, $parent_id): bool + { + $parent = $this->query()->find($parent_id); + + do { + if ($parent->parent_id == $id) { + return true; + } + // 如果没有parent 则为顶级菜单 退出循环 + $parent = $parent->parent; + } while ($parent); + + return false; + } + + public function update($primaryKey, $data): bool + { + $columns = $this->getTableColumns(); + + $parent_id = Arr::get($data, 'parent_id'); + if ($parent_id != 0) { + amis_abort_if($this->parentIsChild($primaryKey, $parent_id), __('admin.admin_menu.parent_id_not_allow')); + } + + $model = $this->query()->whereKey($primaryKey)->first(); + + return $this->saveData($data, $columns, $model); + } + + public function store($data): bool + { + $columns = $this->getTableColumns(); + + $model = $this->getModel(); + + return $this->saveData($data, $columns, $model); + } + + public function changeHomePage($excludeId = 0) + { + $this->query()->when($excludeId, fn ($query) => $query->where('id', '<>', $excludeId))->update(['is_home' => 0]); + } + + public function list() + { + return ['items' => $this->getTree()]; + } + + protected function saveData($data, array $columns, AdminMenu $model): bool + { + foreach ($data as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } + + $v = $k == 'parent_id' ? intval($v) : $v; + + $model->setAttribute($k, $v); + + if ($k == 'is_home' && $v == 1) { + $this->changeHomePage($model->getKey()); + } + } + + return $model->save(); + } +} diff --git a/app/Admin/Services/AdminPermissionService.php b/app/Admin/Services/AdminPermissionService.php new file mode 100644 index 0000000..8d855b1 --- /dev/null +++ b/app/Admin/Services/AdminPermissionService.php @@ -0,0 +1,116 @@ +modelName = Admin::adminPermissionModel(); + } + + public function getTree(): array + { + $list = $this->query()->orderBy('order')->get()->toArray(); + + return array2tree($list); + } + + public function parentIsChild($id, $parent_id): bool + { + $parent = $this->query()->find($parent_id); + + do { + if ($parent->parent_id == $id) { + return true; + } + // 如果没有parent 则为顶级 退出循环 + $parent = $parent->parent; + } while ($parent); + + return false; + } + + public function getEditData($id): Model|\Illuminate\Database\Eloquent\Collection|Builder|array|null + { + $permission = parent::getEditData($id); + + $permission->load(['menus']); + + return $permission; + } + + public function store($data): bool + { + $this->checkRepeated($data); + + $columns = $this->getTableColumns(); + + $model = $this->getModel(); + + return $this->saveData($data, $columns, $model); + } + + public function update($primaryKey, $data): bool + { + $this->checkRepeated($data, $primaryKey); + + $columns = $this->getTableColumns(); + + $parent_id = Arr::get($data, 'parent_id'); + if ($parent_id != 0) { + amis_abort_if($this->parentIsChild($primaryKey, $parent_id), __('admin.admin_permission.parent_id_not_allow')); + } + + $model = $this->query()->whereKey($primaryKey)->first(); + + return $this->saveData($data, $columns, $model); + } + + public function checkRepeated($data, $id = 0) + { + $query = $this->query()->when($id, fn ($query) => $query->where('id', '<>', $id)); + + amis_abort_if($query->clone()->where('name', $data['name']) + ->exists(), __('admin.admin_permission.name_already_exists')); + + amis_abort_if($query->clone()->where('slug', $data['slug']) + ->exists(), __('admin.admin_permission.slug_already_exists')); + } + + public function list() + { + return ['items' => $this->getTree()]; + } + + protected function saveData($data, array $columns, AdminPermission $model): bool + { + $menus = Arr::pull($data, 'menus'); + + foreach ($data as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } + + $model->setAttribute($k, $v); + } + + if ($model->save()) { + $model->menus()->sync(Arr::has($menus, '0.id') ? Arr::pluck($menus, 'id') : $menus); + + return true; + } + + return false; + } +} diff --git a/app/Admin/Services/AdminRoleService.php b/app/Admin/Services/AdminRoleService.php new file mode 100644 index 0000000..0a6af78 --- /dev/null +++ b/app/Admin/Services/AdminRoleService.php @@ -0,0 +1,99 @@ +modelName = Admin::adminRoleModel(); + } + + public function list() + { + $query = $this->listQuery(); + + /** @var \Illuminate\Pagination\LengthAwarePaginator */ + $list = $query->with(['menus', 'permissions'])->paginate(request()->input('perPage', 20)); + $list->through(function ($item) { + $instance = $item->withoutRelations(); + $instance->setAttribute('menu_ids', $item->menus->pluck('id')); + $instance->setAttribute('permission_ids', $item->permissions->pluck('id')); + + return $instance; + }); + $items = $list->items(); + $total = $list->total(); + + return compact('items', 'total'); + } + + public function store($data): bool + { + $this->checkRepeated($data); + + $columns = $this->getTableColumns(); + + $model = $this->getModel(); + + foreach ($data as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } + + $model->setAttribute($k, $v); + } + + return $model->save(); + } + + public function update($primaryKey, $data): bool + { + $this->checkRepeated($data, $primaryKey); + + $columns = $this->getTableColumns(); + + $model = $this->query()->whereKey($primaryKey)->first(); + + foreach ($data as $k => $v) { + if (! in_array($k, $columns)) { + continue; + } + + $model->setAttribute($k, $v); + } + + return $model->save(); + } + + public function checkRepeated($data, $id = 0) + { + $query = $this->query()->when($id, fn ($query) => $query->where('id', '<>', $id)); + + amis_abort_if($query->clone() + ->where('name', $data['name']) + ->exists(), __('admin.admin_role.name_already_exists')); + + amis_abort_if($query->clone() + ->where('slug', $data['slug']) + ->exists(), __('admin.admin_role.slug_already_exists')); + } + + public function savePermissions($primaryKey, $permissions): void + { + $model = $this->query()->whereKey($primaryKey)->first(); + + $model->permissions()->detach(); + $model->permissions()->attach($permissions); + } + + public function saveMenus($primaryKey, $menus): void + { + $model = $this->query()->whereKey($primaryKey)->first(); + + $model->menus()->detach(); + $model->menus()->attach($menus); + } +} diff --git a/app/Admin/routes.php b/app/Admin/routes.php index 588e2d0..8e04c7e 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -1,5 +1,10 @@ resource('system/admin_users', App\Admin\Controllers\AdminUserController::class); - $router->get('system/admin_roles', [App\Admin\Controllers\AdminRoleController::class, 'index'])->name('admin_roles.index'); - $router->resource('system/settings', \App\Admin\Controllers\SettingController::class); - $router->resource('system/keywords', \App\Admin\Controllers\KeywordController::class); - $router->post('quick-edit/admin_users/{admin_user}', [\App\Admin\Controllers\AdminUserController::class, 'update']); + $router->group([ + 'prefix' => 'system', + 'as' => 'system.', + ], function (Router $router) { + // 账号管理 + $router->resource('admin_users', AdminUserController::class); + // 角色管理 + $router->resource('admin_roles', AdminRoleController::class); + $router->post('admin_roles/{admin_role}/menus', [AdminRoleController::class, 'saveMenus'])->name('admin_roles.set_menus'); + $router->post('admin_roles/{admin_role}/permissions', [AdminRoleController::class, 'savePermissions'])->name('admin_roles.set_permissions'); + // 权限管理 + $router->resource('admin_permissions', AdminPermissionController::class); + // 菜单管理 + $router->resource('admin_menus', AdminMenuController::class); + // 系统设置 + $router->resource('settings', \App\Admin\Controllers\SettingController::class); + // 数据字典 + $router->resource('keywords', KeywordController::class); + }); $router->resource('articles', \App\Admin\Controllers\ArticleController::class); @@ -62,6 +81,6 @@ Route::group([ $router->group([ 'prefix' => 'api', ], function (Router $router) { - $router->get('keywords/tree-list', [\App\Admin\Controllers\KeywordController::class, 'getTreeList'])->name('api.keywords.tree-list'); + $router->get('keywords/tree-list', [KeywordController::class, 'getTreeList'])->name('api.keywords.tree-list'); }); }); diff --git a/app/Models/AdminRole.php b/app/Models/AdminRole.php index cc225ce..0a1ffcb 100644 --- a/app/Models/AdminRole.php +++ b/app/Models/AdminRole.php @@ -20,6 +20,6 @@ class AdminRole extends Model public function menus(): BelongsToMany { - return $this->belongsToMany(Admin::adminMenuModel()::class, 'admin_role_menus', 'role_id', 'menu_id'); + return $this->belongsToMany(Admin::adminMenuModel(), 'admin_role_menus', 'role_id', 'menu_id'); } } diff --git a/lang/zh_CN/admin.php b/lang/zh_CN/admin.php index 983e28c..fddab02 100644 --- a/lang/zh_CN/admin.php +++ b/lang/zh_CN/admin.php @@ -137,6 +137,8 @@ return [ 'avatar' => '头像', 'name' => '姓名', 'roles' => '角色', + 'lock' => '锁定', + 'edit_password' => '修改密码', 'search_username' => '搜索用户名/名称', 'password_confirmation' => '两次输入密码不一致', 'old_password_required' => '请输入原密码', @@ -153,6 +155,7 @@ return [ 'name_already_exists' => '角色名称已存在', 'slug_already_exists' => '角色标识已存在', 'set_permissions' => '设置权限', + 'set_menus' => '设置菜单', ], 'admin_permissions' => '权限',