feat: new feature
This commit is contained in:
parent
17f2dc4d3c
commit
81fac9f62c
1
bootstrap/cache/services.php
vendored
1
bootstrap/cache/services.php
vendored
@ -67,6 +67,7 @@
|
||||
'Illuminate\\Contracts\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'Illuminate\\Contracts\\Bus\\QueueingDispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'Illuminate\\Bus\\BatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'Illuminate\\Bus\\DatabaseBatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'cache' => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
'cache.store' => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
'cache.psr6' => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
|
@ -13,15 +13,13 @@
|
||||
"ext-zip": "*",
|
||||
"doctrine/dbal": "^3.4",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"laravel/framework": "^9.33",
|
||||
"laravel/framework": "^9.44",
|
||||
"laravel/tinker": "^2.7",
|
||||
"tymon/jwt-auth": "dev-develop"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nette/php-generator": "^4.0",
|
||||
"nikic/php-parser": "^4.15",
|
||||
"pestphp/pest": "^1.22"
|
||||
},
|
||||
"autoload": {
|
||||
|
1560
composer.lock
generated
1560
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -232,7 +232,11 @@ HTML;
|
||||
*/
|
||||
protected function getUseList(): string
|
||||
{
|
||||
return 'const { data, query, search, reset, loading } = useGetList(api)';
|
||||
if ($this->hasPaginate) {
|
||||
return 'const { data, query, search, reset, loading } = useGetList(api)';
|
||||
} else {
|
||||
return 'const { data, query, search, reset, loading } = useGetList(api, false)';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,11 +114,7 @@ class Model extends Creator
|
||||
$modelName = Str::of($this->tableName)->camel();
|
||||
}
|
||||
|
||||
return $modelName->ucfirst()->whenContains('Model', function ($value) {
|
||||
return Str::of($value);
|
||||
}, function ($value) {
|
||||
return Str::of($value)->append('Model');
|
||||
})->toString();
|
||||
return $modelName->ucfirst()->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,4 +36,15 @@ enum DataRange: int implements Enum
|
||||
self::Department_DOWN_Data => '部门及以下数据',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* assert value
|
||||
*
|
||||
* @param int $value
|
||||
* @return bool
|
||||
*/
|
||||
public function assert(int $value): bool
|
||||
{
|
||||
return $this->value === $value;
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,17 @@ namespace Modules\Permissions\Exceptions;
|
||||
|
||||
use Catch\Enums\Code;
|
||||
use Catch\Exceptions\CatchException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PermissionForbidden extends CatchException
|
||||
{
|
||||
protected $message = 'permission forbidden';
|
||||
|
||||
protected $code = Code::PERMISSION_FORBIDDEN;
|
||||
|
||||
|
||||
public function statusCode(): int
|
||||
{
|
||||
return Response::HTTP_FORBIDDEN;
|
||||
}
|
||||
}
|
||||
|
@ -5,21 +5,20 @@ declare(strict_types=1);
|
||||
namespace Modules\Permissions\Http\Controllers;
|
||||
|
||||
use Catch\Base\CatchController as Controller;
|
||||
use Modules\Permissions\Models\DepartmentsModel;
|
||||
use Modules\Permissions\Models\Departments;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DepartmentsController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly DepartmentsModel $model
|
||||
protected readonly Departments $model
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function index(Request $request): mixed
|
||||
public function index(): mixed
|
||||
{
|
||||
return $this->model->getList();
|
||||
}
|
||||
|
@ -5,13 +5,13 @@ declare(strict_types=1);
|
||||
namespace Modules\Permissions\Http\Controllers;
|
||||
|
||||
use Catch\Base\CatchController as Controller;
|
||||
use Modules\Permissions\Models\JobsModel;
|
||||
use Modules\Permissions\Models\Jobs;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class JobsController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly JobsModel $model
|
||||
protected readonly Jobs $model
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,20 @@ declare(strict_types=1);
|
||||
namespace Modules\Permissions\Http\Controllers;
|
||||
|
||||
use Catch\Base\CatchController as Controller;
|
||||
use Catch\Exceptions\FailedException;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Permissions\Enums\MenuType;
|
||||
use Modules\Permissions\Models\PermissionsModel;
|
||||
use Modules\Permissions\Models\Permissions;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
|
||||
class PermissionsController extends Controller
|
||||
{
|
||||
/**
|
||||
* @param Permissions $model
|
||||
*/
|
||||
public function __construct(
|
||||
protected readonly PermissionsModel $model
|
||||
protected readonly Permissions $model
|
||||
) {
|
||||
}
|
||||
|
||||
@ -21,28 +27,53 @@ class PermissionsController extends Controller
|
||||
*/
|
||||
public function index(): mixed
|
||||
{
|
||||
return $this->model->setBeforeGetList(function ($query){
|
||||
return $query->with('actions')->whereIn('type', [MenuType::Top->value(), MenuType::Menu->value]);
|
||||
return $this->model->setBeforeGetList(function ($query) {
|
||||
return $query->with('actions')->whereIn('type', [MenuType::Top->value(), MenuType::Menu->value()]);
|
||||
})->getList();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
return $this->model->storeBy($request->all());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $id
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
return $this->model->firstBy($id);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $id
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function update($id, Request $request)
|
||||
{
|
||||
return $this->model->updateBy($id, $request->all());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $id
|
||||
* @return bool|null
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
if ($this->model->where($this->model->getParentIdColumn(), $id)->first()) {
|
||||
throw new FailedException('无法进行删除,请先删除子级');
|
||||
}
|
||||
|
||||
return $this->model->deleteBy($id);
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,14 @@ declare(strict_types=1);
|
||||
namespace Modules\Permissions\Http\Controllers;
|
||||
|
||||
use Catch\Base\CatchController as Controller;
|
||||
use Modules\Permissions\Models\RolesModel;
|
||||
use Catch\Exceptions\FailedException;
|
||||
use Modules\Permissions\Models\Roles;
|
||||
use Modules\Permissions\Http\Requests\RoleRequest;
|
||||
|
||||
class RolesController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly RolesModel $model
|
||||
protected readonly Roles $model
|
||||
) {
|
||||
}
|
||||
|
||||
@ -20,8 +21,10 @@ class RolesController extends Controller
|
||||
*/
|
||||
public function index(): mixed
|
||||
{
|
||||
return $this->model->setBeforeGetList(function ($query){
|
||||
return $query->with(['permissions' => function($query){ $query->select('id');}]);
|
||||
return $this->model->setBeforeGetList(function ($query) {
|
||||
return $query->with(['permissions' => function ($query) {
|
||||
$query->select('id');
|
||||
}])->dataRange();
|
||||
})->getList();
|
||||
}
|
||||
|
||||
@ -46,6 +49,10 @@ class RolesController extends Controller
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
if ($this->model->where($this->model->getParentIdColumn(), $id)->first()) {
|
||||
throw new FailedException('请先删除子级');
|
||||
}
|
||||
|
||||
return $this->model->deleteBy($id);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace Modules\Permissions\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Modules\Permissions\Models\RolesModel;
|
||||
use Modules\Permissions\Models\Roles;
|
||||
|
||||
class RoleRequest extends FormRequest
|
||||
{
|
||||
@ -15,9 +15,9 @@ class RoleRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'role_name' => sprintf('required|unique:%s,%s,%s', RolesModel::class, 'role_name', $this->get('id')),
|
||||
'role_name' => sprintf('required|unique:%s,%s,%s', Roles::class, 'role_name', $this->get('id')),
|
||||
|
||||
'identify' => sprintf('required|alpha|unique:%s,%s,%s', RolesModel::class, 'role_name', $this->get('id')),
|
||||
'identify' => sprintf('required|alpha|unique:%s,%s,%s', Roles::class, 'role_name', $this->get('id')),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -4,16 +4,14 @@ namespace Modules\Permissions\Middlewares;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Permissions\Exceptions\PermissionForbidden;
|
||||
use Modules\Permissions\Models\LogOperate;
|
||||
use Modules\User\Models\User;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PermissionGate
|
||||
{
|
||||
public function handle(Request $request, \Closure $next)
|
||||
{
|
||||
if ($request->isMethod('get')) {
|
||||
// return $next($request);
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/* @var User $user */
|
||||
@ -25,17 +23,4 @@ class PermissionGate
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* terminate
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return void
|
||||
*/
|
||||
public function terminate(Request $request, Response $response): void
|
||||
{
|
||||
app(LogOperate::class)->log($request, $response);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use Catch\Base\CatchModel as Model;
|
||||
* @property $updated_at
|
||||
* @property $deleted_at
|
||||
*/
|
||||
class DepartmentsModel extends Model
|
||||
class Departments extends Model
|
||||
{
|
||||
protected $table = 'departments';
|
||||
|
||||
@ -47,4 +47,25 @@ class DepartmentsModel extends Model
|
||||
];
|
||||
|
||||
protected bool $asTree = true;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int|array $id
|
||||
* @return array
|
||||
*/
|
||||
public function findFollowDepartments(int|array $id): array
|
||||
{
|
||||
if (!is_array($id)) {
|
||||
$id = [$id];
|
||||
}
|
||||
|
||||
$followDepartmentIds = $this->whereIn($this->getParentIdColumn(), $id)->pluck('id')->toArray();
|
||||
|
||||
if (! empty($followDepartmentIds)) {
|
||||
$followDepartmentIds = array_merge($followDepartmentIds, $this->findFollowDepartments($followDepartmentIds));
|
||||
}
|
||||
|
||||
return $followDepartmentIds;
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ use Catch\Base\CatchModel as Model;
|
||||
* @property $updated_at
|
||||
* @property $deleted_at
|
||||
*/
|
||||
class JobsModel extends Model
|
||||
class Jobs extends Model
|
||||
{
|
||||
protected $table = 'jobs';
|
||||
|
@ -1,121 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Permissions\Models;
|
||||
|
||||
use Catch\CatchAdmin;
|
||||
use Catch\Traits\DB\BaseOperate;
|
||||
use Catch\Traits\DB\ScopeTrait;
|
||||
use Catch\Traits\DB\Trans;
|
||||
use Illuminate\Contracts\Http\Kernel;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Permissions\Exceptions\PermissionForbidden;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class LogOperate extends Model
|
||||
{
|
||||
use BaseOperate, Trans, ScopeTrait;
|
||||
|
||||
protected $table = 'log_operate';
|
||||
|
||||
protected $fillable = [
|
||||
'id',
|
||||
'module',
|
||||
'operate',
|
||||
'route',
|
||||
'params',
|
||||
'ip',
|
||||
'http_method',
|
||||
'http_method',
|
||||
'start_at',
|
||||
'time_taken',
|
||||
'creator_id',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return void
|
||||
*/
|
||||
public function log(Request $request, Response $response): void
|
||||
{
|
||||
if (! $response->isOk() && $response->exception instanceof PermissionForbidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = Auth::guard(getGuardName())->user();
|
||||
|
||||
$userModel = getAuthUserModel();
|
||||
|
||||
if (! $user instanceof $userModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$user->getAttribute('permissions')->each(function ($permission) use ($user, $request, $response) {
|
||||
if ($permission->isAction()) {
|
||||
[$controller, $action] = explode('@', $permission->permission_mark);
|
||||
|
||||
if (! CatchAdmin::getModuleControllerNamespace($permission->module).$controller.'Controller@'.$action == Route::currentRouteAction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requestStartAt = app(Kernel::class)->requestStartedAt()->timestamp;
|
||||
|
||||
$params = $request->all();
|
||||
// 如果参数过长则不记录
|
||||
if (!empty($params)) {
|
||||
if (strlen($encodeParams = \json_encode($params, JSON_UNESCAPED_UNICODE)) > 5000) {
|
||||
$params = [];
|
||||
}
|
||||
}
|
||||
|
||||
$this->storeBy([
|
||||
'module' => $permission->module,
|
||||
|
||||
'operate' => $permission->permission_name,
|
||||
|
||||
'route' => $permission->permission_mark,
|
||||
|
||||
'creator_id' => $user->id,
|
||||
|
||||
'http_method' => $request->method(),
|
||||
|
||||
'http_code' => $response->getStatusCode(),
|
||||
|
||||
'start_at' => $requestStartAt,
|
||||
|
||||
'time_taken' => time() - $requestStartAt,
|
||||
|
||||
'ip' => $request->ip(),
|
||||
|
||||
'params' => !empty($params) ? $encodeParams : '',
|
||||
|
||||
'created_at' => time()
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Attribute
|
||||
*/
|
||||
protected function timeTaken(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
get: fn($value) => $value . 's',
|
||||
);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Modules\Permissions\Models;
|
||||
|
||||
use Catch\Base\CatchModel as Model;
|
||||
use Catch\CatchAdmin;
|
||||
use Catch\Enums\Status;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Modules\Permissions\Enums\MenuStatus;
|
||||
@ -29,7 +30,7 @@ use Modules\Permissions\Enums\MenuType;
|
||||
* @property $updated_at
|
||||
* @property $deleted_at
|
||||
*/
|
||||
class PermissionsModel extends Model
|
||||
class Permissions extends Model
|
||||
{
|
||||
protected $table = 'permissions';
|
||||
|
||||
@ -58,6 +59,22 @@ class PermissionsModel extends Model
|
||||
|
||||
protected $hidden = ['pivot'];
|
||||
|
||||
/**
|
||||
* default permission actions
|
||||
*
|
||||
* @var array|string[]
|
||||
*/
|
||||
protected array $defaultActions = [
|
||||
'index' => '列表',
|
||||
'store' => '新增',
|
||||
'show' => '读取',
|
||||
'update' => '更新',
|
||||
'destroy' => '删除',
|
||||
'enable' => '禁用/启用',
|
||||
'import' => '导入',
|
||||
'export' => '导出',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
@ -103,6 +120,16 @@ class PermissionsModel extends Model
|
||||
return $this->type == MenuType::Top;
|
||||
}
|
||||
|
||||
/**
|
||||
* is menu
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isMenu(): bool
|
||||
{
|
||||
return $this->type == MenuType::Menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* actions
|
||||
*
|
||||
@ -117,29 +144,73 @@ class PermissionsModel extends Model
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function storeBy(array $data): bool
|
||||
{
|
||||
$model = $this->fill($data);
|
||||
if ($data['actions'] ?? false) {
|
||||
/* @var static $parentMenu */
|
||||
$parentMenu = $this->firstBy(value: $data['parent_id'], field: 'id');
|
||||
|
||||
if ($model->isAction()) {
|
||||
$parentMenu = $this->firstBy($model->parent_id, 'id');
|
||||
$model->setAttribute('module', $parentMenu->module);
|
||||
$model->setAttribute('permission_mark', $parentMenu->permission_mark . '@' . $data['permission_mark']);
|
||||
$model->setAttribute('route', '');
|
||||
$model->setAttribute('icon', '');
|
||||
$model->setAttribute('component', '');
|
||||
$model->setAttribute('redirect', '');
|
||||
return $model->setCreatorId()->save();
|
||||
}
|
||||
if (! $parentMenu->isMenu()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($model->isTopMenu()) {
|
||||
$data['route'] = '/' . trim($data['route'], '/');
|
||||
}
|
||||
$actions = CatchAdmin::getControllerActions($parentMenu->module, $parentMenu->permission_mark);
|
||||
foreach ($actions as $k => $action) {
|
||||
if (! isset($this->defaultActions[$action])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->addAction($this->newInstance([
|
||||
'type' => MenuType::Action->value(),
|
||||
'parent_id' => $data['parent_id'],
|
||||
'permission_name' => $this->defaultActions[$action],
|
||||
'permission_mark' => $action,
|
||||
'sort' => $k + 1
|
||||
]), $parentMenu);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$model = $this->fill($data);
|
||||
|
||||
if ($model->isAction()) {
|
||||
$parentMenu = $this->firstBy($model->parent_id, 'id');
|
||||
return $this->addAction($model, $parentMenu);
|
||||
}
|
||||
|
||||
if ($model->isTopMenu()) {
|
||||
$data['route'] = '/'.trim($data['route'], '/');
|
||||
}
|
||||
|
||||
return parent::storeBy($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* add action
|
||||
*
|
||||
* @param $model
|
||||
* @param Permissions $parent
|
||||
* @return mixed
|
||||
*/
|
||||
protected function addAction($model, mixed $parent): mixed
|
||||
{
|
||||
$model->setAttribute('module', $parent->module);
|
||||
$model->setAttribute('permission_mark', $parent->permission_mark. '@'. $model->permission_mark);
|
||||
$model->setAttribute('route', '');
|
||||
$model->setAttribute('icon', '');
|
||||
$model->setAttribute('component', '');
|
||||
$model->setAttribute('redirect', '');
|
||||
|
||||
if ($this->where('module', $model->getAttribute('module'))->where('permission_mark', $model->getAttribute('permission_mark'))->first()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $model->setCreatorId()->save();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* update data
|
||||
@ -153,9 +224,9 @@ class PermissionsModel extends Model
|
||||
$model = $this->fill($data);
|
||||
|
||||
if ($model->isAction()) {
|
||||
/* @var PermissionsModel $parentMenu */
|
||||
$parentMenu = $this->firstBy($model->parent_id, 'id');
|
||||
$data['permission_mark'] = $parentMenu->permission_mark . '@' . $data['permission_mark'];
|
||||
/* @var Permissions $parentMenu */
|
||||
$parentMenu = $this->firstBy($model->parent_id, 'id');
|
||||
$data['permission_mark'] = $parentMenu->permission_mark.'@'.$data['permission_mark'];
|
||||
}
|
||||
|
||||
return parent::updateBy($id, $data);
|
@ -6,6 +6,7 @@ namespace Modules\Permissions\Models;
|
||||
|
||||
use Catch\Base\CatchModel as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Modules\Permissions\Models\Traits\DataRange;
|
||||
|
||||
/**
|
||||
* @property $role_name
|
||||
@ -18,8 +19,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
* @property $updated_at
|
||||
* @property $deleted_at
|
||||
*/
|
||||
class RolesModel extends Model
|
||||
class Roles extends Model
|
||||
{
|
||||
use DataRange;
|
||||
|
||||
protected $table = 'roles';
|
||||
|
||||
protected $fillable = ['id', 'role_name', 'identify', 'parent_id', 'description', 'data_range', 'creator_id', 'created_at', 'updated_at', 'deleted_at'];
|
||||
@ -59,6 +62,17 @@ class RolesModel extends Model
|
||||
*/
|
||||
public function permissions(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(PermissionsModel::class, 'role_has_permissions', 'role_id', 'permission_id');
|
||||
return $this->belongsToMany(Permissions::class, 'role_has_permissions', 'role_id', 'permission_id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* departments
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function departments(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Departments::class, 'role_has_departments', 'role_id', 'department_id');
|
||||
}
|
||||
}
|
100
modules/Permissions/Models/Traits/DataRange.php
Normal file
100
modules/Permissions/Models/Traits/DataRange.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Permissions\Models\Traits;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Modules\Permissions\Models\Departments;
|
||||
use Modules\Permissions\Models\Roles;
|
||||
use Modules\Permissions\Enums\DataRange as DataRangeEnum;
|
||||
|
||||
trait DataRange
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $query
|
||||
* @param array|Collection $roles
|
||||
* @return mixed
|
||||
*/
|
||||
public function scopeDataRange($query, array|Collection $roles = []): mixed
|
||||
{
|
||||
$currenUser = Auth::guard(getGuardName())->user();
|
||||
|
||||
if ($currenUser->isSuperAdmin()) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$userIds = $this->getDepartmentUserIdsBy($roles, $currenUser);
|
||||
|
||||
if (empty($userIds)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
return $query->whereIn($this->aliasField('creator_id'), $userIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* get department ids
|
||||
*
|
||||
* @param array $roles
|
||||
* @param $currentUser
|
||||
* @return Collection
|
||||
*/
|
||||
public function getDepartmentUserIdsBy(array $roles, $currentUser): Collection
|
||||
{
|
||||
$userIds = Collection::make();
|
||||
|
||||
if (empty($roles)) {
|
||||
$roles = $currentUser->roles()->get();
|
||||
}
|
||||
|
||||
/* @var Roles $role */
|
||||
foreach ($roles as $role) {
|
||||
if (DataRangeEnum::All_Data->assert($role->data_range)) {
|
||||
return Collection::make();
|
||||
}
|
||||
|
||||
if (DataRangeEnum::Personal_Choose->assert($role->data_range)) {
|
||||
$userIds = $userIds->merge($this->getUserIdsByDepartmentId($role->departments()->pluck('id')));
|
||||
}
|
||||
|
||||
if (DataRangeEnum::Personal_Data->assert($role->data_range)) {
|
||||
$userIds = $userIds->push($currentUser->id);
|
||||
}
|
||||
|
||||
if (DataRangeEnum::Department_Data->assert($role->data_range)) {
|
||||
$userIds = $userIds->merge(
|
||||
$this->getUserIdsByDepartmentId([$currentUser->department_id])
|
||||
);
|
||||
}
|
||||
|
||||
if (DataRangeEnum::Department_DOWN_Data->assert($role->data_range)) {
|
||||
$departmentsId = [$currentUser->department_id];
|
||||
|
||||
$departmentModel = new Departments();
|
||||
|
||||
$departmentIds = $departmentModel->findFollowDepartments($departmentsId);
|
||||
|
||||
$userIds = $userIds->merge($this->getUserIdsByDepartmentId($departmentIds))->push($currentUser->id);
|
||||
}
|
||||
}
|
||||
|
||||
return $userIds->unique();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get user ids by department is
|
||||
*
|
||||
* @param array|Collection $departmentIds
|
||||
* @return Collection
|
||||
*/
|
||||
protected function getUserIdsByDepartmentId(array|Collection $departmentIds): Collection
|
||||
{
|
||||
$userModel = app(getAuthUserModel());
|
||||
|
||||
return $userModel->whereIn('department_id', $departmentIds)->pluck('id');
|
||||
}
|
||||
}
|
@ -5,24 +5,17 @@ namespace Modules\Permissions\Providers;
|
||||
use Catch\CatchAdmin;
|
||||
use Catch\Providers\CatchModuleServiceProvider;
|
||||
use Modules\Permissions\Middlewares\PermissionGate;
|
||||
use Modules\Permissions\Models\LogOperate;
|
||||
|
||||
class PermissionsServiceProvider extends CatchModuleServiceProvider
|
||||
{
|
||||
/**
|
||||
* register permission gate
|
||||
* middlewares
|
||||
*
|
||||
* @throws \Psr\Container\ContainerExceptionInterface
|
||||
* @throws \Psr\Container\NotFoundExceptionInterface
|
||||
* @return string[]
|
||||
*/
|
||||
|
||||
protected function registering()
|
||||
protected function middlewares(): array
|
||||
{
|
||||
$route = $this->app['config']->get('catch.route');
|
||||
|
||||
$route['middlewares'][] = PermissionGate::class;
|
||||
|
||||
$this->app['config']->set('catch.route', $route);
|
||||
return [PermissionGate::class];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +28,4 @@ class PermissionsServiceProvider extends CatchModuleServiceProvider
|
||||
// TODO: Implement path() method.
|
||||
return CatchAdmin::getModuleRoutePath('Permissions');
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ import { useOpen } from '/admin/composables/curd/useOpen'
|
||||
|
||||
const api = 'permissions/departments'
|
||||
|
||||
const { data, query, search, reset, loading } = useGetList(api)
|
||||
const { data, query, search, reset, loading } = useGetList(api, false)
|
||||
const { destroy, deleted } = useDestroy()
|
||||
const { open, close, title, visible, id } = useOpen()
|
||||
|
||||
|
@ -18,8 +18,7 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单名称" prop="permission_name" :rules="[{ required: true, message: '菜单名称必须填写' }]">
|
||||
<Select v-model="formData.permission_name" name="permission_name" allow-create :options="actionMenuNames" v-if="isAction" />
|
||||
<el-input v-model="formData.permission_name" name="permission_name" clearable v-else />
|
||||
<el-input v-model="formData.permission_name" name="permission_name" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属模块" prop="module" :rules="[{ required: true, message: '所属模块必须填写' }]" v-if="!isAction">
|
||||
<Select v-model="formData.module" api="modules" @clear="clearModule" />
|
||||
@ -39,7 +38,7 @@
|
||||
<el-cascader :options="permissions" name="parent_id" v-model="formData.parent_id" clearable :props="{ value: 'id', label: 'permission_name', checkStrictly: true }" class="w-full" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限标识" prop="permission_mark" :rules="[{ required: true, message: '权限标识必须填写' }]" v-if="!isTop">
|
||||
<Select v-model="formData.permission_mark" name="permission_mark" :options="actionMenuMark" allow-create v-if="isAction" />
|
||||
<el-input v-model="formData.permission_mark" name="permission_mark" clearable v-if="isAction" />
|
||||
<Select v-model="formData.permission_mark" placeholder="请选择" api="controllers" :query="{ module: formData.module }" v-else />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单Icon" prop="icon" v-if="!isAction">
|
||||
@ -174,28 +173,6 @@ const clearModule = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 当菜单是按钮类型时, 定义两个初始值
|
||||
const actionMenuNames = [
|
||||
{ label: '列表', value: '列表' },
|
||||
{ label: '新增', value: '新增' },
|
||||
{ label: '读取', value: '读取' },
|
||||
{ label: '更新', value: '更新' },
|
||||
{ label: '删除', value: '删除' },
|
||||
{ label: '禁用/启用', value: '禁用/启用' },
|
||||
{ label: '导入', value: '导入' },
|
||||
{ label: '导出', value: '导出' },
|
||||
]
|
||||
|
||||
const actionMenuMark = [
|
||||
{ label: 'index', value: 'index' },
|
||||
{ label: 'store', value: 'store' },
|
||||
{ label: 'show', value: 'show' },
|
||||
{ label: 'update', value: 'update' },
|
||||
{ label: 'destroy', value: 'destroy' },
|
||||
{ label: 'enable', value: 'enable' },
|
||||
{ label: 'import', value: 'import' },
|
||||
{ label: 'export', value: 'export' },
|
||||
]
|
||||
// 创建前的钩子
|
||||
beforeCreate.value = () => {
|
||||
formData.value.parent_id = getParent(formData.value.parent_id)
|
||||
|
@ -12,16 +12,26 @@
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading" row-key="id" default-expand-all :tree-props="{ children: 'children' }">
|
||||
<el-table-column prop="permission_name" label="菜单名称" />
|
||||
<el-table-column prop="route" label="菜单路由" />
|
||||
<el-table-column prop="permission_mark" label="权限标识" width="300">
|
||||
<el-table-column prop="permission_mark" label="权限标识" width="330">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.actions.length" class="flex grid gap-1 grid-cols-4">
|
||||
<el-tag v-for="action in scope.row.actions" class="cursor-pointer min-w-fit" @click="open(action.id)" closable @close="destroy(api, action.id)">{{ action.permission_name }}</el-tag>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-popconfirm confirm-button-text="确认" title="添加基础actions" @confirm="actionGenerate(scope.row.id)" placement="top">
|
||||
<template #reference>
|
||||
<el-tag class="cursor-pointer w-8" v-if="scope.row.type === MenuType.PAGE_TYPE">
|
||||
<Icon name="cog-6-tooth" class="animate-spin w-5 h-5" v-if="actionLoading" />
|
||||
<Icon name="plus" class="w-4 h-4" v-else />
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hidden" label="状态">
|
||||
<el-table-column prop="hidden" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<Status v-model="scope.row.hidden" :id="scope.row.id" :api="api" />
|
||||
<Status v-model="scope.row.hidden" :id="scope.row.id" :api="api" @refresh="search" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" />
|
||||
@ -41,16 +51,17 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import Create from './form/create.vue'
|
||||
import { useGetList } from '/admin/composables/curd/useGetList'
|
||||
import { useDestroy } from '/admin/composables/curd/useDestroy'
|
||||
import { useOpen } from '/admin/composables/curd/useOpen'
|
||||
import { MenuType } from '/admin/enum/app'
|
||||
import http from '../../../../resources/admin/support/http'
|
||||
|
||||
const api = 'permissions/permissions'
|
||||
|
||||
const { data, query, search, reset, loading } = useGetList(api)
|
||||
const { data, query, search, reset, loading } = useGetList(api, false)
|
||||
const { destroy, deleted } = useDestroy()
|
||||
const { open, close, title, visible, id } = useOpen()
|
||||
|
||||
@ -60,4 +71,13 @@ onMounted(() => {
|
||||
search()
|
||||
deleted(reset)
|
||||
})
|
||||
|
||||
const actionLoading = ref<boolean>(false)
|
||||
const actionGenerate = async (id: number) => {
|
||||
actionLoading.value = true
|
||||
http.post(api, { parent_id: id, actions: true }).then(r => {
|
||||
search()
|
||||
actionLoading.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
@ -43,7 +43,18 @@
|
||||
<el-form-item label="数据权限" prop="data_range">
|
||||
<Select v-model="formData.data_range" name="data_range" clearable api="dataRange" class="w-full" />
|
||||
</el-form-item>
|
||||
<el-form-item label="选择权限" prop="permissions">
|
||||
<el-form-item label="自定义权限" prop="department_ids" v-if="showDepartments">
|
||||
<el-cascader
|
||||
:options="departments"
|
||||
name="parent_id"
|
||||
v-model="formData.departmetn_ids"
|
||||
:show-all-levels="false"
|
||||
clearable
|
||||
:props="{ value: 'id', label: 'department_name', checkStrictly: true, multiple: true }"
|
||||
class="w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色权限" prop="permissions">
|
||||
<el-tree
|
||||
ref="permissionTree"
|
||||
v-model="formData.permissions"
|
||||
@ -66,7 +77,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCreate } from '/admin/composables/curd/useCreate'
|
||||
import { useShow } from '/admin/composables/curd/useShow'
|
||||
import { nextTick, onMounted, ref, unref } from 'vue'
|
||||
import { nextTick, onMounted, ref, unref, watch } from 'vue'
|
||||
import http from '/admin/support/http'
|
||||
|
||||
const props = defineProps({
|
||||
@ -75,6 +86,8 @@ const props = defineProps({
|
||||
hasPermissions: Array<Object>,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const { formData, form, loading, submitForm, close, beforeCreate, beforeUpdate } = useCreate(props.api, props.primary)
|
||||
|
||||
if (props.primary) {
|
||||
@ -92,11 +105,14 @@ if (props.primary) {
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const roles = ref()
|
||||
const permissions = ref()
|
||||
// 权限树对象
|
||||
const permissionTree = ref()
|
||||
// 部门
|
||||
const departments = ref()
|
||||
const showDepartments = ref<boolean>(false)
|
||||
|
||||
const permissionLoadingText = ref<string>('加载中...')
|
||||
const getPermissions = async (value: number = 0) => {
|
||||
if (value) {
|
||||
@ -129,10 +145,23 @@ const getRoles = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const getDepartments = () => {
|
||||
http.get('permissions/departments').then(r => {
|
||||
departments.value = r.data.data
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
getRoles()
|
||||
getPermissions()
|
||||
getDepartments()
|
||||
close(() => emit('close'))
|
||||
watch(
|
||||
formData,
|
||||
function (value) {
|
||||
showDepartments.value = value.data_range === 2
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
})
|
||||
|
||||
const selectPermissions = (checkedNodes, checkedKeys) => {
|
||||
|
@ -39,7 +39,7 @@ import { useOpen } from '/admin/composables/curd/useOpen'
|
||||
|
||||
const api = 'permissions/roles'
|
||||
|
||||
const { data, query, search, reset, loading } = useGetList(api)
|
||||
const { data, query, search, reset, loading } = useGetList(api, false)
|
||||
const { destroy, deleted } = useDestroy()
|
||||
const { open, close, title, visible, id } = useOpen()
|
||||
|
||||
|
@ -5,9 +5,13 @@ namespace Modules\User\Http\Controllers;
|
||||
use Catch\Base\CatchController as Controller;
|
||||
use Catch\Support\Module\ModuleRepository;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\User\Models\LogLogin;
|
||||
use Modules\User\Models\LogOperate;
|
||||
use Modules\User\Models\User;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
@ -113,12 +117,26 @@ class UserController extends Controller
|
||||
/**
|
||||
* login log
|
||||
* @param LogLogin $logLogin
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
* @throws \Psr\Container\ContainerExceptionInterface
|
||||
* @throws \Psr\Container\NotFoundExceptionInterface
|
||||
* @return LengthAwarePaginator
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function loginLog(LogLogin $logLogin)
|
||||
{
|
||||
return $logLogin->getUserLogBy($this->getLoginUser()->email);
|
||||
$user = $this->getLoginUser();
|
||||
|
||||
return $logLogin->getUserLogBy($user->isSuperAdmin() ? null : $user->email);
|
||||
}
|
||||
|
||||
public function operateLog(LogOperate $logOperate, Request $request)
|
||||
{
|
||||
$scope = $request->get('scope', 'self');
|
||||
|
||||
return $logOperate->setBeforeGetList(function ($builder) use ($scope){
|
||||
if ($scope == 'self') {
|
||||
return $builder->where('creator_id', $this->getLoginUserId());
|
||||
}
|
||||
return $builder;
|
||||
})->getList();
|
||||
}
|
||||
}
|
||||
|
33
modules/User/Middlewares/OperatingMiddleware.php
Normal file
33
modules/User/Middlewares/OperatingMiddleware.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\User\Middlewares;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Modules\User\Models\LogOperate;
|
||||
|
||||
class OperatingMiddleware
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next): mixed
|
||||
{
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
*/
|
||||
public function terminate(Request $request, Response $response): void
|
||||
{
|
||||
app(LogOperate::class)->log($request, $response);
|
||||
}
|
||||
}
|
@ -24,14 +24,15 @@ class LogLogin extends Model
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $email
|
||||
* @param ?string $email
|
||||
* @return LengthAwarePaginator
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function getUserLogBy(string $email): LengthAwarePaginator
|
||||
public function getUserLogBy(?string $email): LengthAwarePaginator
|
||||
{
|
||||
return self::query()->where('account', $email)
|
||||
->paginate(request()->get('limit', 10));
|
||||
return static::when($email, function ($query) use ($email){
|
||||
$query->where('account', $email);
|
||||
})->paginate(request()->get('limit', 10));
|
||||
}
|
||||
}
|
||||
|
90
modules/User/Models/LogOperate.php
Normal file
90
modules/User/Models/LogOperate.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\User\Models;
|
||||
|
||||
use Catch\CatchAdmin;
|
||||
use Catch\Traits\DB\BaseOperate;
|
||||
use Catch\Traits\DB\ScopeTrait;
|
||||
use Catch\Traits\DB\Trans;
|
||||
use Illuminate\Contracts\Http\Kernel;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class LogOperate extends Model
|
||||
{
|
||||
use BaseOperate, Trans, ScopeTrait;
|
||||
|
||||
protected $table = 'log_operate';
|
||||
|
||||
protected $fillable = [
|
||||
'id',
|
||||
'module',
|
||||
'action',
|
||||
'params',
|
||||
'ip',
|
||||
'http_method',
|
||||
'http_code',
|
||||
'start_at',
|
||||
'time_taken',
|
||||
'creator_id',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return void
|
||||
*/
|
||||
public function log(Request $request, Response $response): void
|
||||
{
|
||||
$user = Auth::guard(getGuardName())->user();
|
||||
|
||||
$userModel = getAuthUserModel();
|
||||
|
||||
if (! $user instanceof $userModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$module, $controller, $action] = CatchAdmin::parseFromRouteAction();
|
||||
|
||||
$requestStartAt = app(Kernel::class)->requestStartedAt()->getPreciseTimestamp(3);
|
||||
|
||||
$params = $request->all();
|
||||
// 如果参数过长则不记录
|
||||
if (!empty($params)) {
|
||||
if (strlen(\json_encode($params, JSON_UNESCAPED_UNICODE)) > 5000) {
|
||||
$params = [];
|
||||
}
|
||||
}
|
||||
|
||||
$timeTaken = intval(microtime(true) * 1000 - $requestStartAt);
|
||||
|
||||
$this->storeBy([
|
||||
'module' => $module,
|
||||
'action' => $controller . '@' . $action,
|
||||
'creator_id' => $user->id,
|
||||
'http_method' => $request->method(),
|
||||
'http_code' => $response->getStatusCode(),
|
||||
'start_at' => $requestStartAt,
|
||||
'time_taken' => $timeTaken,
|
||||
'ip' => $request->ip(),
|
||||
'params' => \json_encode($params, JSON_UNESCAPED_UNICODE),
|
||||
'created_at' => time()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Attribute
|
||||
*/
|
||||
protected function timeTaken(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($value) => $value > 1000 ? intval($value/1000) . 's' : $value . 'ms',
|
||||
);
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use Catch\Support\Module\ModuleRepository;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Permissions\Models\PermissionsModel;
|
||||
use Modules\Permissions\Models\Permissions;
|
||||
|
||||
trait UserRelations
|
||||
{
|
||||
@ -51,7 +51,7 @@ trait UserRelations
|
||||
*/
|
||||
public function withPermissions(): self
|
||||
{
|
||||
/* @var PermissionsModel $permissionsModel */
|
||||
/* @var Permissions $permissionsModel */
|
||||
$permissionsModel = app($this->getPermissionsModel());
|
||||
|
||||
if ($this->isSuperAdmin()) {
|
||||
@ -61,7 +61,7 @@ trait UserRelations
|
||||
|
||||
app($this->getRolesModel())->with(['permissions'])->get()
|
||||
|
||||
->each(function ($role) use (&$permissions){
|
||||
->each(function ($role) use (&$permissions) {
|
||||
$permissions = $permissions->concat($role->permissions);
|
||||
});
|
||||
|
||||
@ -87,7 +87,7 @@ trait UserRelations
|
||||
}
|
||||
|
||||
if ($this->isSuperAdmin()) {
|
||||
// return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->withPermissions();
|
||||
@ -108,34 +108,34 @@ trait UserRelations
|
||||
/**
|
||||
* get RolesModel
|
||||
*
|
||||
* @see \Modules\Permissions\Models\RolesModel
|
||||
* @see \Modules\Permissions\Models\Roles
|
||||
* @return string
|
||||
*/
|
||||
protected function getRolesModel(): string
|
||||
{
|
||||
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'RolesModel';
|
||||
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'Roles';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get JobsModel
|
||||
*
|
||||
* @see \Modules\Permissions\Models\JobsModel
|
||||
* @see \Modules\Permissions\Models\Jobs
|
||||
* @return string
|
||||
*/
|
||||
protected function getJobsModel(): string
|
||||
{
|
||||
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'JobsModel';
|
||||
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'Jobs';
|
||||
}
|
||||
|
||||
/**
|
||||
* get PermissionsModel
|
||||
*
|
||||
* @see \Modules\Permissions\Models\PermissionsModel
|
||||
* @return string
|
||||
*@see \Modules\Permissions\Models\Permissions
|
||||
*/
|
||||
protected function getPermissionsModel(): string
|
||||
{
|
||||
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'PermissionsModel';
|
||||
return '\\'.CatchAdmin::getModuleModelNamespace('permissions').'Permissions';
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use Catch\CatchAdmin;
|
||||
use Catch\Providers\CatchModuleServiceProvider;
|
||||
use Modules\User\Events\Login;
|
||||
use Modules\User\Listeners\Login as LoginListener;
|
||||
use Modules\User\Middlewares\OperatingMiddleware;
|
||||
|
||||
class UserServiceProvider extends CatchModuleServiceProvider
|
||||
{
|
||||
@ -24,9 +25,11 @@ class UserServiceProvider extends CatchModuleServiceProvider
|
||||
return CatchAdmin::getModuleRoutePath('user');
|
||||
}
|
||||
|
||||
|
||||
public function registerEvents(array $events): void
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected function middlewares(): array
|
||||
{
|
||||
parent::registerEvents($events); // TODO: Change the autogenerated stub
|
||||
return [OperatingMiddleware::class];
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,7 @@ use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
return new class () extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
@ -16,18 +15,17 @@ return new class extends Migration
|
||||
Schema::create('log_operate', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('module', 50)->comment('操作');
|
||||
$table->string('operate', 50)->comment('操作');
|
||||
$table->string('route', 50)->comment('路由');
|
||||
$table->string('action', 50)->comment('操作');
|
||||
$table->text('params')->comment('参数');
|
||||
$table->string('ip')->comment('ip 地址');
|
||||
$table->string('http_method', 10)->comment('http 请求方式');
|
||||
$table->string('http_code')->comment('http status code');
|
||||
$table->string('start_at')->comment('请求开始时间');
|
||||
$table->string('time_taken')->comment('请求消耗时间/s');
|
||||
$table->smallInteger('http_code')->comment('http status code');
|
||||
$table->unsignedInteger('start_at')->comment('请求开始时间');
|
||||
$table->smallInteger('time_taken')->comment('请求消耗时间/ms');
|
||||
$table->creatorId();
|
||||
$table->createdAt();
|
||||
|
||||
$table->engine='InnoDB';
|
||||
$table->engine = 'InnoDB';
|
||||
$table->comment('操作日志');
|
||||
});
|
||||
}
|
@ -13,3 +13,5 @@ Route::apiResource('users', UserController::class);
|
||||
Route::put('users/enable/{id}', [UserController::class, 'enable']);
|
||||
Route::match(['post', 'get'], 'user/online', [UserController::class, 'online']);
|
||||
Route::get('user/login/log', [UserController::class, 'loginLog']);
|
||||
Route::get('user/operate/log', [UserController::class, 'operateLog']);
|
||||
|
||||
|
@ -1,30 +1,20 @@
|
||||
<template>
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading">
|
||||
<el-table-column prop="account" label="账户" width="150px" />
|
||||
<el-table-column prop="browser" label="浏览器" width="100px" />
|
||||
<el-table-column prop="platform" label="平台" width="100px" />
|
||||
<el-table-column prop="login_ip" label="IP" width="120px" />
|
||||
<el-table-column prop="status" label="状态" width="100px">
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.status === 1">成功</el-tag>
|
||||
<el-tag type="danger" v-else>失败</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="login_at" label="登录时间" />
|
||||
</el-table>
|
||||
<div class="pl-2 pr-2 bg-white dark:bg-regal-dark rounded-lg mt-4 pb-6">
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading">
|
||||
<el-table-column prop="account" label="账户" width="150px" />
|
||||
<el-table-column prop="browser" label="浏览器" width="100px" />
|
||||
<el-table-column prop="platform" label="平台" width="100px" />
|
||||
<el-table-column prop="login_ip" label="IP" width="120px" />
|
||||
<el-table-column prop="status" label="状态" width="100px">
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.status === 1">成功</el-tag>
|
||||
<el-tag type="danger" v-else>失败</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="login_at" label="登录时间" />
|
||||
</el-table>
|
||||
|
||||
<div class="pt-2 pb-2 flex justify-end">
|
||||
<el-pagination
|
||||
background
|
||||
v-if="total > query.limit"
|
||||
layout="total,sizes,prev, pager,next"
|
||||
:current-page="query.page"
|
||||
:page-size="query.limit"
|
||||
@current-change="changePage"
|
||||
@size-change="changeLimit"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
/>
|
||||
<Paginate />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -34,12 +24,11 @@ import { useGetList } from '/admin/composables/curd/useGetList'
|
||||
|
||||
const api = 'user/login/log'
|
||||
|
||||
const { data, query, search, changePage, changeLimit, loading } = useGetList(api)
|
||||
const { data, query, search, reset, loading } = useGetList(api)
|
||||
|
||||
onMounted(() => search())
|
||||
|
||||
const tableData = computed(() => data.value?.data)
|
||||
const total = computed(() => data.value?.total)
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -1,28 +1,32 @@
|
||||
<template>
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading">
|
||||
<el-table-column prop="username" label="用户名" width="180" />
|
||||
<el-table-column prop="avatar" label="头像" width="180" />
|
||||
<el-table-column prop="email" label="邮箱" />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="scope">
|
||||
<Status v-model="scope.row.status" :id="scope.row.id" :api="api" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" />
|
||||
</el-table>
|
||||
|
||||
<div class="pt-2 pb-2 flex justify-end">
|
||||
<el-pagination
|
||||
background
|
||||
v-if="total > query.limit"
|
||||
layout="total,sizes,prev, pager,next"
|
||||
:current-page="query.page"
|
||||
:page-size="query.limit"
|
||||
@current-change="changePage"
|
||||
@size-change="changeLimit"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
/>
|
||||
<div class="pl-2 pr-2 bg-white dark:bg-regal-dark rounded-lg mt-2 pb-6">
|
||||
<div class="w-full flex justify-end">
|
||||
<el-radio-group v-model="query.scope" size="small" @change="search">
|
||||
<el-radio-button label="self">只看自己</el-radio-button>
|
||||
<el-radio-button label="all">全部</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading">
|
||||
<el-table-column prop="creator" label="创建人" />
|
||||
<el-table-column prop="module" label="模块" />
|
||||
<el-table-column prop="action" label="操作" width="150" />
|
||||
<el-table-column prop="http_method" label="请求方法" width="90" />
|
||||
<el-table-column prop="http_code" label="请求状态" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.http_code >= 200 && scope.row.http_code < 300"> {{ scope.row.http_code }}</el-tag>
|
||||
<el-tag type="danger" v-else>{{ scope.row.http_code }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="time_taken" label="耗时" />
|
||||
<el-table-column prop="params" label="参数">
|
||||
<template #default="scope">
|
||||
<el-tooltip class="box-item" effect="dark" :content="scope.row.params" placement="top-start">
|
||||
<el-button size="small" type="primary">查看</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<Paginate />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -30,14 +34,15 @@
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useGetList } from '/admin/composables/curd/useGetList'
|
||||
|
||||
const api = 'users'
|
||||
const api = 'user/operate/log'
|
||||
|
||||
const { data, query, search, reset, changePage, changeLimit, loading } = useGetList(api)
|
||||
const { data, query, search, reset, loading } = useGetList(api)
|
||||
|
||||
query.value.scope = 'self'
|
||||
|
||||
onMounted(() => search())
|
||||
|
||||
const tableData = computed(() => data.value?.data)
|
||||
const total = computed(() => data.value?.total)
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -45,7 +45,7 @@
|
||||
"unplugin-auto-import": "^0.12.1",
|
||||
"unplugin-icons": "^0.14.15",
|
||||
"unplugin-vue-components": "^0.22.12",
|
||||
"vite": "^4.0.1",
|
||||
"vite": "^4.0.2",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vue-tsc": "^1.0.13"
|
||||
}
|
||||
|
@ -1,22 +1,26 @@
|
||||
import http from '/admin/support/http'
|
||||
import {provide, ref, unref} from 'vue'
|
||||
import { provide, ref, unref } from 'vue'
|
||||
import { Code } from '/admin/enum/app'
|
||||
import Message from '/admin/support/message'
|
||||
|
||||
const initLimit = 10
|
||||
const initPage = 1;
|
||||
const initTotal = 10;
|
||||
const initPage = 1
|
||||
const initTotal = 10
|
||||
|
||||
// get table list
|
||||
export function useGetList(path: string) {
|
||||
export function useGetList(path: string, isPaginate: boolean = true) {
|
||||
const data = ref<object>()
|
||||
const page = ref<number>(initPage)
|
||||
const limit = ref<number>(initLimit)
|
||||
const total = ref<number>(initTotal)
|
||||
const query = ref<object>({
|
||||
page: page.value,
|
||||
limit: limit.value,
|
||||
})
|
||||
const query = ref<object>({})
|
||||
if (isPaginate) {
|
||||
query.value = Object.assign({
|
||||
page: page.value,
|
||||
limit: limit.value,
|
||||
})
|
||||
}
|
||||
|
||||
const loading = ref(true)
|
||||
// fetch list
|
||||
function getList() {
|
||||
@ -54,7 +58,9 @@ export function useGetList(path: string) {
|
||||
function reset() {
|
||||
resetPage()
|
||||
|
||||
query.value = Object.assign({ page: page.value, limit: limit.value })
|
||||
if (isPaginate) {
|
||||
query.value = Object.assign({ page: page.value, limit: limit.value })
|
||||
}
|
||||
|
||||
getList()
|
||||
}
|
||||
@ -68,7 +74,7 @@ export function useGetList(path: string) {
|
||||
}
|
||||
|
||||
function resetPage() {
|
||||
page.value = 1
|
||||
page.value = 1
|
||||
}
|
||||
|
||||
// change limit
|
||||
@ -84,7 +90,7 @@ export function useGetList(path: string) {
|
||||
}
|
||||
|
||||
// provider for paginate component
|
||||
provide('paginate', {page, limit, total, changePage, changeLimit})
|
||||
provide('paginate', { page, limit, total, changePage, changeLimit })
|
||||
|
||||
return { data, query, search, reset, loading }
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { isMiniScreen } from '/admin/support/Helper'
|
||||
|
||||
const isMobile = ref(isMiniScreen())
|
||||
const layoutSide = ' h-screen z-[1000] sm:z-0 absolute top-0 left-0 sm:fixed transition-width duration-300 ease-linear sider-bg'
|
||||
const layoutSide = ' h-screen z-[1000] sm:z-0 absolute top-0 left-0 sm:fixed transition-width duration-300 ease-linear sider-bg overflow-auto'
|
||||
const layoutSideOpenClass = 'w-56' + layoutSide
|
||||
const layoutSideHiddenClass = 'w-0 sm:w-16' + layoutSide
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user