Compare commits

..

No commits in common. "panliang" and "main" have entirely different histories.

45 changed files with 792 additions and 2580 deletions

View File

@ -7,7 +7,7 @@ use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Controllers\AdminController;
use Slowlyo\OwlAdmin\Renderers\Operation;
use App\Admin\Services\AdService;
use App\Services\Admin\AdService;
use App\Admin\Components;
use App\Models\Keyword;
use App\Models\Ad;

View File

@ -1,131 +0,0 @@
<?php
namespace App\Admin\Controllers;
use App\Traits\CustomActionTrait;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Slowlyo\OwlAdmin\Controllers\AdminController as Controller;
use Slowlyo\OwlAdmin\Exceptions\AdminException;
use Throwable;
/**
* @property \App\Admin\Services\BaseService $service
*/
abstract class AdminController extends Controller
{
use CustomActionTrait;
/**
* {@inheritdoc}
*/
public function store(Request $request)
{
try {
DB::beginTransaction();
if ($this->actionOfQuickEdit()) {
$result = $this->service->quickEdit($request->all());
} elseif ($this->actionOfQuickEditItem()) {
$result = $this->service->quickEditItem($request->all());
} else {
$result = $this->service->store($request->all());
}
if (! $result) {
admin_abort($this->service->getError() ?: __('admin.save').__('admin.failed'));
}
DB::commit();
} catch (Throwable $th) {
DB::rollBack();
return $this->renderException($th);
}
return $this->response()->successMessage(__('admin.save').__('admin.successfully'));
}
/**
* {@inheritdoc}
*/
public function update(Request $request)
{
$input = Arr::except($request->all(), $this->service->primaryKey());
if ($request->filled('_fields')) {
$input = Arr::only($input, explode(',', $request->input('_fields')));
}
try {
DB::beginTransaction();
$result = $this->service->update($this->getPrimaryValue($request), $input);
if (! $result) {
admin_abort($this->service->getError() ?: __('admin.save').__('admin.failed'));
}
DB::commit();
} catch (Throwable $th) {
DB::rollBack();
return $this->renderException($th);
}
return $this->response()->successMessage(__('admin.save').__('admin.successfully'));
}
/**
* {@inheritdoc}
*/
public function destroy($ids)
{
try {
DB::beginTransaction();
$result = $this->service->delete($ids);
if ($result === false) {
admin_abort($this->service->getError());
}
DB::commit();
} catch (Throwable $th) {
DB::rollBack();
return $this->renderException($th);
}
return $this->response()->successMessage(__('admin.delete').__('admin.successfully'));
}
public function getQuickEditItemPath(array $fields = ['*'])
{
$path = $this->getUpdatePath();
if ($fields != ['*']) {
$path .= '?_fields='.implode(',', $fields);
}
return $path;
}
protected function renderException(Throwable $e)
{
report($e);
if ($e instanceof AdminException) {
return $e->render();
}
$message = $e->getMessage();
if ($e instanceof ValidationException) {
$message = Arr::first($e->errors());
if (is_array($message)) {
$message = Arr::first($message) ?: '参数错误';
}
}
return $this->response()->fail($message);
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Admin\Controllers;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Controllers\AdminRoleController as AdminRoleBaseController;
class AdminRoleController extends AdminRoleBaseController
{
public function list(): Page
{
$crud = $this->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',
],
]);
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace App\Admin\Controllers;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Operation;
use Slowlyo\OwlAdmin\Renderers\SwitchControl;
use Slowlyo\OwlAdmin\Renderers\TableColumn;
use App\Services\Admin\AdminUserService;
use Slowlyo\OwlAdmin\Services\AdminRoleService;
use Slowlyo\OwlAdmin\Controllers\AdminController;
/**
* @property AdminUserService $service
*/
class AdminUserController extends AdminController
{
protected string $serviceName = AdminUserService::class;
public function list(): Page
{
$crud = $this->baseCRUD()
->headerToolbar([
$this->createButton(true),
...$this->baseHeaderToolBar(),
])
->filter($this->baseFilter()->body(
amisMake()->TextControl('keyword', __('admin.keyword'))
->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(),
amisMake()->TableColumn('avatar', __('admin.admin_user.avatar'))->type('avatar')->src('${avatar}'),
amisMake()->TableColumn('username', __('admin.username')),
amisMake()->TableColumn('name', __('admin.admin_user.name')),
amisMake()->TableColumn('roles', __('admin.admin_user.roles'))->type('each')->items(
amisMake()->Tag()->label('${name}')->className('my-1')
),
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}'),
]),
]);
return $this->baseList($crud);
}
public function form(): Form
{
return $this->baseForm()->body([
amisMake()->ImageControl('avatar', __('admin.admin_user.avatar'))->receiver($this->uploadImagePath()),
amisMake()->TextControl('name', __('admin.admin_user.name'))->required(),
amisMake()->TextControl('username', __('admin.username'))->required(),
amisMake()->TextControl('password', __('admin.password'))->type('input-password')->required()->validations(['minLength' => 6])->hiddenOn('${id > 0}'),
amisMake()->TextControl('confirm_password', __('admin.confirm_password'))->type('input-password')->required()->validations(['minLength' => 6])->hiddenOn('${id > 0}'),
amisMake()->SelectControl('roles', __('admin.admin_user.roles'))
->searchable()
->multiple()
->labelField('name')
->valueField('id')
->joinValues(false)
->extractValue()
->options(AdminRoleService::make()->query()->get(['id', 'name'])),
SwitchControl::make()->name('lock')->label(__('admin.admin_user.lock'))->value(false),
]);
}
public function detail(): Form
{
return $this->baseDetail()->body([]);
}
public function editPassword()
{
return amisMake()->DialogAction()->icon('fa-regular fa-lightbulb')->label(__('admin.admin_user.edit_password'))->level('link')->dialog(
amisMake()->Dialog()->title(__('admin.admin_user.edit_password'))->body([
amisMake()->Form()->title('')
->api([
'method'=>'PUT',
'url'=> admin_url('system/admin_users/$id')
])
->body([
amisMake()->TextControl('id')->value('${id}')->hidden(true),
amisMake()->TextControl('username')->value('${username}')->hidden(true),
amisMake()->TextControl('password', __('admin.password'))->type('input-password')->required()->validations(['minLength' => 6]),
amisMake()->TextControl('confirm_password', __('admin.confirm_password'))->type('input-password')->required()->validations(['minLength' => 6]),
]),
])->size('md')
);
}
}

View File

@ -7,7 +7,7 @@ use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Controllers\AdminController;
use App\Admin\Services\ArticleService;
use App\Services\Admin\ArticleService;
use App\Admin\Components;
use App\Models\Keyword;
use App\Traits\CustomActionTrait;

View File

@ -2,22 +2,20 @@
namespace App\Admin\Controllers;
use App\Admin\Services\System\AdminUserService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Controllers\AuthController as AdminAuthController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpFoundation\Response;
use Slowlyo\OwlAdmin\Models\AdminUser;
use Slowlyo\OwlAdmin\Controllers\AuthController as AdminAuthController;
use Illuminate\Support\Facades\Hash;
class AuthController extends AdminAuthController
{
protected string $serviceName = AdminUserService::class;
public function login(Request $request)
{
if (Admin::config('admin.auth.login_captcha')) {
if (! $request->has('captcha')) {
if (!$request->has('captcha')) {
return $this->response()->fail(__('admin.required', ['attribute' => __('admin.captcha')]));
}
@ -31,25 +29,22 @@ class AuthController extends AdminAuthController
'username' => 'required',
'password' => 'required',
], [
'username'.'.required' => __('admin.required', ['attribute' => __('admin.username')]),
'username' . '.required' => __('admin.required', ['attribute' => __('admin.username')]),
'password.required' => __('admin.required', ['attribute' => __('admin.password')]),
]);
if ($validator->fails()) {
abort(Response::HTTP_BAD_REQUEST, $validator->errors()->first());
}
$adminModel = Admin::adminUserModel();
$adminModel = Admin::config("admin.auth.model", AdminUser::class);
$user = $adminModel::query()->where('username', $request->username)->first();
if ($user && $user->lock) {
if($user && $user->lock){
abort(Response::HTTP_BAD_REQUEST, '您的账号已被锁定,需要联系超级管理员解锁。');
} else {
}else{
if ($user && Hash::check($request->password, $user->password)) {
$module = Admin::currentModule(true);
$prefix = $module ? $module.'.' : '';
$token = $user->createToken($prefix.'admin')->plainTextToken;
$prefix = $module ? $module . '.' : '';
$token = $user->createToken($prefix . 'admin')->plainTextToken;
return $this->response()->success(compact('token'), __('admin.login_successful'));
}

View File

@ -1,20 +1,16 @@
<?php
namespace App\Admin\Controllers\System;
namespace App\Admin\Controllers;
use App\Admin\Components;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\System\KeywordService;
use Illuminate\Http\Request;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\TableColumn;
use Slowlyo\OwlAdmin\Renderers\TextControl;
use Slowlyo\OwlAdmin\Controllers\AdminController;
use App\Services\Admin\KeywordService;
use App\Admin\Components;
use Illuminate\Http\Request;
/**
* @property KeywordService $service
*/
class KeywordController extends AdminController
{
protected string $serviceName = KeywordService::class;
@ -27,15 +23,20 @@ class KeywordController extends AdminController
->footerToolbar([])
//去掉分页-end
->headerToolbar([
$this->createTypeButton('drawer', 'lg')->visible(Admin::user()->can('admin.system.keywords.create')),
$this->createButton(true),
$this->exportAction(),
...$this->baseHeaderToolBar(),
amis('reload')->align('right'),
amis('filter-toggler')->align('right'),
])
->bulkActions([])
->filter($this->baseFilter()->body([
amis()->TextControl('search', __('admin.keyword'))->placeholder(__('admin.keywords.search_name'))->size('md'),
]))
->filterDefaultVisible()
amis()->TextControl('name', __('admin.keyword'))
->size('md')
->placeholder(__('admin.keywords.search_name')),
amis()->TextControl('parent_name', __('admin.keywords.parent_keyword'))
->size('md')
->placeholder(__('admin.keywords.search_name'))
]
))
->columns([
// TableColumn::make()->name('id')->label('ID')->sortable(true),
TableColumn::make()->name('name')->label('名称'),
@ -43,10 +44,11 @@ class KeywordController extends AdminController
TableColumn::make()->name('value')->label('值'),
TableColumn::make()->name('sort')->label('排序'),
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
amis()->Operation()->label(__('admin.actions'))->buttons([
$this->rowEditTypeButton('drawer', 'lg')
->visible(Admin::user()->can('admin.system.keywords.update')),
$this->rowDeleteButton()->visible(Admin::user()->can('admin.system.keywords.delete')),
amisMake()->Operation()->label(__('admin.actions'))->buttons([
//按钮权限示例;
// amis()->action()->label('判断权限')->permission('keywords.custom', amis()->TextControl()->hidden()),
$this->rowEditButton(true),
$this->rowDeleteButton(),
]),
]);
@ -60,12 +62,11 @@ class KeywordController extends AdminController
TextControl::make()->name('name')->label('名称')->required(true),
TextControl::make()->name('key')->label('KEY')->required(true),
TextControl::make()->name('value')->label('值'),
amis()->NumberControl()->name('sort')->value(0)->min()->label('排序'),
amisMake()->NumberControl()->name('sort')->value(0)->min()->label('排序'),
]);
}
public function getTreeList(Request $request)
{
public function getTreeList(Request $request){
return $this->service->getTree();
}
}

View File

@ -1,128 +0,0 @@
<?php
namespace App\Admin\Controllers\System;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\System\AdminMenuService;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
/**
* @property AdminMenuService $service
*/
class AdminMenuController extends AdminController
{
protected string $serviceName = AdminMenuService::class;
public function list(): Page
{
$crud = $this->baseCRUD()
->loadDataOnce()
->syncLocation(false)
->footerToolbar([])
->headerToolbar([
$this->createTypeButton('drawer', '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->rowEditTypeButton('drawer', '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').
'<a href="https://icones.js.org/collection/all" target="_blank"> https://icones.js.org</a>'
),
]),
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([]);
}
}

View File

@ -1,223 +0,0 @@
<?php
namespace App\Admin\Controllers\System;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\System\AdminMenuService;
use App\Admin\Services\System\AdminPermissionService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Renderers\Tag;
/**
* @property AdminPermissionService $service
*/
class AdminPermissionController extends AdminController
{
protected string $serviceName = AdminPermissionService::class;
public function list(): Page
{
$crud = $this->baseCRUD()
->loadDataOnce()
->filterTogglable(false)
->footerToolbar([])
->headerToolbar([
$this->createTypeButton('drawer', 'lg')->visible(Admin::user()->can('admin.system.admin_permissions.create')),
...$this->baseHeaderToolBar(),
])
->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->rowEditTypeButton('drawer', '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.'*';
}
}

View File

@ -1,169 +0,0 @@
<?php
namespace App\Admin\Controllers\System;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\System\AdminRoleService;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Renderers\DrawerAction;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Services\AdminMenuService;
use Slowlyo\OwlAdmin\Services\AdminPermissionService;
/**
* @property AdminRoleService $service
*/
class AdminRoleController extends AdminController
{
protected string $serviceName = AdminRoleService::class;
public function list(): Page
{
$crud = $this->baseCRUD()
->headerToolbar([
$this->createTypeButton('drawer', 'lg')
->visible(Admin::user()->can('admin.system.admin_roles.create')),
...$this->baseHeaderToolBar(),
])
->bulkActions([])
->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->rowSetMenuButton()
->visible(Admin::user()->can('admin.system.admin_roles.set_menus'))
->visibleOn('${slug != "administrator"}'),
$this->rowSetPermissionButton()
->visible(Admin::user()->can('admin.system.admin_roles.set_permissions'))
->visibleOn('${slug != "administrator"}'),
$this->rowEditTypeButton('drawer', 'lg')
->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 rowSetPermissionButton(): DrawerAction
{
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()
->heightAuto()
->showIcon(false)
->initiallyOpen(false)
// ->size('full')
// ->className('h-full b-none')
// ->inputClassName('h-full tree-full')
->labelField('name')
->valueField('id'),
),
])->size('lg')
);
}
public function savePermissions()
{
$this->service->savePermissions(request('id'), request('permissions'));
return $this->autoResponse('success', __('admin.save'));
}
protected function rowSetMenuButton(): DrawerAction
{
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()
->heightAuto()
->showIcon(false)
->initiallyOpen(false)
// ->size('full')
// ->className('h-full b-none')
// ->inputClassName('h-full tree-full')
->labelField('title')
->valueField('id')
),
])->size('lg')
);
}
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([]);
}
}

View File

@ -1,144 +0,0 @@
<?php
namespace App\Admin\Controllers\System;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\System\AdminUserService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Renderers\DrawerAction;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
use Slowlyo\OwlAdmin\Renderers\TableColumn;
use Slowlyo\OwlAdmin\Services\AdminRoleService;
/**
* @property AdminUserService $service
*/
class AdminUserController extends AdminController
{
protected string $serviceName = AdminUserService::class;
public function list(): Page
{
$crud = $this->baseCRUD()
->headerToolbar([
$this->createTypeButton('drawer', 'lg')->visible(Admin::user()->can('admin.system.admin_users.create')),
...$this->baseHeaderToolBar(),
])
->bulkActions([])
->filter($this->baseFilter()->body(
amis()->TextControl('keyword', __('admin.keyword'))
->size('md')
->placeholder(__('admin.admin_user.search_username'))
))
->filterDefaultVisible()
->quickSaveItemApi($this->getQuickEditItemPath(['lock']))
->itemCheckableOn('${id !== 1}')
->columns([
amis()->TableColumn('id', 'ID')->sortable(),
amis()->TableColumn('avatar', __('admin.admin_user.avatar'))->type('avatar')->src('${avatar}'),
amis()->TableColumn('username', __('admin.username')),
amis()->TableColumn('name', __('admin.admin_user.name')),
amis()->TableColumn('roles', __('admin.admin_user.roles'))->type('each')->items(
amis()->Tag()->label('${name}')->className('my-1')
),
tap(amis()->TableColumn('lock', __('admin.admin_user.lock')), function (TableColumn $column) {
if (Admin::user()->can('admin.system.admin_users.update')) {
return $column->quickEdit(
amis()->SwitchControl()->saveImmediately()->mode('inline')->disabledOn('${id === 1}')
);
}
return $column->type('status')->source([
0 => ['label' => '未锁定', 'icon' => 'fa fa-times-circle', 'color' => '#30bf13'],
1 => ['label' => '已锁定', 'icon' => 'fa fa-check-circle', 'color' => '#f70e47'],
]);
}),
amis()->TableColumn('created_at', __('admin.created_at'))->type('datetime')->sortable(true),
$this->rowActions([
$this->rowChangePasswordButton()
->visible(Admin::user()->can('admin.system.admin_users.change_password'))
->visibleOn('${id != 1}'),
$this->rowEditTypeButton('drawer', 'lg')
->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}'),
]),
]);
return $this->baseList($crud);
}
public function form(): Form
{
return $this->baseForm()->body([
amis()->ImageControl('avatar', __('admin.admin_user.avatar'))->receiver(admin_url('upload_image') . '?full-url=1'),
amis()->TextControl('name', __('admin.admin_user.name'))->required(),
amis()->TextControl('username', __('admin.username'))->required(),
amis()->TextControl('password', __('admin.password'))->type('input-password')->required()->validations(['minLength' => 6])->hiddenOn('${id > 0}'),
amis()->TextControl('confirm_password', __('admin.confirm_password'))->type('input-password')->required()->validations(['minLength' => 6])->hiddenOn('${id > 0}'),
amis()->SelectControl('roles', __('admin.admin_user.roles'))
->searchable()
->multiple()
->labelField('name')
->valueField('id')
->joinValues(false)
->extractValue()
->options(AdminRoleService::make()->query()->get(['id', 'name'])),
amis()->SwitchControl()->name('lock')->label(__('admin.admin_user.lock'))->value(false),
]);
}
public function detail(): Form
{
return $this->baseDetail()->body([]);
}
/**
* 修改密码
*/
public function changePassword($id, Request $request)
{
$validator = Validator::make(
data: $request->input(),
rules: [
'password' => ['bail', 'required', 'confirmed', 'min:6'],
],
attributes: [
'password' => '密码',
],
);
if ($validator->fails()) {
admin_abort($validator->errors()->first());
}
$this->service->update($id, $request->only(['password']));
return $this->autoResponse('success', __('admin.save'));
}
/**
* 修改密码按钮
*/
protected function rowChangePasswordButton(): DrawerAction
{
return amis()->DrawerAction()->icon('fa-regular fa-lightbulb')->label(__('admin.admin_user.edit_password'))->level('link')->drawer(
amis()->Drawer()->title(__('admin.admin_user.edit_password'))->body([
amis()->Form()->title('')
->api([
'method' => 'POST',
'url' => admin_url('system/admin_users/$id/change-password'),
])
->body([
amis()->TextControl('password', __('admin.password'))->type('input-password')->required()->validations(['minLength' => 6]),
amis()->TextControl('password_confirmation', __('admin.confirm_password'))->type('input-password')->required()->validations(['minLength' => 6]),
]),
])->size('lg')
);
}
}

View File

@ -1,195 +0,0 @@
<?php
namespace App\Admin;
use Illuminate\Support\Arr;
use Slowlyo\OwlAdmin\Admin;
class Menu
{
protected array $menus = [];
public function all()
{
$menus = $this->userMenus()
->push(...array_map(fn ($item) => $this->formatItem($item), $this->menus))
->sortBy('order')
->values()
->toArray();
return array_merge($this->list2Menu($menus), $this->extra());
}
private function userMenus()
{
/** @var \App\Models\AdminUser */
$user = Admin::user();
return $user->allMenus();
}
private function list2Menu($list, $parentId = 0, $parentName = ''): array
{
$data = [];
foreach ($list as $key => $item) {
if ($item['parent_id'] == $parentId) {
$idStr = "[{$item['id']}]";
$_temp = [
'name' => $parentName ? $parentName.'-'.$idStr : $idStr,
'path' => $item['url'],
'component' => data_get($item, 'component') ?? 'amis',
'is_home' => $item['is_home'],
'is_full' => $item['is_full'] ?? 0,
'is_link' => $item['url_type'] == Admin::adminMenuModel()::TYPE_LINK,
'meta' => [
'title' => $item['title'],
'icon' => $item['icon'] ?? '-',
'hide' => $item['visible'] == 0,
'order' => $item['order'],
],
];
$children = $this->list2Menu($list, (int) $item['id'], $_temp['name']);
if (! empty($children)) {
$_temp['component'] = 'amis';
$_temp['children'] = $children;
}
$data[] = $_temp;
if (! in_array($_temp['path'], Admin::config('admin.route.without_extra_routes'))) {
array_push($data, ...$this->generateRoute($_temp));
}
unset($list[$key]);
}
}
return $data;
}
private function generateRoute($item): array
{
$url = $item['path'] ?? '';
$url = preg_replace('/\?.*/', '', $url);
if (! $url || array_key_exists('children', $item)) {
return [];
}
$menu = fn ($action, $path) => [
'name' => $item['name'].'-'.$action,
'path' => $url.$path,
'component' => 'amis',
'meta' => [
'hide' => true,
'icon' => Arr::get($item, 'meta.icon'),
'title' => Arr::get($item, 'meta.title').' - '.__('admin.'.$action),
],
];
return [
$menu('create', '/create'),
$menu('show', '/:id'),
$menu('edit', '/:id/edit'),
];
}
public function add($menus)
{
$this->menus = array_merge($this->menus, $menus);
return $this;
}
private function formatItem($item)
{
return array_merge([
'title' => '',
'url' => '',
'url_type' => 1,
'icon' => '',
'parent_id' => 0,
'id' => 999,
'is_home' => 0,
'visible' => 1,
'order' => 99,
], $item);
}
/**
* 额外菜单
*
* @return array|array[]
*/
private function extra()
{
$extraMenus = [
[
'name' => 'user_setting',
'path' => '/user_setting',
'component' => 'amis',
'meta' => [
'hide' => true,
'title' => __('admin.user_setting'),
'icon' => 'material-symbols:manage-accounts',
'singleLayout' => 'basic',
],
],
];
if (Admin::config('admin.show_development_tools')) {
$extraMenus = array_merge($extraMenus, $this->devToolMenus());
}
return $extraMenus;
}
/**
* 开发者工具菜单
*
* @return array[]
*/
private function devToolMenus()
{
return [
[
'name' => 'dev_tools',
'path' => '/dev_tools',
'component' => 'amis',
'meta' => [
'title' => __('admin.developer'),
'icon' => 'fluent:window-dev-tools-20-regular',
],
'children' => [
[
'name' => 'dev_tools_extensions',
'path' => '/dev_tools/extensions',
'component' => 'amis',
'meta' => [
'title' => __('admin.extensions.menu'),
'icon' => 'ion:extension-puzzle-outline',
],
],
[
'name' => 'dev_tools_code_generator',
'path' => '/dev_tools/code_generator',
'component' => 'amis',
'meta' => [
'title' => __('admin.code_generator'),
'icon' => 'ic:baseline-code',
],
],
[
'name' => 'dev_tools_editor',
'path' => '/dev_tools/editor',
'component' => 'editor',
'meta' => [
'title' => __('admin.visual_editor'),
'icon' => 'mdi:monitor-edit',
],
],
],
],
];
}
}

View File

@ -1,22 +0,0 @@
<?php
namespace App\Admin\Middleware;
use Closure;
use Illuminate\Http\Response;
use Slowlyo\OwlAdmin\Admin;
class Authenticate
{
public function handle($request, Closure $next)
{
if (Admin::permission()->authIntercept($request)) {
return Admin::response()
->additional(['code' => Response::HTTP_UNAUTHORIZED])
->doNotDisplayToast()
->fail(__('admin.please_login'));
}
return $next($request);
}
}

View File

@ -1,103 +0,0 @@
<?php
namespace App\Admin\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Support\Cores\Permission;
use Symfony\Component\HttpFoundation\Response;
class CheckPermission
{
/**
* @var array<int, string>
*/
protected $exceptPaths = [
'/api/*',
'/start_chunk_upload_file',
'/save_chunk_upload_file',
'/finish_chunk_upload_file',
'/upload_file',
'/upload_image',
'/upload_rich'
];
/**
* @var array<int, string>
*/
protected $exceptRoutes = [
//
];
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($this->inExceptArray($request) || $this->checkRoutePermission($request)) {
return $next($request);
}
return Admin::response()->fail(__('admin.unauthorized'), ['route' => $request->route()->getName()]);
}
protected function checkRoutePermission(Request $request): bool
{
if (is_null($user = Admin::user())) {
return false;
}
$ability = $this->normalizeRouteAbility(
$original = $request->route()->getName()
);
return collect($ability)
->when($ability !== $original, fn (Collection $collection) => $collection->push($original))
->contains(fn ($ability) => $user->can($ability));
}
protected function normalizeRouteAbility(string $ability)
{
foreach ($this->resourceAbilityMap() as $method => $map) {
if (str_ends_with($ability, ".{$method}")) {
return preg_replace("/(.*)\.{$method}$/", '${1}.'.$map, $ability);
}
}
return $ability;
}
protected function resourceAbilityMap(): array
{
return [
'index' => 'list',
'show' => 'view',
'store' => 'create',
'edit' => 'update',
'destroy' => 'delete',
];
}
protected function inExceptArray(Request $request): bool
{
if (in_array($request->route()->getName(), $this->exceptRoutes)) {
return true;
}
$permission = Admin::permission();
return $request->is(
collect($this->exceptPaths)
->when(Admin::permission(), fn (Collection $collection, Permission $permission) => $collection->merge($permission->authExcept)->merge($permission->permissionExcept))
->map(function ($path) {
$path = trim((string) config('admin.route.prefix'), '/').'/'.trim((string) $path, '/');
return ltrim($path, '/');
})
);
}
}

View File

@ -1,162 +0,0 @@
<?php
namespace App\Admin\Services;
use Slowlyo\OwlAdmin\Services\AdminService;
use Illuminate\Database\Eloquent\Model;
/**
* @method Region getModel()
* @method Region|\Illuminate\Database\Query\Builder query()
*/
class BaseService extends AdminService
{
protected string $modelFilterName = '';
protected bool $modelSortAble = false;
protected Model $currentModel;
public function getCurrentModel()
{
return $this->currentModel;
}
public function sortColumn()
{
return 'id';
}
public function getTree()
{
$list = $this->query()->orderByDesc('sort')->get();
$minNum = $list->min('parent_id');
return array2tree($list->toArray(), $minNum);
}
public function list()
{
$query = $this->listQuery();
if (request()->input('_all')) {
$list = (clone $query)->get();
$items = $list->all();
$total = $list->count();
} else {
$list = (clone $query)->paginate(request()->input('perPage', 20));
$items = $list->items();
$total = $list->total();
}
return compact('items', 'total');
}
public function getModelFilter()
{
return $this->modelFilterName;
}
public function listQuery()
{
$query = $this->query();
$this->addRelations($query);
if ($filter = $this->getModelFilter()) {
$query->filter(request()->input(), $filter);
}
if ($this->modelSortAble) {
$query->sort();
}
$this->sortable($query);
return $query;
}
public function store($data): bool
{
$data = $this->resloveData($data);
$validate = $this->validate($data);
if ($validate !== true) {
$this->setError($validate);
return false;
}
$model = $this->modelName::create($data);
$this->currentModel = $model;
return true;
}
public function update($primaryKey, $data): bool
{
$model = $this->query()->whereKey($primaryKey)->firstOrFail();
$data = $this->resloveData($data, $model);
$validate = $this->validate($data, $model);
if ($validate !== true) {
$this->setError($validate);
return false;
}
$model->update($data);
$this->currentModel = $model;
return true;
}
public function delete(string $ids): mixed
{
$this->preDelete(explode(',', $ids));
return parent::delete($ids);
}
/**
* 处理表单数据
*
* @param array $data
* @param Model $model : 添加, 非空: 修改
* @return array
*/
public function resloveData($data, $model = null)
{
return $data;
}
/**
* 表单验证
*
* @param array $data
* @param Model $model : 添加, 非空: 修改
* @return mixed true: 验证通过, string: 错误提示
*/
public function validate($data, $model = null)
{
// $createRules = [
// 'key' => ['required', Rule::unique('keywords', 'key')],
// 'name' => ['required'],
// ];
// $updateRules = [
// 'key' => [Rule::unique('keywords', 'key')->ignore($model->id)]
// ];
// $validator = Validator::make($data, $model ? $updateRules : $createRules, [
// 'key.unique' => ':input 已经存在'
// ]);
// if ($validator->fails()) {
// return $validator->errors()->first();
// }
return true;
}
/**
* 删除的前置方法
*
* @param array $ids 主键id
*/
public function preDelete(array $ids): void
{
}
}

View File

@ -1,95 +0,0 @@
<?php
namespace App\Admin\Services\System;
use App\Admin\Services\BaseService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Models\AdminMenu;
/**
* @method AdminMenu getModel()
* @method AdminMenu|Builder query()
*/
class AdminMenuService extends BaseService
{
public function __construct()
{
$this->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();
}
}

View File

@ -1,117 +0,0 @@
<?php
namespace App\Admin\Services\System;
use App\Admin\Services\BaseService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Models\AdminPermission;
/**
* @method AdminPermission getModel()
* @method AdminPermission|Builder query()
*/
class AdminPermissionService extends BaseService
{
public function __construct()
{
$this->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;
}
}

View File

@ -1,100 +0,0 @@
<?php
namespace App\Admin\Services\System;
use App\Admin\Services\BaseService;
use Slowlyo\OwlAdmin\Admin;
class AdminRoleService extends BaseService
{
public function __construct()
{
$this->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);
}
}

View File

@ -1,161 +0,0 @@
<?php
namespace App\Admin\Services\System;
use App\Admin\Services\BaseService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Hash;
use Slowlyo\OwlAdmin\Admin;
class AdminUserService extends BaseService
{
protected string $modelName;
public function __construct()
{
$this->modelName = Admin::adminUserModel();
}
public function getEditData($id): Model|\Illuminate\Database\Eloquent\Collection|Builder|array|null
{
$adminUser = parent::getEditData($id)->makeHidden('password');
$adminUser->load('roles');
return $adminUser;
}
public function store($data): bool
{
if ($this->checkUsernameUnique($data['username'])) {
admin_abort(__('admin.admin_user.username_already_exists'));
}
if (! data_get($data, 'password')) {
admin_abort(__('admin.required', ['attribute' => __('admin.password')]));
}
if (array_key_exists('confirm_password', $data)) {
if ($data['password'] !== $data['confirm_password']) {
admin_abort(__('admin.admin_user.password_confirmation'));
}
unset($data['confirm_password']);
}
if (Hash::needsRehash($data['password'])) {
$data['password'] = Hash::make($data['password']);
}
$model = $this->getModel();
$user = $model::create(Arr::except($data, ['roles']));
if (isset($data['roles'])) {
$user->roles()->attach($data['roles']);
}
return true;
}
public function update($primaryKey, $data): bool
{
if (isset($data['username']) && $this->checkUsernameUnique($data['username'], $primaryKey)) {
admin_abort(__('admin.admin_user.username_already_exists'));
}
if (isset($data['password']) && Hash::needsRehash($data['password'])) {
$data['password'] = Hash::make($data['password']);
}
$user = $this->query()->whereKey($primaryKey)->firstOrFail();
foreach (['username', 'password', 'name', 'avatar', 'lock'] as $attribute) {
if (array_key_exists($attribute, $data)) {
$user->{$attribute} = $data[$attribute];
}
}
if ($user->isDirty('lock') && $user->lock != 1) {
$user->error_num = 0;
$user->last_error_at = null;
}
$user->save();
if (isset($data['roles'])) {
$roles = Arr::pull($data, 'roles');
$user->roles()->detach();
$user->roles()->attach(Arr::has($roles, '0.id') ? Arr::pluck($roles, 'id') : $roles);
}
return true;
}
public function checkUsernameUnique($username, $id = 0): bool
{
return $this->query()
->where('username', $username)
->when($id, fn ($query) => $query->where('id', '<>', $id))
->exists();
}
public function updateUserSetting($primaryKey, $data): bool
{
$this->passwordHandler($data, $primaryKey);
return parent::update($primaryKey, $data);
}
public function passwordHandler(&$data, $id = null)
{
$password = Arr::get($data, 'password');
if ($password) {
if ($password !== Arr::get($data, 'confirm_password')) {
admin_abort(__('admin.admin_user.password_confirmation'));
}
if (strlen($password) < 6) {
admin_abort('密码长度至少6个字符');
}
if ($id) {
if (! Arr::get($data, 'old_password')) {
admin_abort(__('admin.admin_user.old_password_required'));
}
$oldPassword = $this->query()->where('id', $id)->value('password');
if (! Hash::check($data['old_password'], $oldPassword)) {
admin_abort(__('admin.admin_user.old_password_error'));
}
}
$data['password'] = bcrypt($password);
unset($data['confirm_password']);
unset($data['old_password']);
}
}
public function list()
{
$keyword = request()->keyword;
$query = $this
->query()
->with('roles')
->select(['id', 'name', 'username', 'avatar', 'created_at', 'lock'])
->when($keyword, function ($query) use ($keyword) {
$query->where('username', 'like', "%{$keyword}%")->orWhere('name', 'like', "%{$keyword}%");
});
$this->sortable($query);
$items = (clone $query)->paginate(request()->input('perPage', 20))->items();
$total = (clone $query)->count();
return compact('items', 'total');
}
}

View File

@ -19,8 +19,15 @@ Route::group([
$router->post('login', [App\Admin\Controllers\AuthController::class, 'login']);
$router->resource('system/admin_users', App\Admin\Controllers\AdminUserController::class);
$router->post('quick-edit/admin_users/{admin_user}',[\App\Admin\Controllers\AdminUserController::class, 'update']);
$router->get('system/admin_roles', [App\Admin\Controllers\AdminRoleController::class, 'index']);
$router->resource('system/settings', \App\Admin\Controllers\SettingController::class);
$router->resource('system/keywords', \App\Admin\Controllers\KeywordController::class);
$router->resource('articles', \App\Admin\Controllers\ArticleController::class);
$router->resource('ads', \App\Admin\Controllers\AdController::class);
@ -33,26 +40,4 @@ Route::group([
$router->post('start_chunk_upload_file', [\App\Admin\Controllers\IndexController::class, 'startChunk']);
$router->post('save_chunk_upload_file', [\App\Admin\Controllers\IndexController::class, 'saveChunk']);
$router->post('finish_chunk_upload_file', [\App\Admin\Controllers\IndexController::class, 'finishChunk']);
$router->group([
'prefix' => 'system',
'as' => 'system.',
], function (Router $router) {
// 账号管理
$router->resource('admin_users', App\Admin\Controllers\System\AdminUserController::class);
$router->post('/admin_users/{admin_user}/change-password', [App\Admin\Controllers\System\AdminUserController::class, 'changePassword'])->name('admin_users.change_password');
// 角色管理
$router->resource('admin_roles', App\Admin\Controllers\System\AdminRoleController::class);
$router->post('admin_roles/{admin_role}/menus', [App\Admin\Controllers\System\AdminRoleController::class, 'saveMenus'])->name('admin_roles.set_menus');
$router->post('admin_roles/{admin_role}/permissions', [App\Admin\Controllers\System\AdminRoleController::class, 'savePermissions'])->name('admin_roles.set_permissions');
// 权限管理
$router->resource('admin_permissions', App\Admin\Controllers\System\AdminPermissionController::class);
// 菜单管理
$router->resource('admin_menus', App\Admin\Controllers\System\AdminMenuController::class);
// 系统设置
$router->resource('settings', \App\Admin\Controllers\SettingController::class);
// 数据字典
$router->resource('keywords', App\Admin\Controllers\System\KeywordController::class);
});
});

View File

@ -1,18 +0,0 @@
<?php
namespace App\ModelFilters;
use EloquentFilter\ModelFilter;
class KeywordFilter extends ModelFilter
{
public function search($name)
{
return $this->where('name', 'like', '%'.$name.'%')->orWhere('key', 'like', '%'.$name.'%');
}
public function parentKey($key)
{
return $this->where('parent_key', $key);
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Models\AdminRole as Model;
class AdminRole extends Model
{
protected static function boot(): void
{
parent::boot();
static::deleting(function (AdminRole $model) {
$model->menus()->detach();
$model->permissions()->detach();
});
}
public function menus(): BelongsToMany
{
return $this->belongsToMany(Admin::adminMenuModel(), 'admin_role_menus', 'role_id', 'menu_id');
}
}

View File

@ -1,121 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Collection;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Models\AdminUser as Model;
class AdminUser extends Model
{
/**
* @var \Illuminate\Support\Collection
*/
protected $allAbilities;
public function roles(): BelongsToMany
{
return $this->belongsToMany(Admin::adminRoleModel(), 'admin_role_users', 'user_id', 'role_id')->withTimestamps();
}
/**
* 获取此用户的全部能力
*/
public function allAbilities(): Collection
{
if ($this->allAbilities) {
return $this->allAbilities;
}
$model = Admin::adminPermissionModel();
$allPermissions = $model::all(['id', 'parent_id', 'slug'])->keyBy('id');
/** @var \Illuminate\Database\Eloquent\Collection 此用户的所有角色的权限集合 */
$rolePermissions = $this->roles
->pluck('permissions')
->flatten()
->keyBy('id');
$abilities = $rolePermissions->map(fn ($permission) => $permission->slug);
foreach ($rolePermissions as $rolePermission) {
if (is_null($rolePermission->parent_id) || $abilities->has($rolePermission->parent_id)) {
continue;
}
$parent = $allPermissions->get($rolePermission->parent_id);
while ($parent) {
$abilities->put($parent->id, $parent->slug);
if (is_null($parent->parent_id)) {
break;
}
$parent = $allPermissions->get($parent->parent_id);
}
}
unset($allPermissions, $rolePermissions);
return $this->allAbilities = $abilities;
}
public function can($abilities, $arguments = []): bool
{
if (empty($abilities)) {
return true;
}
if ($this->isAdministrator()) {
return true;
}
return collect($abilities)->every(
fn ($ability) => $this->allAbilities()->contains($ability)
);
}
public function allMenus(): Collection
{
$model = Admin::adminMenuModel();
$allMenus = $model::all()->keyBy($this->getKeyName());
if ($this->isAdministrator()) {
return $allMenus;
}
/** @var \Illuminate\Database\Eloquent\Collection */
$roleMenus = $this->roles
->pluck('menus')
->flatten()
->keyBy($this->getKeyName());
$allRoleMenus = $roleMenus->collect();
foreach ($roleMenus as $roleMenu) {
if (is_null($roleMenu->parent_id) || $allRoleMenus->has($roleMenu->parent_id)) {
continue;
}
$parent = $allMenus->get($roleMenu->parent_id);
while ($parent) {
$allRoleMenus->put($parent->id, $parent);
if (is_null($parent->parent_id)) {
break;
}
$parent = $allMenus->get($parent->parent_id);
}
}
unset($allMenus, $roleMenus);
return $allRoleMenus;
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace App\ModelFilters;
namespace App\Models\Filters;
use Illuminate\Support\Arr;
use EloquentFilter\ModelFilter;

View File

@ -1,6 +1,6 @@
<?php
namespace App\ModelFilters;
namespace App\Models\Filters;
use EloquentFilter\ModelFilter;
use Illuminate\Support\Arr;

View File

@ -0,0 +1,31 @@
<?php
namespace App\Models\Filters;
use EloquentFilter\ModelFilter;
use App\Models\Keyword;
class KeywordFilter extends ModelFilter
{
/**
* 关键字
*/
public function name($name)
{
return $this->where('name','like', '%'.$name.'%')
->orWhere('key','like', '%'.$name.'%');
}
public function parentName($parent_name)
{
if(request('has_owner', 1)){
$this->where(function($q) use ($parent_name){
$q->where('name','like', '%'.$parent_name.'%')
->orWhere('key','like', '%'.$parent_name.'%');
});
}
return $this->orWhere('path','like', '%-'.
Keyword::where('name','like', '%'.$parent_name.'%')->orWhere('key','like', '%'.$parent_name.'%')->value('id')
. '-%' ?? '');
}
}

View File

@ -2,18 +2,16 @@
namespace App\Models;
use App\Admin\Components;
use App\ModelFilters\KeywordFilter;
use EloquentFilter\Filterable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use EloquentFilter\Filterable;
use App\Admin\Components;
use Illuminate\Support\Str;
class Keyword extends Model
{
use Filterable;
use HasFactory;
use Filterable;
protected $fillable = ['name', 'key', 'value', 'parent_id', 'parent_key', 'path', 'sort', 'lv'];
@ -25,27 +23,26 @@ class Keyword extends Model
protected static function boot()
{
parent::boot();
// 监听 Keyword 的创建事件,用于初始化 path 和 lv 字段值
static::saving(function (Keyword $keyword) {
if (is_null($parent = $keyword->parent)) {
$keyword->forceFill([
'path' => '-',
'lv' => 1,
]);
if ((string) $keyword->key === '') {
$keyword->key = Str::random(16);
static::saving(function ($keyword) {
// 如果创建的是一个根类目
if (! $keyword->parent_id) {
// 将层级设为 1
$keyword->lv = 1;
// 将 path 设为 -
$keyword->path = '-';
if(empty($keyword->key)){
$keyword->key = Str::quickRandom($length = 16);
}
} else {
$keyword->forceFill([
'parent_key' => $parent->lv > 1 ? $parent->parent_key : $parent->key,
'path' => $parent->full_path,
'lv' => $parent->lv + 1,
]);
if ((string) $keyword->key === '') {
$keyword->key = $parent->key.'_'.($parent->children()->count() + 1);
// 将层级设为父类目的层级 + 1
$keyword->lv = $keyword->parent->lv + 1;
$keyword->parent_key = $keyword->parent->key;
// 将 path 值设为父类目的 path 追加父类目 ID 以及最后跟上一个 - 分隔符
$keyword->path = $keyword->parent->path.$keyword->parent_id.'-';
//当前key是否为空
if(empty($keyword->key)){
$keyword->key = $keyword->parent_key . '_' . (self::where('parent_key', $keyword->parent_key)->count() + 1);
}
}
});
@ -61,32 +58,19 @@ class Keyword extends Model
return $this->hasMany(static::class, 'parent_id');
}
public function scopeSort($q)
{
return $q->orderBy('sort', 'asc');
}
public function scopeAllChildrenOfKey($q, $parentKey)
{
$q->where('path', 'like', '%-'.
$q->where('path','like', '%-'.
static::where('key', $parentKey)->value('id')
.'-%' ?? '');
. '-%' ?? '');
}
public static function tagsMap(string $key)
public static function tagsMap(String $key)
{
$mapArr = [];
self::query()->where('parent_key', $key)->get()->map(function ($item) use (&$mapArr) {
self::query()->where('parent_key', $key)->get()->map(function($item) use (&$mapArr){
$mapArr[$item->id] = Components::make()->keywordsTag($item->name, $item->value);
});
return $mapArr;
}
protected function fullPath(): Attribute
{
return Attribute::make(
get: fn (mixed $value, array $attributes) => $attributes['path'].$attributes['id'].'-',
);
}
}

View File

@ -2,10 +2,7 @@
namespace App\Providers;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
@ -14,7 +11,8 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
$this->app->singleton('admin.menu', \App\Admin\Menu::class);
//
}
/**
@ -22,24 +20,6 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
JsonResource::withoutWrapping();
$this->definePolymorphicTypes();
Validator::extend('phone', function ($attribute, $value, $parameters, $validator) {
return preg_match('/^1[\d]{10}$/', $value);
});
}
/**
* 自定义多态类型
*/
protected function definePolymorphicTypes(): void
{
Relation::enforceMorphMap(
collect([
\App\Models\AdminUser::class,
])->mapWithKeys(fn ($model) => [(new $model)->getTable() => $model])->all()
);
\Schema::defaultStringLength(191);
}
}

View File

@ -1,9 +1,9 @@
<?php
namespace App\Admin\Services;
namespace App\Services\Admin;
use App\Models\Ad;
use App\ModelFilters\AdFilter;
use App\Models\Filters\AdFilter;
use App\Traits\UploadTrait;
use Illuminate\Support\Arr;

View File

@ -1,6 +1,6 @@
<?php
namespace App\Admin\Services;
namespace App\Services\Admin;
use Illuminate\Support\Arr;
use Slowlyo\OwlAdmin\Admin;

View File

@ -1,9 +1,9 @@
<?php
namespace App\Admin\Services;
namespace App\Services\Admin;
use App\Models\Article;
use App\ModelFilters\ArticleFilter;
use App\Models\Filters\ArticleFilter;
use App\Traits\UploadTrait;
use Illuminate\Support\Arr;

View File

@ -0,0 +1,58 @@
<?php
namespace App\Services\Admin;
use Slowlyo\OwlAdmin\Services\AdminService;
/**
* @method Region getModel()
* @method Region|\Illuminate\Database\Query\Builder query()
*/
class BaseService extends AdminService
{
protected array $withRelationships = [];
protected string $modelFilterName = '';
protected bool $modelSortAble = false;
public function getTree()
{
$list = $this->query()->orderByDesc('sort')->get();
$minNum = $list->min('parent_id');
return array2tree($list->toArray(), $minNum);
}
public function getModelFilter()
{
return $this->modelFilterName;
}
public function listQuery()
{
$model = $this->getModel();
$filter = $this->getModelFilter();
$query = $this->query();
if($this->withRelationships){
$query->with($this->withRelationships);
}
if ($filter) {
$query->filter(request()->input(), $filter);
}
if($this->modelSortAble){
$query->sort();
}
$this->sortable($query);
return $query;
}
public function getDetail($id)
{
return $this->query()->with($this->withRelationships)->find($id);
}
}

View File

@ -1,11 +1,10 @@
<?php
namespace App\Admin\Services\System;
namespace App\Services\Admin;
use App\ModelFilters\KeywordFilter;
use App\Admin\Services\BaseService;
use App\Models\Keyword;
use Illuminate\Support\Arr;
use App\Models\Keyword;
use App\Models\Filters\KeywordFilter;
/**
* @method Keyword getModel()
@ -14,15 +13,13 @@ use Illuminate\Support\Arr;
class KeywordService extends BaseService
{
protected string $modelName = Keyword::class;
protected string $modelFilterName = KeywordFilter::class;
public function getTree()
{
$list = $this->query()->sort()->filter(request()->all(), $this->modelFilterName)->get();
$list = $this->query()->filter(request()->all(), $this->modelFilterName)->orderByDesc('sort')->get();
$minNum = $list->min('parent_id');
return ! $list->isEmpty() ? array2tree($list->toArray(), $minNum) : [];
return !$list->isEmpty() ? array2tree($list->toArray(), $minNum) :[];
}
public function parentIsChild($id, $pid): bool
@ -56,7 +53,7 @@ class KeywordService extends BaseService
$model = $this->getModel();
foreach ($data as $k => $v) {
if (! in_array($k, $columns)) {
if (!in_array($k, $columns)) {
continue;
}
@ -78,7 +75,6 @@ class KeywordService extends BaseService
if ($pid != 0) {
if ($this->parentIsChild($primaryKey, $pid)) {
$this->setError('父级不允许设置为当前子权限');
return false;
}
}
@ -86,7 +82,7 @@ class KeywordService extends BaseService
$model = $this->query()->whereKey($primaryKey)->first();
foreach ($data as $k => $v) {
if (! in_array($k, $columns)) {
if (!in_array($k, $columns)) {
continue;
}
@ -98,11 +94,10 @@ class KeywordService extends BaseService
public function hasRepeated($data, $id = 0): bool
{
$query = $this->query()->when($id, fn ($query) => $query->where('id', '<>', $id));
$query = $this->query()->when($id, fn($query) => $query->where('id', '<>', $id));
if ((clone $query)->where('key', $data['key'])->exists()) {
$this->setError('KEY重复');
return true;
}
@ -112,7 +107,7 @@ class KeywordService extends BaseService
public function delete(string $ids): mixed
{
$ids = explode(',', $ids);
if (count($ids) == 1) {
if(count($ids) == 1){
$this->query()->where('path', 'like', '%-'.$ids[0].'-%')->delete();
}

View File

@ -63,10 +63,6 @@ trait UploadTrait
$path = $file->store(Admin::config('admin.upload.tem_directory.' . $type).'/'.date('Y-m-d'), Admin::config('admin.upload.disk'));
if (request()->has('full-url')) {
$path = Storage::disk(Admin::config('admin.upload.disk'))->url($path);
}
return $this->response()->success(['value' => $path]);
}

View File

@ -21,13 +21,7 @@ return [
'prefix' => 'admin-api',
'domain' => null,
'namespace' => 'App\\Admin\\Controllers',
'middleware' => [
\App\Admin\Middleware\Authenticate::class,
'admin.bootstrap',
\App\Admin\Middleware\CheckPermission::class,
'sanctum',
'substitute',
],
'middleware' => ['admin'],
// 不包含额外路由, 配置后, 不会追加新增/详情/编辑页面路由
'without_extra_routes' => [
'/dashboard',
@ -53,15 +47,16 @@ return [
'providers' => [
'admin' => [
'driver' => 'eloquent',
'model' => \App\Models\AdminUser::class,
'model' => \Slowlyo\OwlAdmin\Models\AdminUser::class,
],
],
'except' => [
],
],
'upload' => [
'disk' => env('FILESYSTEM_DISK', 'public'),
'disk' => env("FILESYSTEM_DISK", 'public'),
// 文件上传目录
'directory' => [
'image' => 'images',
@ -73,13 +68,13 @@ return [
'image' => 'temporary/images',
'file' => 'temporary/file',
'rich' => 'temporary/rich',
],
]
],
'https' => env('ADMIN_HTTPS', false),
// 是否显示 [开发者工具]
'show_development_tools' => env('ADMIN_SHOW_DEVELOPMENT_TOOLS', false),
'show_development_tools' => env('ADMIN_SHOW_DEVELOPMENT_TOOLS', true),
// 是否显示 [权限] 功能中的自动生成按钮
'show_auto_generate_permission_button' => env('ADMIN_SHOW_AUTO_GENERATE_PERMISSION_BUTTON', true),
@ -91,7 +86,7 @@ return [
'layout' => [
// 浏览器标题, 功能名称使用 %title% 代替
'title' => '%title% | 门店助手-管理后台',
'title' => '%title% | OwlAdmin',
'header' => [
// 是否显示 [刷新] 按钮
'refresh' => true,
@ -113,16 +108,16 @@ return [
*/
'keep_alive_exclude' => [],
// 底部信息
'footer' => '',
'footer' => '<a href="https://github.com/slowlyo/owl-admin" target="_blank">Owl Admin</a>',
],
'database' => [
'connection' => env('DB_CONNECTION', 'mysql'),
'connection' => env('ADMIN_DB_CONNECTION') ?? env('DB_CONNECTION', 'mysql'),
],
'models' => [
'admin_user' => \App\Models\AdminUser::class,
'admin_role' => \App\Models\AdminRole::class,
'admin_user' => \Slowlyo\OwlAdmin\Models\AdminUser::class,
'admin_role' => \Slowlyo\OwlAdmin\Models\AdminRole::class,
'admin_menu' => \Slowlyo\OwlAdmin\Models\AdminMenu::class,
'admin_permission' => \Slowlyo\OwlAdmin\Models\AdminPermission::class,
],

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('password_reset_tokens');
}
};

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('failed_jobs');
}
};

View File

@ -1,45 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('admin_menus', function (Blueprint $table) {
$table->string('slug')->unique()->nullable();
});
Schema::table('admin_permissions', function (Blueprint $table) {
$table->dropUnique(['name']);
$table->string('slug')->change();
});
Schema::create('admin_role_menus', function (Blueprint $table) {
$table->integer('role_id');
$table->integer('menu_id');
$table->index(['role_id', 'menu_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('admin_menus', function (Blueprint $table) {
$table->dropColumn(['slug']);
});
Schema::table('admin_permissions', function (Blueprint $table) {
$table->unique(['name']);
$table->string('slug', 50)->change();
});
Schema::dropIfExists('admin_role_menus');
}
};

View File

@ -0,0 +1,71 @@
<?php
namespace Database\Seeders;
use Slowlyo\OwlAdmin\Models\AdminMenu;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Throwable;
class AdminMenuSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
$menus = [
['title' => 'index', 'icon' => 'line-md:home-twotone-alt', 'url' => '/index', 'is_home'=>1, 'order'=>1],
['title' => 'admin_system', 'icon' => 'material-symbols:settings-outline', 'url' => '/system', 'order'=>2,
'children' => [
['title' => 'admin_users', 'icon' => 'ph:user-gear', 'url' => '/system/admin_users', 'order'=>1],
['title' => 'admin_roles', 'icon' => 'carbon:user-role', 'url' => '/system/admin_roles', 'order'=>2],
['title' => 'admin_permission', 'icon' => 'carbon:user-role', 'url' => '/system/admin_permissions', 'order'=>3],
['title' => 'admin_menu', 'icon' => 'fluent-mdl2:permissions', 'url' => '/system/admin_menus', 'order'=>4],
['title' => 'admin_setting', 'icon' => 'akar-icons:settings-horizontal', 'url' => '/system/settings', 'order'=>5],
['title' => 'keywords', 'icon' => 'ph:codesandbox-logo-light', 'url' => '/system/keywords', 'order'=>6]
],
],
['title' => 'web_content', 'icon' => 'ic:outline-collections-bookmark', 'url' => '', 'order'=>3,
'children' =>[
['title'=>'articles', 'icon'=>'ic:outline-article','url'=>'/articles', 'order'=>1],
['title'=>'ads', 'icon'=>'lets-icons:img-box','url'=>'/ads', 'order'=>2],
]
]
];
DB::table('admin_menus')->truncate();
try {
DB::begintransaction();
$this->createMenus($menus);
DB::commit();
} catch (Throwable $th) {
DB::rollBack();
report($th);
}
}
public function createMenus(array $menus, $pid = 0)
{
foreach ($menus as $menu) {
$mm = AdminMenu::create([
'title' => $menu['title'],
'icon' => $menu['icon'],
'url' => $menu['url'],
'parent_id' => $pid,
'url_type' => $menu['url_type'] ?? 1,
'visible' => $menu['visible'] ?? 1,
'is_home' => $menu['is_home'] ?? 0,
'order' => $menu['order'] ?? 0,
'is_full' => 0,
]);
if (isset($menu['children'])) {
$this->createMenus($menu['children'], $mm->id);
}
}
}
}

View File

@ -1,166 +0,0 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Slowlyo\OwlAdmin\Models\AdminMenu;
use Slowlyo\OwlAdmin\Models\AdminPermission;
class AdminPermissionSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$data = [
/*
|--------------------------------------------------------------------------
| 主页
|--------------------------------------------------------------------------
*/
'home' => [
'name' => '主页',
'icon' => 'line-md:home-twotone-alt',
'uri' => '/dashboard',
'is_home' => 1,
'children' => [],
'order' => 1,
],
'system' => [
'name' => '系统管理',
'icon' => 'material-symbols:settings-outline',
'uri' => '/system',
'children' => [
'admin_users' => [
'name' => '账号管理',
'icon' => 'ph:user-gear',
'uri' => '/system/admin_users',
'resource' => ['list', 'update'],
'children' => ['change_password' => '修改密码'],
],
'admin_roles' => [
'name' => '角色管理',
'icon' => 'carbon:user-role',
'uri' => '/system/admin_roles',
'resource' => ['list', 'create', 'update', 'delete'],
'children' => [
'set_menus' => '设置菜单',
'set_permissions' => '设置权限',
],
],
'admin_permissions' => [
'name' => '权限管理',
'icon' => 'fluent-mdl2:permissions',
'uri' => '/system/admin_permissions',
'resource' => ['list', 'create', 'update', 'delete'],
'children' => [],
],
'admin_menus' => [
'name' => '菜单管理',
'icon' => 'ant-design:menu-unfold-outlined',
'uri' => '/system/admin_menus',
'resource' => ['list', 'create', 'update', 'delete'],
'children' => [],
],
'settings' => [
'name' => '系统设置',
'icon' => 'akar-icons:settings-horizontal',
'uri' => '/system/settings',
'resource' => true,
'children' => [],
],
'keywords' => [
'name' => '数据字典',
'icon' => 'ph:codesandbox-logo-light',
'uri' => '/system/keywords',
'resource' => ['list', 'create', 'update', 'delete'],
'children' => [],
],
],
],
];
$this->handleAdminMenus($data);
$this->handleAdminPermissions($data);
}
public function handleAdminMenus(array $data, ?AdminMenu $parent = null): void
{
$sort = 0;
foreach ($data as $slug => $node) {
if (! is_array($node) || ! array_key_exists('uri', $node)) {
continue;
}
$sort++;
/** @var \Slowlyo\OwlAdmin\Models\AdminMenu */
$menu = AdminMenu::updateOrCreate([
'slug' => ($parent->slug ?? 'admin').'.'.$slug,
], [
'parent_id' => $parent->id ?? 0,
'order' => $sort,
'title' => $node['name'],
'icon' => $node['icon'],
'url' => $node['uri'],
'url_type' => $node['uri_type'] ?? 1,
'visible' => $node['visible'] ?? 1,
'is_home' => $node['is_home'] ?? 0,
'is_full' => $node['is_full'] ?? 0,
]);
$this->handleAdminMenus($node['children'] ?? [], $menu);
}
}
protected function handleAdminPermissions(array $data, ?AdminPermission $parent = null)
{
foreach ($data as $slug => $node) {
$permission = AdminPermission::updateOrCreate([
'slug' => ($parent->slug ?? 'admin').'.'.$slug,
], [
'parent_id' => $parent->id ?? 0,
'name' => is_array($node) ? $node['name'] : $node,
]);
if (! is_array($node)) {
continue;
}
// 资源路由权限
if (array_key_exists('resource', $node)) {
$resourceAbilities = [];
if (is_array($node['resource'])) {
$resourceAbilities = $node['resource'];
} elseif ($node['resource'] === true) {
$resourceAbilities = array_keys($this->resourceAbilityMap());
}
foreach ($resourceAbilities as $resourceAbility) {
AdminPermission::updateOrCreate([
'slug' => $permission->slug.'.'.$resourceAbility,
], [
'parent_id' => $permission->id,
'name' => $this->resourceAbilityMap()[$resourceAbility],
]);
}
}
$this->handleAdminPermissions($node['children'] ?? [], $permission);
}
}
protected function resourceAbilityMap(): array
{
return [
'list' => '列表',
'create' => '新增',
'update' => '编辑',
'delete' => '删除',
'view' => '查看',
];
}
}

View File

@ -1,38 +0,0 @@
<?php
namespace Database\Seeders;
use Slowlyo\OwlAdmin\Models\{AdminRole, AdminUser};
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class AdminSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
foreach ([
'admin_roles',
'admin_users',
'admin_role_users',
] as $table) {
DB::table($table)->truncate();
}
$roleAdministrator = AdminRole::create([
'name' => 'Administrator',
'slug' => 'administrator',
]);
$admin = AdminUser::create([
'username' => 'admin',
'password' => bcrypt('admin'),
]);
$admin->roles()->saveMany([$roleAdministrator]);
}
}

View File

@ -12,8 +12,7 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
$this->call(AdminSeeder::class);
$this->call(AdminPermissionSeeder::class);
$this->call(AdminMenuSeeder::class);
$this->call(KeywordSeeder::class);
}
}

View File

@ -24,7 +24,6 @@ return [
'delete' => '删除',
'copy' => '复制',
'confirm_delete' => '确认删除选中项?',
'confirm' => '是否确定?',
'back' => '返回',
'reset' => '重置',
'search' => '搜索',
@ -61,6 +60,7 @@ return [
'cancel' => '取消',
'please_login' => '请先登录',
'unauthorized' => '无权访问',
'user_disabled' => '用户已被禁用',
'code_generators' => [
'remark1' => '额外参数请参考',
@ -131,6 +131,10 @@ return [
'save_current_config' => '保存当前配置',
'input_config_name' => '请填写配置名称',
'same_name_tips' => '相同名称的配置将会被覆盖',
'save_path_dir' => '主应用',
'save_path_select' => '选择目录',
'save_path_select_tips' => '可选择项目根目录或插件根目录',
'save_path_label_prefix' => '插件 -> ',
],
'admin_users' => '管理员',
@ -138,13 +142,12 @@ return [
'avatar' => '头像',
'name' => '姓名',
'roles' => '角色',
'lock' => '锁定',
'edit_password' => '修改密码',
'search_username' => '搜索用户名/姓名',
'search_username' => '搜索用户名/名称',
'password_confirmation' => '两次输入密码不一致',
'old_password_required' => '请输入原密码',
'old_password_error' => '原密码错误',
'username_already_exists' => '用户名已存在',
'cannot_delete' => '不可删除超级管理员',
],
'admin_roles' => '角色',
@ -156,7 +159,8 @@ return [
'name_already_exists' => '角色名称已存在',
'slug_already_exists' => '角色标识已存在',
'set_permissions' => '设置权限',
'set_menus' => '设置菜单',
'cannot_delete' => '不可删除超级管理员',
'used' => '不可删除正在使用的角色',
],
'admin_permissions' => '权限',
@ -183,9 +187,12 @@ return [
'url' => '链接',
'visible' => '可见',
'type' => '类型',
'iframe' => 'Iframe',
'iframe_description' => '开启后页面将缓存,重新打开时不会重新加载',
'api' => '页面Api',
'api_description' => 'schemaApi, 页面初始化请求的api, 需要与Controller中的queryPath一致',
'route' => '路由',
'keep_alive' => '缓存页面',
'link' => '外链',
'class_name' => '类名',
'class_name_description' => '菜单的CSS类名, 一般用于自定义样式',
@ -198,11 +205,7 @@ return [
'parent_id_not_allow' => '父级菜单不允许设置为当前子菜单',
'component' => '组件',
'component_desc' => '默认为 amis , 非自定义前端页面请勿修改',
],
'keywords' => [
'search_name' => '名称/KEY',
'parent_keyword' => '父级关键字',
'url_exists' => '菜单路径重复',
],
'extensions' => [
@ -261,4 +264,43 @@ return [
'selected_rows_no_data' => '请选择要导出的数据',
'please_install_laravel_excel' => '请先安装 laravel-excel 扩展',
],
'keywords' => [
'search_name' => '名称/KEY',
'parent_keyword' => '父级关键字',
],
'articles' => [
'id' => '主键ID',
'title' => '标题',
'content' => '内容',
'cover' =>'封面',
'category' => '分类',
'tags' => '标签',
't_ids' => '标签',
'published_at' => '定时发布',
'published_at_g' => '发布时间',
'is_enable' => '显示',
'is_recommend' => '推荐',
'sort' => '排序',
'appendixes' => '附件',
'published_at_remark' => '*若未设置发布时间且操作设置为显示,则默认生成发布时间',
],
'ads' => [
'id' => 'ID',
'address' => '位置',
'resource' =>'内容',
'published_at' => '定时发布',
'published_at_g' => '发布时间',
'is_enable' => '显示',
'remark' => '备注',
'sort' => '排序',
'published_at_remark' => '*若未设置发布时间且操作设置为显示,则默认生成发布时间',
'jump_type' => '跳转类型',
'jump_config'=>'跳转配置',
'jump_config_arr'=>[
'web_link' => '网页地址',
'app_link' => '应用路径',
'mini_id' => '小程序ID',
'mini_link'=> '小程序路径'
],
]
];

View File

@ -13,4 +13,6 @@ use Illuminate\Support\Facades\Route;
|
*/
Route::redirect('/', '/admin');
Route::get('/', function () {
return view('welcome');
});