瀏覽代碼

Add RBAC Module & More

1. 添加RBAC的权限&角色控制件;
2. 将用户‘is_admin’转换为角色;
3. 针对RBAC对管理页面进行定制化处理;
4. 加深layout分层处理;
5. 修改部分路由名称;
6. 分解路由文件至多文件;
兔姬桑 4 年之前
父節點
當前提交
2a4bf701f2
共有 64 個文件被更改,包括 3768 次插入3089 次删除
  1. 0 5
      app/Console/Commands/AutoJob.php
  2. 39 54
      app/Http/Controllers/Admin/PermissionController.php
  3. 47 55
      app/Http/Controllers/Admin/RoleController.php
  4. 2 11
      app/Http/Controllers/Admin/TicketController.php
  5. 54 40
      app/Http/Controllers/Admin/UserController.php
  6. 19 15
      app/Http/Controllers/Admin/UserGroupController.php
  7. 0 30
      app/Http/Controllers/AdminController.php
  8. 9 11
      app/Http/Controllers/AuthController.php
  9. 4 0
      app/Http/Controllers/UserController.php
  10. 13 6
      app/Http/Kernel.php
  11. 31 0
      app/Http/Middleware/Permission.php
  12. 0 28
      app/Http/Middleware/isAdmin.php
  13. 2 3
      app/Http/Requests/Admin/UserUpdateRequest.php
  14. 5 1
      app/Providers/AuthServiceProvider.php
  15. 18 1
      app/Providers/RouteServiceProvider.php
  16. 1 1
      app/Providers/TelescopeServiceProvider.php
  17. 50 40
      composer.lock
  18. 1 1
      config/permission.php
  19. 176 0
      database/migrations/2020_12_07_120247_permission_data.php
  20. 91 0
      resources/views/_layout.blade.php
  21. 6 2
      resources/views/admin/aff/detail.blade.php
  22. 27 15
      resources/views/admin/aff/index.blade.php
  23. 50 36
      resources/views/admin/article/index.blade.php
  24. 391 326
      resources/views/admin/config/config.blade.php
  25. 93 84
      resources/views/admin/config/emailFilter.blade.php
  26. 0 32
      resources/views/admin/config/profile.blade.php
  27. 22 11
      resources/views/admin/config/system.blade.php
  28. 19 7
      resources/views/admin/coupon/index.blade.php
  29. 244 230
      resources/views/admin/index.blade.php
  30. 63 56
      resources/views/admin/inviteList.blade.php
  31. 477 439
      resources/views/admin/layouts.blade.php
  32. 5 1
      resources/views/admin/logs/callback.blade.php
  33. 5 1
      resources/views/admin/logs/order.blade.php
  34. 5 1
      resources/views/admin/logs/traffic.blade.php
  35. 5 1
      resources/views/admin/logs/userBanHistory.blade.php
  36. 5 1
      resources/views/admin/logs/userTraffic.blade.php
  37. 47 41
      resources/views/admin/marketing/pushList.blade.php
  38. 23 11
      resources/views/admin/node/auth.blade.php
  39. 50 41
      resources/views/admin/node/cert/index.blade.php
  40. 199 167
      resources/views/admin/node/index.blade.php
  41. 102 0
      resources/views/admin/permission/index.blade.php
  42. 54 0
      resources/views/admin/permission/info.blade.php
  43. 109 0
      resources/views/admin/role/index.blade.php
  44. 125 0
      resources/views/admin/role/info.blade.php
  45. 57 44
      resources/views/admin/rule/group/index.blade.php
  46. 65 47
      resources/views/admin/rule/index.blade.php
  47. 9 5
      resources/views/admin/rule/log.blade.php
  48. 22 13
      resources/views/admin/shop/index.blade.php
  49. 16 6
      resources/views/admin/subscribe/index.blade.php
  50. 53 41
      resources/views/admin/ticket/index.blade.php
  51. 90 82
      resources/views/admin/ticket/reply.blade.php
  52. 60 53
      resources/views/admin/user/export.blade.php
  53. 50 40
      resources/views/admin/user/group/index.blade.php
  54. 11 12
      resources/views/admin/user/group/info.blade.php
  55. 81 51
      resources/views/admin/user/index.blade.php
  56. 54 57
      resources/views/admin/user/info.blade.php
  57. 20 103
      resources/views/auth/error.blade.php
  58. 75 150
      resources/views/auth/layouts.blade.php
  59. 40 120
      resources/views/auth/maintenance.blade.php
  60. 1 1
      resources/views/components/chat-unit.blade.php
  61. 218 301
      resources/views/user/layouts.blade.php
  62. 119 0
      routes/admin.php
  63. 38 0
      routes/user.php
  64. 1 158
      routes/web.php

+ 0 - 5
app/Console/Commands/AutoJob.php

@@ -149,11 +149,6 @@ class AutoJob extends Command
         if (sysConfig('is_traffic_ban')) {
             $trafficBanTime = sysConfig('traffic_ban_time');
             foreach (User::activeUser()->whereBanTime(null)->get() as $user) {
-                // 对管理员豁免
-                if ($user->is_admin) {
-                    continue;
-                }
-
                 // 多往前取5分钟,防止数据统计任务执行时间过长导致没有数据
                 if ($user->isTrafficWarning()) {
                     $user->update([

+ 39 - 54
app/Http/Controllers/Admin/PermissionController.php

@@ -5,82 +5,67 @@ namespace App\Http\Controllers\Admin;
 use App\Http\Controllers\Controller;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
+use Spatie\Permission\Models\Permission;
 
 class PermissionController extends Controller
 {
-    /**
-     * Display a listing of the resource.
-     *
-     * @return Response
-     */
     public function index()
     {
-        //
+        $permissions = Permission::query()->paginate(15);
+
+        return view('admin.permission.index', compact('permissions'));
     }
 
-    /**
-     * Show the form for creating a new resource.
-     *
-     * @return Response
-     */
     public function create()
     {
-        //
+        return view('admin.permission.info');
     }
 
-    /**
-     * Store a newly created resource in storage.
-     *
-     * @param  Request  $request
-     * @return Response
-     */
     public function store(Request $request)
     {
-        //
-    }
+        $validator = validator()->make($request->all(), ['name' => 'required', 'description' => 'required']);
 
-    /**
-     * Display the specified resource.
-     *
-     * @param  int  $id
-     * @return Response
-     */
-    public function show($id)
-    {
-        //
+        if ($validator->fails()) {
+            return redirect()->back()->withInput()->withErrors($validator->errors());
+        }
+
+        $permission = Permission::create($request->all());
+
+        if ($permission) {
+            return redirect()->route('admin.permission.edit', $permission)->with('successMsg', '操作成功');
+        }
+
+        return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    /**
-     * Show the form for editing the specified resource.
-     *
-     * @param  int  $id
-     * @return Response
-     */
-    public function edit($id)
+    public function edit(Permission $permission)
     {
-        //
+        return view('admin.permission.info', compact('permission'));
     }
 
-    /**
-     * Update the specified resource in storage.
-     *
-     * @param  Request  $request
-     * @param  int  $id
-     * @return Response
-     */
-    public function update(Request $request, $id)
+    public function update(Request $request, Permission $permission)
     {
-        //
+        $validator = validator()->make($request->all(), ['name' => 'required', 'description' => 'required']);
+
+        if ($validator->fails()) {
+            return redirect()->back()->withInput()->withErrors($validator->errors());
+        }
+
+        if ($permission->update($request->all())) {
+            return redirect()->back()->with('successMsg', '操作成功');
+        }
+
+        return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    /**
-     * Remove the specified resource from storage.
-     *
-     * @param  int  $id
-     * @return Response
-     */
-    public function destroy($id)
+    public function destroy(Permission $permission)
     {
-        //
+        try {
+            $permission->delete();
+        } catch (Exception $e) {
+            return Response::json(['status' => 'fail', 'message' => '删除失败,'.$e->getMessage()]);
+        }
+
+        return Response::json(['status' => 'success', 'message' => '清理成功']);
     }
 }

+ 47 - 55
app/Http/Controllers/Admin/RoleController.php

@@ -4,83 +4,75 @@ namespace App\Http\Controllers\Admin;
 
 use App\Http\Controllers\Controller;
 use Illuminate\Http\Request;
-use Illuminate\Http\Response;
+use Spatie\Permission\Models\Permission;
+use Spatie\Permission\Models\Role;
 
 class RoleController extends Controller
 {
-    /**
-     * Display a listing of the resource.
-     *
-     * @return Response
-     */
     public function index()
     {
-        //
+        $roles = Role::with('permissions')->paginate(15);
+
+        return view('admin.role.index', compact('roles'));
     }
 
-    /**
-     * Show the form for creating a new resource.
-     *
-     * @return Response
-     */
     public function create()
     {
-        //
+        $permissions = Permission::all()->pluck('description', 'name');
+
+        return view('admin.role.info', compact('permissions'));
     }
 
-    /**
-     * Store a newly created resource in storage.
-     *
-     * @param  Request  $request
-     * @return Response
-     */
     public function store(Request $request)
     {
-        //
-    }
+        $validator = validator()->make($request->all(), ['name' => 'required', 'description' => 'required']);
 
-    /**
-     * Display the specified resource.
-     *
-     * @param  int  $id
-     * @return Response
-     */
-    public function show($id)
-    {
-        //
+        if ($validator->fails()) {
+            return redirect()->back()->withInput()->withErrors($validator->errors());
+        }
+
+        $role = Role::create($request->except('permissions'));
+        $permissions = $request->input('permissions') ?: [];
+        if ($role->givePermissionTo($permissions)) {
+            return redirect()->route('admin.role.edit', $role)->with('successMsg', '操作成功');
+        }
+
+        return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    /**
-     * Show the form for editing the specified resource.
-     *
-     * @param  int  $id
-     * @return Response
-     */
-    public function edit($id)
+    public function edit(Role $role)
     {
-        //
+        $role->load('permissions');
+        $permissions = Permission::all()->pluck('description', 'name');
+
+        return view('admin.role.info', compact('role', 'permissions'));
     }
 
-    /**
-     * Update the specified resource in storage.
-     *
-     * @param  Request  $request
-     * @param  int  $id
-     * @return Response
-     */
-    public function update(Request $request, $id)
+    public function update(Request $request, Role $role)
     {
-        //
+        $validator = validator()->make($request->all(), ['name' => 'required', 'description' => 'required']);
+
+        if ($validator->fails()) {
+            return redirect()->back()->withInput()->withErrors($validator->errors());
+        }
+
+        $role->update($request->except('permissions'));
+        $permissions = $request->input('permissions') ?: [];
+        if ($role->syncPermissions($permissions)) {
+            return redirect()->back()->with('successMsg', '操作成功');
+        }
+
+        return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    /**
-     * Remove the specified resource from storage.
-     *
-     * @param  int  $id
-     * @return Response
-     */
-    public function destroy($id)
+    public function destroy(Role $role)
     {
-        //
+        try {
+            $role->delete();
+        } catch (Exception $e) {
+            return Response::json(['status' => 'fail', 'message' => '删除失败,'.$e->getMessage()]);
+        }
+
+        return Response::json(['status' => 'success', 'message' => '清理成功']);
     }
 }

+ 2 - 11
app/Http/Controllers/Admin/TicketController.php

@@ -104,17 +104,8 @@ class TicketController extends Controller
             $content = '标题:'.$ticket->title.'<br>管理员回复:'.$content;
 
             // 发通知邮件
-            if (! Auth::getUser()->is_admin) {
-                if (sysConfig('webmaster_email')) {
-                    $logId = Helpers::addNotificationLog($title, $content, 1, sysConfig('webmaster_email'));
-                    Mail::to(sysConfig('webmaster_email'))->send(new replyTicket($logId, $title, $content));
-                }
-                // 推送通知管理员
-                PushNotification::send($title, $content);
-            } else {
-                $logId = Helpers::addNotificationLog($title, $content, 1, $ticket->user->email);
-                Mail::to($ticket->user->email)->send(new replyTicket($logId, $title, $content));
-            }
+            $logId = Helpers::addNotificationLog($title, $content, 1, $ticket->user->email);
+            Mail::to($ticket->user->email)->send(new replyTicket($logId, $title, $content));
 
             return Response::json(['status' => 'success', 'message' => '回复成功']);
         }

+ 54 - 40
app/Http/Controllers/Admin/UserController.php

@@ -21,6 +21,7 @@ use Log;
 use Redirect;
 use Response;
 use Session;
+use Spatie\Permission\Models\Role;
 use Str;
 
 class UserController extends Controller
@@ -114,9 +115,18 @@ class UserController extends Controller
     // 添加账号页面
     public function create()
     {
+        if (Auth::getUser()->hasRole('Super Admin')) {
+            $roles = Role::all()->pluck('description', 'name');
+        } elseif (Auth::getUser()->hasPermissionTo('give roles')) {
+            $roles = Auth::getUser()->roles();
+        } else {
+            $roles = [];
+        }
+
         return view('admin.user.info', [
-            'levelList' => Level::orderBy('level')->get(),
-            'groupList' => UserGroup::orderBy('id')->get(),
+            'levels' => Level::orderBy('level')->get(),
+            'userGroups' => UserGroup::orderBy('id')->get(),
+            'roles' => $roles,
         ]);
     }
 
@@ -124,7 +134,7 @@ class UserController extends Controller
     public function store(UserStoreRequest $request): JsonResponse
     {
         try {
-            $data = $request->except('_token', 'uuid');
+            $data = $request->except('_token', 'uuid', 'roles');
             $data['password'] = $data['password'] ?? Str::random();
             $data['port'] = $data['port'] ?? Helpers::getPort();
             $data['passwd'] = $data['passwd'] ?? Str::random();
@@ -135,6 +145,12 @@ class UserController extends Controller
             $data['reset_time'] = $data['reset_time'] > date('Y-m-d') ? $data['reset_time'] : null;
             $user = User::create($data);
 
+            $roles = $request->input('roles');
+            if ($roles && (Auth::getUser()->hasPermissionTo('give roles') || Auth::getUser()->hasRole('Super Admin'))
+                || (in_array('Super Admin', $roles, true) && Auth::getUser()->hasRole('Super Admin'))) {
+                $user->assignRole($roles);
+            }
+
             if ($user) {
                 // 写入用户流量变动记录
                 Helpers::addUserTrafficModifyLog($user->id, null, 0, $data['transfer_enable'], '后台手动添加用户');
@@ -151,24 +167,29 @@ class UserController extends Controller
     }
 
     // 编辑账号页面
-    public function edit($id)
+    public function edit(User $user)
     {
-        $user = User::find($id);
+        if (Auth::getUser()->hasRole('Super Admin')) {
+            $roles = Role::all()->pluck('description', 'name');
+        } elseif (Auth::getUser()->hasPermissionTo('give roles')) {
+            $roles = Auth::getUser()->roles();
+        } else {
+            $roles = [];
+        }
 
         return view('admin.user.info', [
             'user' => $user->load('inviter:id,email'),
-            'levelList' => Level::orderBy('level')->get(),
-            'groupList' => UserGroup::orderBy('id')->get(),
+            'levels' => Level::orderBy('level')->get(),
+            'userGroups' => UserGroup::orderBy('id')->get(),
+            'roles' => $roles,
         ]);
     }
 
     // 编辑账号
-    public function update(UserUpdateRequest $request, $id)
+    public function update(UserUpdateRequest $request, User $user)
     {
-        $user = User::find($id);
-
         try {
-            $data = $request->except('_token', 'password', 'uuid', 'password', 'is_admin');
+            $data = $request->except('_token', 'password', 'uuid', 'password', 'roles');
             $data['passwd'] = $request->input('passwd') ?? Str::random();
             $data['vmess_id'] = $request->input('uuid') ?? Str::uuid();
             $data['transfer_enable'] *= GB;
@@ -176,6 +197,14 @@ class UserController extends Controller
             $data['expired_at'] = $data['expired_at'] ?? date('Y-m-d', strtotime('+365 days'));
             $data['remark'] = str_replace(['atob', 'eval'], '', $data['remark']);
 
+            // 只有超级管理员才能赋予超级管理员
+            $roles = $request->input('roles');
+
+            if ($roles && (Auth::getUser()->hasPermissionTo('give roles') || Auth::getUser()->hasRole('Super Admin'))
+                || (in_array('Super Admin', $roles, true) && Auth::getUser()->hasRole('Super Admin'))) {
+                $user->syncRoles($roles);
+            }
+
             // Input checking for dummy
             if ($data['enable'] === '1') {
                 if ($data['status'] === '-1' || $data['transfer_enable'] === 0 || $data['expired_at'] < date('Y-m-d')) {
@@ -183,20 +212,15 @@ class UserController extends Controller
                 }
             }
 
-            // 只有admin才有权限操作管理员属性
-            if (Auth::getUser()->is_admin === 1) {
-                $data['is_admin'] = (int) $request->input('is_admin');
-            }
-
             // 非演示环境才可以修改管理员密码
             $password = $request->input('password');
-            if (! empty($password) && ! (env('APP_DEMO') && $id === 1)) {
+            if (! empty($password) && ! (env('APP_DEMO') && $user->id === 1)) {
                 $data['password'] = $password;
             }
 
             // 写入用户流量变动记录
             if ($user->transfer_enable !== $data['transfer_enable']) {
-                Helpers::addUserTrafficModifyLog($id, null, $user->transfer_enable, $data['transfer_enable'], '后台手动编辑用户');
+                Helpers::addUserTrafficModifyLog($user->id, null, $user->transfer_enable, $data['transfer_enable'], '后台手动编辑用户');
             }
 
             if ($user->update($data)) {
@@ -212,26 +236,23 @@ class UserController extends Controller
     }
 
     // 删除用户
-    public function destroy($id)
+    public function destroy(User $user)
     {
-        if ($id <= 1) {
+        if ($user->id === 1) {
             return Response::json(['status' => 'fail', 'message' => '系统管理员不可删除']);
         }
 
         try {
-            DB::beginTransaction();
-
-            User::find($id)->delete();
-
-            DB::commit();
-
-            return Response::json(['status' => 'success', 'message' => '删除成功']);
+            if ($user->delete()) {
+                return Response::json(['status' => 'success', 'message' => '删除成功']);
+            }
         } catch (Exception $e) {
             Log::error('删除用户信息异常:'.$e->getMessage());
-            DB::rollBack();
 
-            return Response::json(['status' => 'fail', 'message' => '删除失败']);
+            return Response::json(['status' => 'fail', 'message' => '删除失败'.$e->getMessage()]);
         }
+
+        return Response::json(['status' => 'fail', 'message' => '删除失败']);
     }
 
     // 批量生成账号
@@ -262,16 +283,14 @@ class UserController extends Controller
     // 转换成某个用户的身份
     public function switchToUser(Request $request): JsonResponse
     {
-        $id = $request->input('user_id');
-
-        $user = User::find($id);
+        $user = User::find($request->input('user_id'));
         if (! $user) {
             return Response::json(['status' => 'fail', 'message' => '用户不存在']);
         }
 
         // 存储当前管理员ID,并将当前登录信息改成要切换的用户的身份信息
         Session::put('admin', Auth::id());
-        Auth::login($user);
+        Session::put('user', $user->id);
 
         return Response::json(['status' => 'success', 'message' => '身份切换成功']);
     }
@@ -312,14 +331,9 @@ class UserController extends Controller
     }
 
     // 导出配置信息
-    public function export(Request $request, $id)
+    public function export(Request $request, User $user)
     {
-        if (empty($id)) {
-            return Redirect::back();
-        }
-
-        $user = User::find($id);
-        if (empty($user)) {
+        if ($user === null) {
             return Redirect::back();
         }
 

+ 19 - 15
app/Http/Controllers/Admin/UserGroupController.php

@@ -26,15 +26,15 @@ class UserGroupController extends Controller
     // 添加用户分组页面
     public function create()
     {
-        $view['nodeList'] = Node::whereStatus(1)->get();
+        $nodes = Node::whereStatus(1)->pluck('name', 'id');
 
-        return view('admin.user.group.info', $view);
+        return view('admin.user.group.info', compact('nodes'));
     }
 
     // 添加用户分组
     public function store(Request $request): RedirectResponse
     {
-        $validator = Validator::make($request->all(), ['name' => 'required', 'nodes' => 'required']);
+        $validator = Validator::make($request->all(), ['name' => 'required']);
 
         if ($validator->fails()) {
             return Redirect::back()->withInput()->withErrors($validator->errors());
@@ -50,21 +50,25 @@ class UserGroupController extends Controller
     }
 
     // 编辑用户分组页面
-    public function edit($id)
+    public function edit(UserGroup $group)
     {
-        $view['userGroup'] = UserGroup::findOrFail($id);
-        $view['nodeList'] = Node::whereStatus(1)->get();
+        $nodes = Node::whereStatus(1)->pluck('name', 'id');
 
-        return view('admin.user.group.info', $view);
+        return view('admin.user.group.info', compact('group', 'nodes'));
     }
 
     // 编辑用户分组
-    public function update(Request $request, $id)
+    public function update(Request $request, UserGroup $group)
     {
-        $userGroup = UserGroup::findOrFail($id);
-        $userGroup->name = $request->input('name');
-        $userGroup->nodes = $request->input('nodes');
-        if ($userGroup->save()) {
+        $validator = Validator::make($request->all(), ['name' => 'required']);
+
+        if ($validator->fails()) {
+            return Redirect::back()->withInput()->withErrors($validator->errors());
+        }
+
+        $group->name = $request->input('name');
+        $group->nodes = $request->input('nodes');
+        if ($group->save()) {
             return Redirect::back()->with('successMsg', '操作成功');
         }
 
@@ -72,15 +76,15 @@ class UserGroupController extends Controller
     }
 
     // 删除用户分组
-    public function destroy($id): JsonResponse
+    public function destroy(UserGroup $group): JsonResponse
     {
         // 校验该分组下是否存在关联账号
-        if (User::whereGroupId($id)->count()) {
+        if (User::whereGroupId($group->id)->count()) {
             return Response::json(['status' => 'fail', 'message' => '该分组下存在关联账号,请先取消关联!']);
         }
 
         try {
-            UserGroup::whereId($id)->delete();
+            $group->delete();
         } catch (Exception $e) {
             return Response::json(['status' => 'fail', 'message' => '删除失败,'.$e->getMessage()]);
         }

+ 0 - 30
app/Http/Controllers/AdminController.php

@@ -67,31 +67,6 @@ class AdminController extends Controller
         return view('admin.index', $view);
     }
 
-    // 修改个人资料
-    public function profile(Request $request)
-    {
-        if ($request->isMethod('POST')) {
-            $new_password = $request->input('new_password');
-
-            if (! Hash::check($request->input('old_password'), Auth::getUser()->password)) {
-                return Redirect::back()->withErrors('旧密码错误,请重新输入');
-            }
-
-            if (Hash::check($new_password, Auth::getUser()->password)) {
-                return Redirect::back()->withErrors('新密码不可与旧密码一样,请重新输入');
-            }
-
-            $ret = Auth::getUser()->update(['password' => $new_password]);
-            if (! $ret) {
-                return Redirect::back()->withErrors('修改失败');
-            }
-
-            return Redirect::back()->with('successMsg', '修改成功');
-        }
-
-        return view('admin.config.profile');
-    }
-
     // 邀请码列表
     public function inviteList(Request $request)
     {
@@ -165,9 +140,4 @@ class AdminController extends Controller
 
         return view('admin.config.config', $view);
     }
-
-    public function getPort(): int
-    {
-        return Helpers::getPort();
-    }
 }

+ 9 - 11
app/Http/Controllers/AuthController.php

@@ -72,18 +72,16 @@ class AuthController extends Controller
             }
 
             // 校验普通用户账号状态
-            if (! $user->is_admin) {
-                if ($user->status < 0) {
-                    Auth::logout(); // 强制销毁会话,因为Auth::attempt的时候会产生会话
+            if ($user->status < 0) {
+                Auth::logout(); // 强制销毁会话,因为Auth::attempt的时候会产生会话
 
-                    return Redirect::back()->withInput()->withErrors(trans('auth.login_ban', ['email' => sysConfig('webmaster_email')]));
-                }
+                return Redirect::back()->withInput()->withErrors(trans('auth.login_ban', ['email' => sysConfig('webmaster_email')]));
+            }
 
-                if ($user->status === 0 && sysConfig('is_activate_account')) {
-                    Auth::logout(); // 强制销毁会话,因为Auth::attempt的时候会产生会话
+            if ($user->status === 0 && sysConfig('is_activate_account')) {
+                Auth::logout(); // 强制销毁会话,因为Auth::attempt的时候会产生会话
 
-                    return Redirect::back()->withInput()->withErrors(trans('auth.active_tip').'<a href="'.route('active').'?email='.$email.'" target="_blank"><span style="color:#000">【'.trans('auth.active_account').'】</span></a>');
-                }
+                return Redirect::back()->withInput()->withErrors(trans('auth.active_tip').'<a href="'.route('active').'?email='.$email.'" target="_blank"><span style="color:#000">【'.trans('auth.active_account').'】</span></a>');
             }
 
             // 写入登录日志
@@ -93,7 +91,7 @@ class AuthController extends Controller
             Auth::getUser()->update(['last_login' => time()]);
 
             // 根据权限跳转
-            if ($user->is_admin) {
+            if ($user->hasPermissionTo('admin.index')) {
                 return Redirect::route('admin.index');
             }
 
@@ -101,7 +99,7 @@ class AuthController extends Controller
         }
 
         if (Auth::check()) {
-            if (Auth::getUser()->is_admin) {
+            if (Auth::getUser()->hasPermissionTo('admin.index')) {
                 return Redirect::route('admin.index');
             }
 

+ 4 - 0
app/Http/Controllers/UserController.php

@@ -43,6 +43,10 @@ class UserController extends Controller
 {
     public function index()
     {
+        if (Session::has('user')) {
+            Auth::loginUsingId(Session::get('user'));
+            Session::forget('user');
+        }
         $user = Auth::getUser();
         $totalTransfer = $user->transfer_enable;
         $usedTransfer = $user->usedTraffic();

+ 13 - 6
app/Http/Kernel.php

@@ -6,12 +6,12 @@ use App\Http\Middleware\Affiliate;
 use App\Http\Middleware\Authenticate;
 use App\Http\Middleware\CheckForMaintenanceMode;
 use App\Http\Middleware\EncryptCookies;
-use App\Http\Middleware\isAdmin;
 use App\Http\Middleware\isAdminLogin;
 use App\Http\Middleware\isForbidden;
 use App\Http\Middleware\isLogin;
 use App\Http\Middleware\isMaintenance;
 use App\Http\Middleware\isSecurity;
+use App\Http\Middleware\Permission;
 use App\Http\Middleware\RedirectIfAuthenticated;
 use App\Http\Middleware\SetLocale;
 use App\Http\Middleware\TrimStrings;
@@ -33,7 +33,6 @@ use Illuminate\Routing\Middleware\ThrottleRequests;
 use Illuminate\Routing\Middleware\ValidateSignature;
 use Illuminate\Session\Middleware\StartSession;
 use Illuminate\View\Middleware\ShareErrorsFromSession;
-use Spatie\Permission\Middlewares\PermissionMiddleware;
 
 class Kernel extends HttpKernel
 {
@@ -71,6 +70,18 @@ class Kernel extends HttpKernel
             SubstituteBindings::class,
         ],
 
+        'user' => [
+            isForbidden::class,
+            isMaintenance::class,
+            isLogin::class,
+        ],
+
+        'admin' => [
+            isForbidden::class,
+            isAdminLogin::class,
+            Permission::class,
+        ],
+
         'api' => [
             'throttle:60,1',
             SubstituteBindings::class,
@@ -96,13 +107,9 @@ class Kernel extends HttpKernel
         'throttle' => ThrottleRequests::class,
         'verified' => EnsureEmailIsVerified::class,
         'webApi' => WebApi::class,
-        'isAdmin' => isAdmin::class,
-        'isAdminLogin' => isAdminLogin::class,
-        'isLogin' => isLogin::class,
         'isMaintenance' => isMaintenance::class,
         'isSecurity' => isSecurity::class,
         'isForbidden' => isForbidden::class,
         'affiliate' => Affiliate::class,
-        'permission' => PermissionMiddleware::class,
     ];
 }

+ 31 - 0
app/Http/Middleware/Permission.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Http\Request;
+use Spatie\Permission\Exceptions\UnauthorizedException;
+
+class Permission
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  Request  $request
+     * @param  Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next, $guard = null)
+    {
+        if (app('auth')->guard($guard)->guest()) {
+            throw UnauthorizedException::notLoggedIn();
+        }
+
+        $route = request()->route()->getName();
+        if (app('auth')->guard($guard)->user()->can($route)) {
+            return $next($request);
+        }
+
+        throw UnauthorizedException::forPermissions((array) $route);
+    }
+}

+ 0 - 28
app/Http/Middleware/isAdmin.php

@@ -1,28 +0,0 @@
-<?php
-
-namespace App\Http\Middleware;
-
-use Auth;
-use Closure;
-use Illuminate\Http\Request;
-use Redirect;
-
-class isAdmin
-{
-    /**
-     * 校验是否为管理员身份.
-     *
-     * @param  Request  $request
-     * @param  Closure  $next
-     *
-     * @return mixed
-     */
-    public function handle(Request $request, Closure $next)
-    {
-        if (! Auth::getUser()->is_admin) {
-            return Redirect::route('home');
-        }
-
-        return $next($request);
-    }
-}

+ 2 - 3
app/Http/Requests/Admin/UserUpdateRequest.php

@@ -10,8 +10,8 @@ class UserUpdateRequest extends FormRequest
     {
         return [
             'username' => 'required',
-            'email' => 'required|unique:user,email,'.$this->user,
-            'port' => 'required|numeric|exclude_if:port,0|gt:0|unique:user,port,'.$this->user,
+            'email' => 'required|unique:user,email,'.$this->user->id,
+            'port' => 'required|numeric|exclude_if:port,0|gt:0|unique:user,port,'.$this->user->id,
             'passwd' => 'required|string',
             'uuid' => 'required|uuid',
             'transfer_enable' => 'required|numeric',
@@ -24,7 +24,6 @@ class UserUpdateRequest extends FormRequest
             'remark' => 'nullable|string',
             'level' => 'required|numeric',
             'group_id' => 'numeric',
-            'is_admin' => 'boolean|exclude_unless:id,1|gte:1',
             'reset_time' => 'nullable|date_format:Y-m-d',
             'invite_num' => 'numeric',
             'status' => 'required|integer|between:-1,1',

+ 5 - 1
app/Providers/AuthServiceProvider.php

@@ -2,6 +2,7 @@
 
 namespace App\Providers;
 
+use Gate;
 use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
 
 class AuthServiceProvider extends ServiceProvider
@@ -23,6 +24,9 @@ class AuthServiceProvider extends ServiceProvider
     public function boot()
     {
         $this->registerPolicies();
-        //
+
+        Gate::before(function ($user) {
+            return $user->hasRole('Super Admin') ? true : null;
+        });
     }
 }

+ 18 - 1
app/Providers/RouteServiceProvider.php

@@ -44,7 +44,10 @@ class RouteServiceProvider extends ServiceProvider
         $this->mapApiRoutes();
 
         $this->mapWebRoutes();
-        //
+
+        $this->mapUserRoutes();
+
+        $this->mapAdminRoutes();
     }
 
     /**
@@ -75,4 +78,18 @@ class RouteServiceProvider extends ServiceProvider
             ->namespace($this->namespace)
             ->group(base_path('routes/web.php'));
     }
+
+    protected function mapUserRoutes()
+    {
+        Route::middleware(['web', 'user'])
+            ->namespace($this->namespace)
+            ->group(base_path('routes/user.php'));
+    }
+
+    protected function mapAdminRoutes()
+    {
+        Route::middleware(['web', 'admin'])
+            ->namespace($this->namespace)
+            ->group(base_path('routes/admin.php'));
+    }
 }

+ 1 - 1
app/Providers/TelescopeServiceProvider.php

@@ -63,7 +63,7 @@ class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
     protected function gate()
     {
         Gate::define('viewTelescope', function ($user) {
-            return $user->is_admin;
+            return $user->hasRole('Super Admin');
         });
     }
 }

+ 50 - 40
composer.lock

@@ -4249,16 +4249,16 @@
         },
         {
             "name": "stripe/stripe-php",
-            "version": "v7.66.1",
+            "version": "v7.67.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/stripe/stripe-php.git",
-                "reference": "a2ebaa272a8797b21e81afaf8d5ba0953ff15e13"
+                "reference": "935d2c67912007f6d17b6c08a62050252c509129"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/a2ebaa272a8797b21e81afaf8d5ba0953ff15e13",
-                "reference": "a2ebaa272a8797b21e81afaf8d5ba0953ff15e13",
+                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/935d2c67912007f6d17b6c08a62050252c509129",
+                "reference": "935d2c67912007f6d17b6c08a62050252c509129",
                 "shasum": ""
             },
             "require": {
@@ -4268,7 +4268,7 @@
                 "php": ">=5.6.0"
             },
             "require-dev": {
-                "friendsofphp/php-cs-fixer": "2.16.5",
+                "friendsofphp/php-cs-fixer": "2.17.1",
                 "php-coveralls/php-coveralls": "^2.1",
                 "phpunit/phpunit": "^5.7",
                 "squizlabs/php_codesniffer": "^3.3",
@@ -4304,38 +4304,37 @@
             ],
             "support": {
                 "issues": "https://github.com/stripe/stripe-php/issues",
-                "source": "https://github.com/stripe/stripe-php/tree/v7.66.1"
+                "source": "https://github.com/stripe/stripe-php/tree/v7.67.0"
             },
-            "time": "2020-12-01T18:44:12+00:00"
+            "time": "2020-12-09T19:00:34+00:00"
         },
         {
             "name": "swiftmailer/swiftmailer",
-            "version": "v6.2.3",
+            "version": "v6.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/swiftmailer/swiftmailer.git",
-                "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9"
+                "reference": "56f0ab23f54c4ccbb0d5dcc67ff8552e0c98d59e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
-                "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
+                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/56f0ab23f54c4ccbb0d5dcc67ff8552e0c98d59e",
+                "reference": "56f0ab23f54c4ccbb0d5dcc67ff8552e0c98d59e",
                 "shasum": ""
             },
             "require": {
-                "egulias/email-validator": "~2.0",
+                "egulias/email-validator": "^2.0",
                 "php": ">=7.0.0",
                 "symfony/polyfill-iconv": "^1.0",
                 "symfony/polyfill-intl-idn": "^1.10",
                 "symfony/polyfill-mbstring": "^1.0"
             },
             "require-dev": {
-                "mockery/mockery": "~0.9.1",
-                "symfony/phpunit-bridge": "^3.4.19|^4.1.8"
+                "mockery/mockery": "^1.0",
+                "symfony/phpunit-bridge": "^4.4|^5.0"
             },
             "suggest": {
-                "ext-intl": "Needed to support internationalized email addresses",
-                "true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed"
+                "ext-intl": "Needed to support internationalized email addresses"
             },
             "type": "library",
             "extra": {
@@ -4370,9 +4369,19 @@
             ],
             "support": {
                 "issues": "https://github.com/swiftmailer/swiftmailer/issues",
-                "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.3"
+                "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.4"
             },
-            "time": "2019-11-12T09:31:26+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-12-08T18:02:06+00:00"
         },
         {
             "name": "symfony/console",
@@ -7471,21 +7480,21 @@
         },
         {
             "name": "barryvdh/laravel-ide-helper",
-            "version": "v2.8.1",
+            "version": "v2.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-ide-helper.git",
-                "reference": "affa55122f83575888d4ebf1728992686e8223de"
+                "reference": "5515cabea39b9cf55f98980d0f269dc9d85cfcca"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/affa55122f83575888d4ebf1728992686e8223de",
-                "reference": "affa55122f83575888d4ebf1728992686e8223de",
+                "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/5515cabea39b9cf55f98980d0f269dc9d85cfcca",
+                "reference": "5515cabea39b9cf55f98980d0f269dc9d85cfcca",
                 "shasum": ""
             },
             "require": {
                 "barryvdh/reflection-docblock": "^2.0.6",
-                "composer/composer": "^1.6 || ^2.0@dev",
+                "composer/composer": "^1.6 || ^2",
                 "doctrine/dbal": "~2.3",
                 "ext-json": "*",
                 "illuminate/console": "^6 || ^7 || ^8",
@@ -7495,13 +7504,14 @@
                 "phpdocumentor/type-resolver": "^1.1.0"
             },
             "require-dev": {
+                "ext-pdo_sqlite": "*",
                 "friendsofphp/php-cs-fixer": "^2",
                 "illuminate/config": "^6 || ^7 || ^8",
                 "illuminate/view": "^6 || ^7 || ^8",
-                "mockery/mockery": "^1.3",
+                "mockery/mockery": "^1.3.3",
                 "orchestra/testbench": "^4 || ^5 || ^6",
                 "phpunit/phpunit": "^8.5 || ^9",
-                "spatie/phpunit-snapshot-assertions": "^1.4 || ^2.2 || ^3",
+                "spatie/phpunit-snapshot-assertions": "^1.4 || ^2.2 || ^3 || ^4",
                 "vimeo/psalm": "^3.12"
             },
             "type": "library",
@@ -7544,7 +7554,7 @@
             ],
             "support": {
                 "issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
-                "source": "https://github.com/barryvdh/laravel-ide-helper/tree/master"
+                "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.8.2"
             },
             "funding": [
                 {
@@ -7552,7 +7562,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-09-07T07:36:37+00:00"
+            "time": "2020-12-06T08:55:05+00:00"
         },
         {
             "name": "barryvdh/reflection-docblock",
@@ -8108,16 +8118,16 @@
         },
         {
             "name": "facade/ignition",
-            "version": "2.5.2",
+            "version": "2.5.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/ignition.git",
-                "reference": "08668034beb185fa2ac6f09b1034eaa440952ace"
+                "reference": "d8dc4f90ed469f9f9313b976fb078c20585d5c99"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition/zipball/08668034beb185fa2ac6f09b1034eaa440952ace",
-                "reference": "08668034beb185fa2ac6f09b1034eaa440952ace",
+                "url": "https://api.github.com/repos/facade/ignition/zipball/d8dc4f90ed469f9f9313b976fb078c20585d5c99",
+                "reference": "d8dc4f90ed469f9f9313b976fb078c20585d5c99",
                 "shasum": ""
             },
             "require": {
@@ -8181,7 +8191,7 @@
                 "issues": "https://github.com/facade/ignition/issues",
                 "source": "https://github.com/facade/ignition"
             },
-            "time": "2020-11-17T09:18:51+00:00"
+            "time": "2020-12-09T20:25:45+00:00"
         },
         {
             "name": "facade/ignition-contracts",
@@ -8543,25 +8553,25 @@
         },
         {
             "name": "maximebf/debugbar",
-            "version": "v1.16.3",
+            "version": "v1.16.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/maximebf/php-debugbar.git",
-                "reference": "1a1605b8e9bacb34cc0c6278206d699772e1d372"
+                "reference": "c86c717e4bf3c6d98422da5c38bfa7b0f494b04c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/1a1605b8e9bacb34cc0c6278206d699772e1d372",
-                "reference": "1a1605b8e9bacb34cc0c6278206d699772e1d372",
+                "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/c86c717e4bf3c6d98422da5c38bfa7b0f494b04c",
+                "reference": "c86c717e4bf3c6d98422da5c38bfa7b0f494b04c",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1",
+                "php": "^7.1|^8",
                 "psr/log": "^1.0",
                 "symfony/var-dumper": "^2.6|^3|^4|^5"
             },
             "require-dev": {
-                "phpunit/phpunit": "^5"
+                "phpunit/phpunit": "^7.5.20 || ^9.4.2"
             },
             "suggest": {
                 "kriswallsmith/assetic": "The best way to manage assets",
@@ -8602,9 +8612,9 @@
             ],
             "support": {
                 "issues": "https://github.com/maximebf/php-debugbar/issues",
-                "source": "https://github.com/maximebf/php-debugbar/tree/v1.16.3"
+                "source": "https://github.com/maximebf/php-debugbar/tree/v1.16.4"
             },
-            "time": "2020-05-06T07:06:27+00:00"
+            "time": "2020-12-07T10:48:48+00:00"
         },
         {
             "name": "mockery/mockery",

+ 1 - 1
config/permission.php

@@ -104,7 +104,7 @@ return [
      * By default wildcard permission lookups are disabled.
      */
 
-    'enable_wildcard_permission' => false,
+    'enable_wildcard_permission' => true,
 
     'cache' => [
 

+ 176 - 0
database/migrations/2020_12_07_120247_permission_data.php

@@ -0,0 +1,176 @@
+<?php
+
+use App\Models\User;
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Spatie\Permission\Models\Permission;
+use Spatie\Permission\Models\Role;
+use Spatie\Permission\PermissionRegistrar;
+
+class PermissionData extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        $tableNames = config('permission.table_names');
+        Schema::table($tableNames['permissions'], function (Blueprint $table) {
+            $table->string('description')->after('name');
+        });
+
+        Schema::table($tableNames['roles'], function (Blueprint $table) {
+            $table->string('description')->after('name');
+        });
+
+        Artisan::call('cache:clear');
+        app()[PermissionRegistrar::class]->forgetCachedPermissions();
+
+        $permissions = [
+            ['name' => 'admin.aff.detail', 'description' => '【推广系统】提现申请详情', 'guard_name' => 'web'],
+            ['name' => 'admin.aff.index', 'description' => '【推广系统】提现管理列表', 'guard_name' => 'web'],
+            ['name' => 'admin.aff.rebate', 'description' => '【推广系统】返利流水记录', 'guard_name' => 'web'],
+            ['name' => 'admin.aff.setStatus', 'description' => '【推广系统】设置提现状态', 'guard_name' => 'web'],
+            ['name' => 'admin.article.create,store', 'description' => '【客服系统】新建文章', 'guard_name' => 'web'],
+            ['name' => 'admin.article.destroy', 'description' => '【客服系统】删除文章', 'guard_name' => 'web'],
+            ['name' => 'admin.article.edit,update', 'description' => '【客服系统】编辑文章', 'guard_name' => 'web'],
+            ['name' => 'admin.article.index,show', 'description' => '【客服系统】文章列表', 'guard_name' => 'web'],
+            ['name' => 'admin.config.*', 'description' => '【设置】通用配置', 'guard_name' => 'web'],
+            ['name' => 'admin.coupon.create,store', 'description' => '【商品系统】新建卡劵', 'guard_name' => 'web'],
+            ['name' => 'admin.coupon.destroy', 'description' => '【商品系统】删除卡劵', 'guard_name' => 'web'],
+            ['name' => 'admin.coupon.export', 'description' => '【商品系统】导出卡劵', 'guard_name' => 'web'],
+            ['name' => 'admin.coupon.index', 'description' => '【商品系统】卡劵列表', 'guard_name' => 'web'],
+            ['name' => 'admin.goods.create,store', 'description' => '【商品系统】新建商品', 'guard_name' => 'web'],
+            ['name' => 'admin.goods.destroy', 'description' => '【商品系统】删除商品', 'guard_name' => 'web'],
+            ['name' => 'admin.goods.edit,update', 'description' => '【商品系统】编辑商品', 'guard_name' => 'web'],
+            ['name' => 'admin.goods.index', 'description' => '【商品系统】商品列表', 'guard_name' => 'web'],
+            ['name' => 'admin.index', 'description' => '【管理中心】首页', 'guard_name' => 'web'],
+            ['name' => 'admin.invite.index', 'description' => '【推广系统】邀请列表', 'guard_name' => 'web'],
+            ['name' => 'admin.invite.create', 'description' => '【推广系统】生成邀请码', 'guard_name' => 'web'],
+            ['name' => 'admin.invite.export', 'description' => '【推广系统】导出邀请码', 'guard_name' => 'web'],
+            ['name' => 'admin.log.ban', 'description' => '【日志系统】封禁记录', 'guard_name' => 'web'],
+            ['name' => 'admin.log.credit', 'description' => '【日志系统】余额记录', 'guard_name' => 'web'],
+            ['name' => 'admin.log.flow', 'description' => '【日志系统】流量变动记录', 'guard_name' => 'web'],
+            ['name' => 'admin.log.ip', 'description' => '【日志系统】在线IP记录', 'guard_name' => 'web'],
+            ['name' => 'admin.log.notify', 'description' => '【日志系统】通知记录', 'guard_name' => 'web'],
+            ['name' => 'admin.log.online', 'description' => '【日志系统】在线监控', 'guard_name' => 'web'],
+            ['name' => 'admin.log.traffic', 'description' => '【日志系统】流量日志', 'guard_name' => 'web'],
+            ['name' => 'admin.log.viewer', 'description' => '【日志系统】运行日志', 'guard_name' => 'web'],
+            ['name' => 'admin.marketing.add', 'description' => '【客服系统】推送消息', 'guard_name' => 'web'],
+            ['name' => 'admin.marketing.email', 'description' => '【客服系统】邮件消息列表', 'guard_name' => 'web'],
+            ['name' => 'admin.marketing.push', 'description' => '【客服系统】推送消息列表', 'guard_name' => 'web'],
+            ['name' => 'admin.node.auth.destroy', 'description' => '【线路系统】删除授权', 'guard_name' => 'web'],
+            ['name' => 'admin.node.auth.index', 'description' => '【线路系统】授权列表', 'guard_name' => 'web'],
+            ['name' => 'admin.node.auth.store', 'description' => '【线路系统】新建授权', 'guard_name' => 'web'],
+            ['name' => 'admin.node.auth.update', 'description' => '【线路系统】编辑授权', 'guard_name' => 'web'],
+            ['name' => 'admin.node.cert.create,store', 'description' => '【线路系统】新建证书', 'guard_name' => 'web'],
+            ['name' => 'admin.node.cert.destroy', 'description' => '【线路系统】删除证书', 'guard_name' => 'web'],
+            ['name' => 'admin.node.cert.edit,update', 'description' => '【线路系统】编辑证书', 'guard_name' => 'web'],
+            ['name' => 'admin.node.cert.index', 'description' => '【线路系统】证书列表', 'guard_name' => 'web'],
+            ['name' => 'admin.node.check', 'description' => '【线路系统】阻断检测', 'guard_name' => 'web'],
+            ['name' => 'admin.node.create,store', 'description' => '【线路系统】新建线路', 'guard_name' => 'web'],
+            ['name' => 'admin.node.destroy', 'description' => '【线路系统】删除线路', 'guard_name' => 'web'],
+            ['name' => 'admin.node.edit,update', 'description' => '【线路系统】编辑线路', 'guard_name' => 'web'],
+            ['name' => 'admin.node.geo', 'description' => '【线路系统】更新地理', 'guard_name' => 'web'],
+            ['name' => 'admin.node.index', 'description' => '【线路系统】线路列表', 'guard_name' => 'web'],
+            ['name' => 'admin.node.monitor', 'description' => '【线路系统】流量监控', 'guard_name' => 'web'],
+            ['name' => 'admin.node.ping', 'description' => '【线路系统】测速', 'guard_name' => 'web'],
+            ['name' => 'admin.node.pingLog', 'description' => '【线路系统】测速日志', 'guard_name' => 'web'],
+            ['name' => 'admin.node.reload', 'description' => '【线路系统】重载', 'guard_name' => 'web'],
+            ['name' => 'admin.order', 'description' => '【商品系统】订单列表', 'guard_name' => 'web'],
+            ['name' => 'admin.payment.callback', 'description' => '【日志系统】回调列表', 'guard_name' => 'web'],
+            ['name' => 'admin.permission.create,store', 'description' => '【权限系统】新建权限', 'guard_name' => 'web'],
+            ['name' => 'admin.permission.destroy', 'description' => '【权限系统】删除权限', 'guard_name' => 'web'],
+            ['name' => 'admin.permission.edit,update', 'description' => '【权限系统】编辑权限', 'guard_name' => 'web'],
+            ['name' => 'admin.permission.index', 'description' => '【权限系统】权限列表', 'guard_name' => 'web'],
+            ['name' => 'admin.role.create,store', 'description' => '【权限系统】新建角色', 'guard_name' => 'web'],
+            ['name' => 'admin.role.destroy', 'description' => '【权限系统】删除角色', 'guard_name' => 'web'],
+            ['name' => 'admin.role.edit,update', 'description' => '【权限系统】编辑角色', 'guard_name' => 'web'],
+            ['name' => 'admin.role.index', 'description' => '【权限系统】角色列表', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.clear', 'description' => '【审计规则】清除触发日志', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.destroy', 'description' => '【审计规则】删除规则', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.group.assign,editNode', 'description' => '【审计规则】分组关联线路', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.group.create,store', 'description' => '【审计规则】新建分组', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.group.destroy', 'description' => '【审计规则】删除分组', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.group.edit,update', 'description' => '【审计规则】编辑分组', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.group.index', 'description' => '【审计规则】分组列表', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.index', 'description' => '【审计规则】规则列表', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.log', 'description' => '【审计规则】触发日志', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.store', 'description' => '【审计规则】新建规则', 'guard_name' => 'web'],
+            ['name' => 'admin.rule.update', 'description' => '【审计规则】编辑规则', 'guard_name' => 'web'],
+            ['name' => 'admin.subscribe.index', 'description' => '【用户系统】订阅列表', 'guard_name' => 'web'],
+            ['name' => 'admin.subscribe.log', 'description' => '【用户系统】订阅记录', 'guard_name' => 'web'],
+            ['name' => 'admin.subscribe.set', 'description' => '【用户系统】编辑订阅状态', 'guard_name' => 'web'],
+            ['name' => 'admin.system.index', 'description' => '【设置】查看系统设置', 'guard_name' => 'web'],
+            ['name' => 'admin.system.update,extend', 'description' => '【设置】编辑系统设置', 'guard_name' => 'web'],
+            ['name' => 'admin.test.*', 'description' => '【设置】通知,支付设置测试', 'guard_name' => 'web'],
+            ['name' => 'admin.ticket.destroy', 'description' => '【客服系统】删除工单', 'guard_name' => 'web'],
+            ['name' => 'admin.ticket.edit,update', 'description' => '【客服系统】回复工单', 'guard_name' => 'web'],
+            ['name' => 'admin.ticket.index', 'description' => '【客服系统】工单列表', 'guard_name' => 'web'],
+            ['name' => 'admin.ticket.store', 'description' => '【客服系统】新建工单', 'guard_name' => 'web'],
+            ['name' => 'admin.tools.*', 'description' => '【工具箱】', 'guard_name' => 'web'],
+            ['name' => 'admin.user.batch', 'description' => '【用户系统】生成用户', 'guard_name' => 'web'],
+            ['name' => 'admin.user.create,store', 'description' => '【用户系统】新建用户', 'guard_name' => 'web'],
+            ['name' => 'admin.user.destroy', 'description' => '【用户系统】删除用户', 'guard_name' => 'web'],
+            ['name' => 'admin.user.edit,update', 'description' => '【用户系统】编辑用户', 'guard_name' => 'web'],
+            ['name' => 'admin.user.export', 'description' => '【用户系统】配置信息', 'guard_name' => 'web'],
+            ['name' => 'admin.user.exportProxy', 'description' => '【用户系统】读取配置', 'guard_name' => 'web'],
+            ['name' => 'admin.user.group.create,store', 'description' => '【用户系统】新建分组', 'guard_name' => 'web'],
+            ['name' => 'admin.user.group.destroy', 'description' => '【用户系统】删除分组', 'guard_name' => 'web'],
+            ['name' => 'admin.user.group.edit,update', 'description' => '【用户系统】编辑分组', 'guard_name' => 'web'],
+            ['name' => 'admin.user.group.index', 'description' => '【用户系统】分组列表', 'guard_name' => 'web'],
+            ['name' => 'admin.user.index', 'description' => '【用户系统】用户列表', 'guard_name' => 'web'],
+            ['name' => 'admin.user.monitor', 'description' => '【用户系统】流量统计', 'guard_name' => 'web'],
+            ['name' => 'admin.user.online', 'description' => '【用户系统】在线巡查', 'guard_name' => 'web'],
+            ['name' => 'admin.user.reset', 'description' => '【用户系统】流量重置', 'guard_name' => 'web'],
+            ['name' => 'admin.user.switch', 'description' => '【用户系统】用户视角', 'guard_name' => 'web'],
+            ['name' => 'admin.user.updateCredit', 'description' => '【用户系统】编辑余额', 'guard_name' => 'web'],
+            ['name' => 'give roles', 'description' => '【用户系统】赋予角色权限', 'guard_name' => 'web'],
+        ];
+
+        Permission::insert($permissions);
+        Role::create(['name' => 'Super Admin', 'description' => '超级管理员']);
+
+        foreach (User::whereIsAdmin(1)->get() as $admin) {
+            $admin->assignRole('Super Admin');
+        }
+
+        Schema::table('user', function (Blueprint $table) {
+            $table->dropColumn(['is_admin']);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        $tableNames = config('permission.table_names');
+        Schema::table($tableNames['permissions'], function (Blueprint $table) {
+            $table->dropColumn('description');
+        });
+
+        Schema::table($tableNames['roles'], function (Blueprint $table) {
+            $table->dropColumn('description');
+        });
+
+        Artisan::call('cache:clear');
+        app()[PermissionRegistrar::class]->forgetCachedPermissions();
+
+        Schema::table('user', function (Blueprint $table) {
+            $table->boolean('is_admin')->default(0)->comment('是否管理员:0-否、1-是')->after('group_id');
+        });
+
+        foreach (User::role('Super Admin')->get() as $admin) {
+            $admin->is_admin = 1;
+            $admin->save();
+        }
+
+        Role::query()->delete();
+        Permission::query()->delete();
+    }
+}

+ 91 - 0
resources/views/_layout.blade.php

@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<!--[if IE 8]>
+<html lang="{{app()->getLocale()}}" class="ie8 no-js css-menubar"> <![endif]-->
+<!--[if IE 9]>
+<html lang="{{app()->getLocale()}}" class="ie9 no-js css-menubar"> <![endif]-->
+<!--[if !IE]><!-->
+<html lang="{{app()->getLocale()}}">
+<!--<![endif]-->
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="description"
+          content="An account management Panel based on Laravel7 framework. Include multiple payment, account management, system caching, admin notification, products models, and more.">
+    <meta name="keywords" content="ProxyPanel Laravel Shadowsocks ShadowsocksR V2Ray Trojan VNET VPN">
+    <meta name="author" content="ZBrettonYe">
+    <meta name="copyright" content="2017-2020©ProxyPanel">
+    <title>@yield('title')</title>
+    <link href="{{asset('favicon.ico')}}" rel="shortcut icon apple-touch-icon">
+    <!-- 样式表/Stylesheets -->
+    <link href="/assets/global/css/bootstrap.min.css" type="text/css" rel="stylesheet">
+    <link href="/assets/global/css/bootstrap-extend.min.css" type="text/css" rel="stylesheet">
+    <link href="/assets/css/site.min.css" type="text/css" rel="stylesheet">
+    <!-- 插件/Plugins -->
+    <link href="/assets/global/vendor/animsition/animsition.min.css" type="text/css" rel="stylesheet">
+    <link href="/assets/global/vendor/asscrollable/asScrollable.min.css" type="text/css" rel="stylesheet">
+    <link href="/assets/global/vendor/slidepanel/slidePanel.min.css" type="text/css" rel="stylesheet">
+    <link href="/assets/global/vendor/flag-icon-css/flag-icon.min.css" type="text/css" rel="stylesheet">
+@yield('layout_css')
+<!-- 字体/Fonts -->
+    <link href="/assets/global/fonts/web-icons/web-icons.min.css" type="text/css" rel="stylesheet">
+    <link href="//fonts.loli.net/css?family=Roboto:300,400,500,300italic" type="text/css" rel="stylesheet">
+    <!--[if lt IE 9]>
+    <script src="/assets/global/vendor/html5shiv/html5shiv.min.js" type="text/javascript"></script>
+    <![endif]-->
+    <!--[if lt IE 10]>
+    <script src="/assets/global/vendor/media-match/media.match.min.js" type="text/javascript"></script>
+    <script src="/assets/global/vendor/respond/respond.min.js" type="text/javascript"></script>
+    <![endif]-->
+    <!-- Scripts -->
+    <script src="/assets/global/vendor/breakpoints/breakpoints.min.js" type="text/javascript"></script>
+    <script type="text/javascript">
+      Breakpoints();
+    </script>
+</head>
+
+<body class="animsition @yield('body_class')">
+@yield('layout_content')
+<!-- 核心/Core -->
+<script src="/assets/global/vendor/babel-external-helpers/babel-external-helpers.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/jquery/jquery.min.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/popper-js/umd/popper.min.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/bootstrap/bootstrap.min.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/animsition/animsition.min.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/mousewheel/jquery.mousewheel.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/asscrollbar/jquery-asScrollbar.min.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/asscrollable/jquery-asScrollable.min.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/ashoverscroll/jquery-asHoverScroll.min.js" type="text/javascript"></script>
+<!-- 插件/Plugins -->
+<script src="/assets/global/vendor/screenfull/screenfull.js" type="text/javascript"></script>
+<script src="/assets/global/vendor/slidepanel/jquery-slidePanel.min.js" type="text/javascript"></script>
+<!-- 脚本/Scripts -->
+<script src="/assets/global/js/Component.js" type="text/javascript"></script>
+<script src="/assets/global/js/Plugin.js" type="text/javascript"></script>
+<script src="/assets/global/js/Base.js" type="text/javascript"></script>
+<script src="/assets/global/js/Config.js" type="text/javascript"></script>
+<script src="/assets/js/Section/Menubar.js" type="text/javascript"></script>
+<script src="/assets/js/Section/Sidebar.js" type="text/javascript"></script>
+<script src="/assets/js/Section/PageAside.js" type="text/javascript"></script>
+<script src="/assets/js/Plugin/menu.js" type="text/javascript"></script>
+<!-- 设置/Config -->
+<script src="/assets/global/js/config/colors.js" type="text/javascript"></script>
+<script type="text/javascript">
+  Config.set('assets', '/assets');
+</script>
+<!-- 页面/Page -->
+<script src="/assets/js/Site.js" type="text/javascript"></script>
+<script src="/assets/global/js/Plugin/asscrollable.js" type="text/javascript"></script>
+<script src="/assets/global/js/Plugin/slidepanel.js" type="text/javascript"></script>
+<script type="text/javascript">
+  (function(document, window, $) {
+    'use strict';
+    const Site = window.Site;
+    $(document).ready(function() {
+      Site.run();
+    });
+  })(document, window, jQuery);
+</script>
+@yield('layout_javascript')
+</body>
+</html>

+ 6 - 2
resources/views/admin/aff/detail.blade.php

@@ -41,9 +41,13 @@
                                 <td> {{$commission->id}} </td>
                                 <td> {{$commission->invitee->email ?? '【账号已删除】'}} </td>
                                 <td>
-                                    <a href="{{route('admin.order', ['id' => $commission->order->id])}}" target="_blank">
+                                    @can('admin.order')
+                                        <a href="{{route('admin.order', ['id' => $commission->order->id])}}" target="_blank">
+                                            {{$commission->order->goods->name}}
+                                        </a>
+                                    @else
                                         {{$commission->order->goods->name}}
-                                    </a>
+                                    @endcan
                                 </td>
                                 <td> ¥{{$commission->amount}} </td>
                                 <td> ¥{{$commission->commission}} </td>

+ 27 - 15
resources/views/admin/aff/index.blade.php

@@ -48,9 +48,13 @@
                                 @if(empty($apply->user))
                                     【账号已删除】
                                 @else
-                                    <a href="{{route('admin.user.index', ['id'=>$apply->user_id])}}" target="_blank">
+                                    @can('admin.user.index')
+                                        <a href="{{route('admin.user.index', ['id'=>$apply->user_id])}}" target="_blank">
+                                            {{$apply->user->email}}
+                                        </a>
+                                    @else
                                         {{$apply->user->email}}
-                                    </a>
+                                    @endcan
                                 @endif
                             </td>
                             <td> ¥{{$apply->amount}} </td>
@@ -67,19 +71,25 @@
                             </td>
                             <td> {{$apply->created_at == $apply->updated_at ? '' : $apply->updated_at}} </td>
                             <td>
-                                <div class="btn-group">
-                                    @if($apply->status === 0)
-                                        <a href="javascript:setStatus('{{$apply->id}}','1')" class="btn btn-sm btn-success">
-                                            <i class="icon wb-check" aria-hidden="true"></i>通过</a>
-                                        <a href="javascript:setStatus('{{$apply->id}}','-1')" class="btn btn-sm btn-danger">
-                                            <i class="icon wb-close" aria-hidden="true"></i>驳回</a>
-                                    @elseif($apply->status === 1)
-                                        <a href="javascript:setStatus('{{$apply->id}}','2')" class="btn btn-sm btn-primary">
-                                            <i class="icon wb-check-circle" aria-hidden="true"></i>已打款</a>
-                                    @endif
-                                    <a href="{{route('admin.aff.detail', $apply->id)}}" class="btn btn-sm btn-default">
-                                        <i class="icon wb-search"></i></a>
-                                </div>
+                                @canany(['admin.aff.setStatus', 'admin.aff.detail'])
+                                    <div class="btn-group">
+                                        @can('admin.aff.setStatus')
+                                            @if($apply->status === 0)
+                                                <a href="javascript:setStatus('{{$apply->id}}','1')" class="btn btn-sm btn-success">
+                                                    <i class="icon wb-check" aria-hidden="true"></i>通过</a>
+                                                <a href="javascript:setStatus('{{$apply->id}}','-1')" class="btn btn-sm btn-danger">
+                                                    <i class="icon wb-close" aria-hidden="true"></i>驳回</a>
+                                            @elseif($apply->status === 1)
+                                                <a href="javascript:setStatus('{{$apply->id}}','2')" class="btn btn-sm btn-primary">
+                                                    <i class="icon wb-check-circle" aria-hidden="true"></i>已打款</a>
+                                            @endif
+                                        @endcan
+                                        @can('admin.aff.detail')
+                                            <a href="{{route('admin.aff.detail', $apply->id)}}" class="btn btn-sm btn-default">
+                                                <i class="icon wb-search"></i></a>
+                                        @endcan
+                                    </div>
+                                @endcanany
                             </td>
                         </tr>
                     @endforeach
@@ -123,6 +133,7 @@
         window.location.href = '{{route('admin.aff.index')}}?email=' + $('#email').val() + '&status=' + $('#status option:selected').val();
       }
 
+      @can('admin.aff.setStatus')
       // 更改状态
       function setStatus(id, status) {
         $.post('{{route('admin.aff.setStatus')}}', {_token: '{{csrf_token()}}', id: id, status: status}, function(ret) {
@@ -133,5 +144,6 @@
           }
         });
       }
+        @endcan
     </script>
 @endsection

+ 50 - 36
resources/views/admin/article/index.blade.php

@@ -7,9 +7,11 @@
         <div class="panel">
             <div class="panel-heading">
                 <h3 class="panel-title">文章列表</h3>
-                <div class="panel-actions">
-                    <a href="{{route('admin.article.create')}}" class="btn btn-primary"><i class="icon wb-plus"></i>添加文章</a>
-                </div>
+                @can('admin.article.create')
+                    <div class="panel-actions">
+                        <a href="{{route('admin.article.create')}}" class="btn btn-primary"><i class="icon wb-plus"></i>添加文章</a>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
@@ -39,17 +41,27 @@
                                 <td> 未知</td>
                             @endif
                             <td>
-                                <a href="{{route('admin.article.show',$article->id)}}" target="_blank"> {{Str::limit($article->title, 80)}} </a>
+                                @can('admin.article.show')
+                                    <a href="{{route('admin.article.show',$article->id)}}" target="_blank"> {{Str::limit($article->title, 80)}} </a>
+                                @else
+                                    {{Str::limit($article->title, 80)}}
+                                @endcan
                             </td>
                             <td> {{$article->sort}} </td>
                             <td> {{$article->created_at}} </td>
                             <td>
-                                <div class="btn-group">
-                                    <a href="{{route('admin.article.edit',['article'=>$article->id, 'page'=>Request::input('page')])}}" class="btn btn-outline-primary">
-                                        <i class="icon wb-edit"></i></a>
-                                    <button class="btn btn-outline-danger" onclick="delArticle('{{route('admin.article.destroy',$article->id)}}')">
-                                        <i class="icon wb-close"></i></button>
-                                </div>
+                                @canany(['admin.article.edit', 'admin.article.destroy'])
+                                    <div class="btn-group">
+                                        @can('admin.article.edit')
+                                            <a href="{{route('admin.article.edit',['article'=>$article->id, 'page'=>Request::input('page')])}}" class="btn btn-outline-primary">
+                                                <i class="icon wb-edit"></i></a>
+                                        @endcan
+                                        @can('admin.article.destroy')
+                                            <button class="btn btn-outline-danger" onclick="delArticle('{{route('admin.article.destroy',$article->id)}}')">
+                                                <i class="icon wb-close"></i></button>
+                                        @endcan
+                                    </div>
+                                @endcanany
                             </td>
                         </tr>
                     @endforeach
@@ -74,32 +86,34 @@
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
-    <script type="text/javascript">
-      // 删除文章
-      function delArticle(url) {
-        swal.fire({
-          title: '确定删除文章?',
-          icon: 'question',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: url,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
+    @can('admin.article.destroy')
+        <script type="text/javascript">
+          // 删除文章
+          function delArticle(url) {
+            swal.fire({
+              title: '确定删除文章?',
+              icon: 'question',
+              showCancelButton: true,
+              cancelButtonText: '{{trans('home.ticket_close')}}',
+              confirmButtonText: '{{trans('home.ticket_confirm')}}',
+            }).then((result) => {
+              if (result.value) {
+                $.ajax({
+                  method: 'DELETE',
+                  url: url,
+                  data: {_token: '{{csrf_token()}}'},
+                  dataType: 'json',
+                  success: function(ret) {
+                    if (ret.status === 'success') {
+                      swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                    } else {
+                      swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                    }
+                  },
+                });
+              }
             });
           }
-        });
-      }
-    </script>
+        </script>
+    @endcan
 @endsection

+ 391 - 326
resources/views/admin/config/config.blade.php

@@ -119,12 +119,14 @@
                                             @if($obfs->is_default)
                                                 <span class="badge badge-lg badge-default">默认</span>
                                             @else
-                                                <button class="btn btn-primary" onclick="setDefault('{{$obfs->id}}')">
-                                                    默认
-                                                </button>
-                                                <button class="btn btn-danger" onclick="delConfig('{{$obfs->id}}','{{$obfs->name}}')">
-                                                    <i class="icon wb-trash"></i>
-                                                </button>
+                                                <div class="btn-group">
+                                                    <button class="btn btn-primary" onclick="setDefault('{{$obfs->id}}')">
+                                                        默认
+                                                    </button>
+                                                    <button class="btn btn-danger" onclick="delConfig('{{$obfs->id}}','{{$obfs->name}}')">
+                                                        <i class="icon wb-trash"></i>
+                                                    </button>
+                                                </div>
                                             @endif
                                         </td>
                                     </tr>
@@ -279,6 +281,7 @@
             </div>
         </div>
     </div>
+
     <div class="modal fade" id="add_level_modal" aria-hidden="true" role="dialog" tabindex="-1">
         <div class="modal-dialog modal-simple modal-center">
             <div class="modal-content">
@@ -306,6 +309,7 @@
             </div>
         </div>
     </div>
+
     <div class="modal fade" id="add_country_modal" aria-hidden="true" role="dialog" tabindex="-1">
         <div class="modal-dialog modal-simple modal-center">
             <div class="modal-content">
@@ -333,6 +337,7 @@
             </div>
         </div>
     </div>
+
     <div class="modal fade" id="add_label_modal" aria-hidden="true" role="dialog" tabindex="-1">
         <div class="modal-dialog modal-simple modal-center">
             <div class="modal-content">
@@ -368,347 +373,373 @@
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
 
     <script type="text/javascript">
-      // 添加等级
-      function addLevel() {
-        const level = $('#add_level').val();
-        const level_name = $('#add_level_name').val();
-
-        if (level.trim() === '') {
-          $('#level_msg').show().html('等级不能为空');
-          $('#level').focus();
-          return false;
+        @can('admin.config.level.store')
+        // 添加等级
+        function addLevel() {
+          const level = $('#add_level').val();
+          const level_name = $('#add_level_name').val();
+
+          if (level.trim() === '') {
+            $('#level_msg').show().html('等级不能为空');
+            $('#level').focus();
+            return false;
+          }
+
+          if (level_name.trim() === '') {
+            $('#level_msg').show().html('等级名称不能为空');
+            $('#level_name').focus();
+            return false;
+          }
+
+          $.ajax({
+            url: '{{route('admin.config.level.store')}}',
+            method: 'POST',
+            data: {_token: '{{csrf_token()}}', level: level, level_name: level_name},
+            beforeSend: function() {
+              $('#level_msg').show().html('正在添加');
+            },
+            success: function(ret) {
+              if (ret.status === 'fail') {
+                $('#level_msg').show().html(ret.message);
+                return false;
+              }
+              $('#add_level_modal').modal('hide');
+              window.location.reload();
+            },
+            error: function() {
+              $('#level_msg').show().html('请求错误,请重试');
+            },
+            complete: function() {
+              swal.fire({title: '添加成功', icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+            },
+          });
         }
 
-        if (level_name.trim() === '') {
-          $('#level_msg').show().html('等级名称不能为空');
-          $('#level_name').focus();
-          return false;
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.level.update')
+        // 更新等级
+        function updateLevel(id) {
+          $.ajax({
+            method: 'PUT',
+            url: '{{route('admin.config.level.update', '')}}/' + id,
+            data: {
+              _token: '{{csrf_token()}}',
+              level: $('#level_' + id).val(),
+              level_name: $('#level_name_' + id).val(),
+            },
+            dataType: 'json',
+            success: function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+              } else {
+                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+              }
+            },
+          });
         }
 
-        $.ajax({
-          url: '{{route('admin.config.level.store')}}',
-          method: 'POST',
-          data: {_token: '{{csrf_token()}}', level: level, level_name: level_name},
-          beforeSend: function() {
-            $('#level_msg').show().html('正在添加');
-          },
-          success: function(ret) {
-            if (ret.status === 'fail') {
-              $('#level_msg').show().html(ret.message);
-              return false;
-            }
-            $('#add_level_modal').modal('hide');
-            window.location.reload();
-          },
-          error: function() {
-            $('#level_msg').show().html('请求错误,请重试');
-          },
-          complete: function() {
-            swal.fire({title: '添加成功', icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-          },
-        });
-      }
-
-      // 更新等级
-      function updateLevel(id) {
-        $.ajax({
-          method: 'PUT',
-          url: '{{route('admin.config.level.update', '')}}/' + id,
-          data: {
-            _token: '{{csrf_token()}}',
-            level: $('#level_' + id).val(),
-            level_name: $('#level_name_' + id).val(),
-          },
-          dataType: 'json',
-          success: function(ret) {
-            if (ret.status === 'success') {
-              swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-            } else {
-              swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.level.destroy')
+        // 删除等级
+        function delLevel(id, name) {
+          swal.fire({
+            title: '确定删除等级 【' + name + '】 ?',
+            icon: 'question',
+            allowEnterKey: false,
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'DELETE',
+                url: '{{route('admin.config.level.destroy', '')}}/' + id,
+                data: {_token: '{{csrf_token()}}'},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+              });
             }
-          },
-        });
-      }
-
-      // 删除等级
-      function delLevel(id, name) {
-        swal.fire({
-          title: '确定删除等级 【' + name + '】 ?',
-          icon: 'question',
-          allowEnterKey: false,
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: '{{route('admin.config.level.destroy', '')}}/' + id,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
-            });
+          });
+        }
+
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.country.store')
+        // 添加国家/地区
+        function addCountry() {
+          const country_name = $('#add_country_name').val();
+          const country_code = $('#add_country_code').val();
+
+          if (country_code.trim() === '') {
+            $('#country_msg').show().html('国家/地区代码不能为空');
+            $('#add_country_code').focus();
+            return false;
           }
-        });
-      }
-
-      // 添加国家/地区
-      function addCountry() {
-        const country_name = $('#add_country_name').val();
-        const country_code = $('#add_country_code').val();
-
-        if (country_code.trim() === '') {
-          $('#country_msg').show().html('国家/地区代码不能为空');
-          $('#add_country_code').focus();
-          return false;
+
+          if (country_name.trim() === '') {
+            $('#country_msg').show().html('国家/地区名称不能为空');
+            $('#add_country_name').focus();
+            return false;
+          }
+
+          $.ajax({
+            url: '{{route('admin.config.country.store')}}',
+            method: 'POST',
+            data: {_token: '{{csrf_token()}}', code: country_code, name: country_name},
+            beforeSend: function() {
+              $('#country_msg').show().html('正在添加');
+            },
+            success: function(ret) {
+              if (ret.status === 'fail') {
+                $('#country_msg').show().html(ret.message);
+                return false;
+              }
+              $('#add_country_modal').modal('hide');
+              window.location.reload();
+            },
+            error: function() {
+              $('#country_msg').show().html('请求错误,请重试');
+            },
+            complete: function() {
+              swal.fire({
+                title: '添加成功',
+                icon: 'success',
+                timer: 1000,
+                showConfirmButton: false,
+              }).then(() => window.location.reload());
+            },
+          });
         }
 
-        if (country_name.trim() === '') {
-          $('#country_msg').show().html('国家/地区名称不能为空');
-          $('#add_country_name').focus();
-          return false;
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.country.update')
+        // 更新国家/地区
+        function updateCountry(code) {
+          $.ajax({
+            method: 'PUT',
+            url: '{{route('admin.config.country.update', '')}}/' + code,
+            data: {_token: '{{csrf_token()}}', name: $('#country_' + code).val()},
+            dataType: 'json',
+            success: function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+              } else {
+                swal.fire({title: ret.message, icon: 'error'});
+              }
+            },
+          });
         }
 
-        $.ajax({
-          url: '{{route('admin.config.country.store')}}',
-          method: 'POST',
-          data: {_token: '{{csrf_token()}}', code: country_code, name: country_name},
-          beforeSend: function() {
-            $('#country_msg').show().html('正在添加');
-          },
-          success: function(ret) {
-            if (ret.status === 'fail') {
-              $('#country_msg').show().html(ret.message);
-              return false;
-            }
-            $('#add_country_modal').modal('hide');
-            window.location.reload();
-          },
-          error: function() {
-            $('#country_msg').show().html('请求错误,请重试');
-          },
-          complete: function() {
-            swal.fire({
-              title: '添加成功',
-              icon: 'success',
-              timer: 1000,
-              showConfirmButton: false,
-            }).then(() => window.location.reload());
-          },
-        });
-      }
-
-      // 更新国家/地区
-      function updateCountry(code) {
-        $.ajax({
-          method: 'PUT',
-          url: '{{route('admin.config.country.update', '')}}/' + code,
-          data: {_token: '{{csrf_token()}}', name: $('#country_' + code).val()},
-          dataType: 'json',
-          success: function(ret) {
-            if (ret.status === 'success') {
-              swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-            } else {
-              swal.fire({title: ret.message, icon: 'error'});
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.country.destroy')
+        // 删除国家/地区
+        function delCountry(code, name) {
+          swal.fire({
+            title: '确定删除 【' + name + '】 信息?',
+            icon: 'question',
+            allowEnterKey: false,
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'DELETE',
+                url: '{{route('admin.config.country.destroy', '')}}/' + code,
+                data: {_token: '{{csrf_token()}}'},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+              });
             }
-          },
-        });
-      }
-
-      // 删除国家/地区
-      function delCountry(code, name) {
-        swal.fire({
-          title: '确定删除 【' + name + '】 信息?',
-          icon: 'question',
-          allowEnterKey: false,
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: '{{route('admin.config.country.destroy', '')}}/' + code,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
-            });
-          }
-        });
-      }
-
-      // 添加配置
-      function addConfig() {
-        const name = $('#name').val();
-        const type = $('#type').val();
-
-        if (name.trim() === '') {
-          $('#msg').show().html('名称不能为空');
-          $('#name').focus();
-          return false;
+          });
         }
 
-        $.ajax({
-          url: '{{route('admin.config.ss.store')}}',
-          method: 'POST',
-          data: {_token: '{{csrf_token()}}', name: name, type: type},
-          dataType: 'json',
-          beforeSend: function() {
-            $('#msg').show().html('正在添加');
-          },
-          success: function(ret) {
-            if (ret.status === 'fail') {
-              $('#msg').show().html(ret.message);
-              return false;
-            }
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
 
-            $('#add_config_modal').modal('hide');
-          },
-          error: function() {
-            $('#msg').show().html('请求错误,请重试');
-          },
-          complete: function() {
-            swal.fire({title: '添加成功', icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-          },
-        });
-      }
-
-      // 置为默认
-      function setDefault(id) {
-        $.ajax({
-          method: 'PUT',
-          url: '{{route('admin.config.ss.update', '')}}/' + id,
-          data: {_token: '{{csrf_token()}}'},
-          dataType: 'json',
-          success: function(ret) {
-            if (ret.status === 'success') {
-              swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-            } else {
-              swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-            }
-          },
-        });
-      }
-
-      // 删除配置
-      function delConfig(id, name) {
-        swal.fire({
-          title: '确定删除配置 【' + name + '】 ?',
-          icon: 'question',
-          allowEnterKey: false,
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: '{{route('admin.config.ss.destroy', '')}}/' + id,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
-            });
+        @can('admin.config.ss.store')
+        // 添加配置
+        function addConfig() {
+          const name = $('#name').val();
+          const type = $('#type').val();
+
+          if (name.trim() === '') {
+            $('#msg').show().html('名称不能为空');
+            $('#name').focus();
+            return false;
           }
-        });
-      }
 
-      // 添加标签
-      function addLabel() {
-        const name = $('#add_label').val();
-        const sort = $('#add_label_sort').val();
+          $.ajax({
+            url: '{{route('admin.config.ss.store')}}',
+            method: 'POST',
+            data: {_token: '{{csrf_token()}}', name: name, type: type},
+            dataType: 'json',
+            beforeSend: function() {
+              $('#msg').show().html('正在添加');
+            },
+            success: function(ret) {
+              if (ret.status === 'fail') {
+                $('#msg').show().html(ret.message);
+                return false;
+              }
 
-        if (name.trim() === '') {
-          $('#lable_msg').show().html('标签不能为空');
-          return false;
+              $('#add_config_modal').modal('hide');
+            },
+            error: function() {
+              $('#msg').show().html('请求错误,请重试');
+            },
+            complete: function() {
+              swal.fire({title: '添加成功', icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+            },
+          });
         }
 
-        if (sort.trim() === '') {
-          $('#lable_msg').show().html('标签排序不能为空');
-          return false;
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.ss.update')
+        // 置为默认
+        function setDefault(id) {
+          $.ajax({
+            method: 'PUT',
+            url: '{{route('admin.config.ss.update', '')}}/' + id,
+            data: {_token: '{{csrf_token()}}'},
+            dataType: 'json',
+            success: function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+              } else {
+                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+              }
+            },
+          });
         }
 
-        $.ajax({
-          url: '{{route('admin.config.label.store')}}',
-          method: 'POST',
-          data: {_token: '{{csrf_token()}}', name: name, sort: sort},
-          beforeSend: function() {
-            $('#level_msg').show().html('正在添加');
-          },
-          success: function(ret) {
-            if (ret.status === 'fail') {
-              $('#lable_msg').show().html(ret.message);
-              return false;
-            }
-            $('#add_label_modal').modal('hide');
-            window.location.reload();
-          },
-          error: function() {
-            $('#lable_msg').show().html('请求错误,请重试');
-          },
-          complete: function() {
-            swal.fire({
-              title: '添加成功',
-              icon: 'success',
-              timer: 1000,
-              showConfirmButton: false,
-            }).then(() => window.location.reload());
-          },
-        });
-      }
-
-      // 编辑标签
-      function updateLabel(id) {
-        $.ajax({
-          method: 'PUT',
-          url: '{{route('admin.config.label.update', '')}}/' + id,
-          data: {
-            _token: '{{csrf_token()}}',
-            name: $('#label_name_' + id).val(),
-            sort: $('#label_sort_' + id).val(),
-          },
-          dataType: 'json',
-          success: function(ret) {
-            if (ret.status === 'success') {
-              swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-            } else {
-              swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.ss.destroy')
+        // 删除配置
+        function delConfig(id, name) {
+          swal.fire({
+            title: '确定删除配置 【' + name + '】 ?',
+            icon: 'question',
+            allowEnterKey: false,
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'DELETE',
+                url: '{{route('admin.config.ss.destroy', '')}}/' + id,
+                data: {_token: '{{csrf_token()}}'},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+              });
             }
-          },
-        });
-      }
-
-      // 删除标签
-      function delLabel(id, name) {
-        swal.fire({
-          title: '警告',
-          text: '确定删除标签 【' + name + '】 ?',
-          icon: 'warning',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
+          });
+        }
+
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.label.store')
+        // 添加标签
+        function addLabel() {
+          const name = $('#add_label').val();
+          const sort = $('#add_label_sort').val();
+
+          if (name.trim() === '') {
+            $('#lable_msg').show().html('标签不能为空');
+            return false;
+          }
+
+          if (sort.trim() === '') {
+            $('#lable_msg').show().html('标签排序不能为空');
+            return false;
+          }
+
           $.ajax({
-            method: 'DELETE',
-            url: '{{route('admin.config.label.destroy', '')}}/' + id,
-            data: {_token: '{{csrf_token()}}'},
+            url: '{{route('admin.config.label.store')}}',
+            method: 'POST',
+            data: {_token: '{{csrf_token()}}', name: name, sort: sort},
+            beforeSend: function() {
+              $('#level_msg').show().html('正在添加');
+            },
+            success: function(ret) {
+              if (ret.status === 'fail') {
+                $('#lable_msg').show().html(ret.message);
+                return false;
+              }
+              $('#add_label_modal').modal('hide');
+              window.location.reload();
+            },
+            error: function() {
+              $('#lable_msg').show().html('请求错误,请重试');
+            },
+            complete: function() {
+              swal.fire({
+                title: '添加成功',
+                icon: 'success',
+                timer: 1000,
+                showConfirmButton: false,
+              }).then(() => window.location.reload());
+            },
+          });
+        }
+
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.label.update')
+        // 编辑标签
+        function updateLabel(id) {
+          $.ajax({
+            method: 'PUT',
+            url: '{{route('admin.config.label.update', '')}}/' + id,
+            data: {
+              _token: '{{csrf_token()}}',
+              name: $('#label_name_' + id).val(),
+              sort: $('#label_sort_' + id).val(),
+            },
             dataType: 'json',
             success: function(ret) {
               if (ret.status === 'success') {
@@ -718,7 +749,41 @@
               }
             },
           });
-        });
-      }
+        }
+
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
+
+        @can('admin.config.label.destroy')
+        // 删除标签
+        function delLabel(id, name) {
+          swal.fire({
+            title: '警告',
+            text: '确定删除标签 【' + name + '】 ?',
+            icon: 'warning',
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            $.ajax({
+              method: 'DELETE',
+              url: '{{route('admin.config.label.destroy', '')}}/' + id,
+              data: {_token: '{{csrf_token()}}'},
+              dataType: 'json',
+              success: function(ret) {
+                if (ret.status === 'success') {
+                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                } else {
+                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                }
+              },
+            });
+          });
+        }
+
+        @else
+        swal.fire({title: '您没有权限修改参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+        @endcan
     </script>
 @endsection

+ 93 - 84
resources/views/admin/config/emailFilter.blade.php

@@ -9,10 +9,12 @@
                 <h2 class="panel-title">邮箱过滤列表
                     <small>(用于屏蔽注册邮箱后缀)</small>
                 </h2>
-                <div class="panel-actions">
-                    <button class="btn btn-primary" data-toggle="modal" data-target="#add_email_suffix"> 添加邮箱后缀
-                    </button>
-                </div>
+                @can('admin.config.filter.store')
+                    <div class="panel-actions">
+                        <button class="btn btn-primary" data-toggle="modal" data-target="#add_email_suffix"> 添加邮箱后缀
+                        </button>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <table class="table text-md-center" data-toggle="table" data-mobile-responsive="true">
@@ -31,9 +33,11 @@
                             <td> {{$vo->type==1? '黑':'白'}} </td>
                             <td> {{$vo->words}} </td>
                             <td>
-                                <button class="btn btn-danger" onclick="delSuffix('{{$vo->id}}','{{$vo->words}}')">
-                                    <i class="icon wb-trash"></i>
-                                </button>
+                                @can('admin.config.filter.destroy')
+                                    <button class="btn btn-danger" onclick="delSuffix('{{$vo->id}}','{{$vo->words}}')">
+                                        <i class="icon wb-trash"></i>
+                                    </button>
+                                @endcan
                             </td>
                         </tr>
                     @endforeach
@@ -55,97 +59,102 @@
         </div>
     </div>
 
-    <div id="add_email_suffix" class="modal fade" tabindex="-1" data-focus-on="input:first" data-keyboard="false">
-        <div class="modal-dialog modal-simple modal-center modal-lg">
-            <div class="modal-content">
-                <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                        <span aria-hidden="true">×</span>
-                    </button>
-                    <h4 class="modal-title"> 添加邮箱后缀 </h4>
-                </div>
-                <div class="modal-body">
-                    <div class="form-group row">
-                        <label class="col-form-label col-md-2" for="type">类型</label>
-                        <div class="col-md-10 d-flex align-items-center">
-                            <div class="radio-custom radio-primary radio-inline">
-                                <input type="radio" name="type" value="1" checked/>
-                                <label for="type">黑名单</label>
+    @can('admin.config.filter.store')
+        <div id="add_email_suffix" class="modal fade" tabindex="-1" data-focus-on="input:first" data-keyboard="false">
+            <div class="modal-dialog modal-simple modal-center modal-lg">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">×</span>
+                        </button>
+                        <h4 class="modal-title"> 添加邮箱后缀 </h4>
+                    </div>
+                    <div class="modal-body">
+                        <div class="form-group row">
+                            <label class="col-form-label col-md-2" for="type">类型</label>
+                            <div class="col-md-10 d-flex align-items-center">
+                                <div class="radio-custom radio-primary radio-inline">
+                                    <input type="radio" name="type" value="1" checked/>
+                                    <label for="type">黑名单</label>
+                                </div>
+                                <div class="radio-custom radio-primary radio-inline">
+                                    <input type="radio" name="type" value="2"/>
+                                    <label for="type">白名单</label>
+                                </div>
                             </div>
-                            <div class="radio-custom radio-primary radio-inline">
-                                <input type="radio" name="type" value="2"/>
-                                <label for="type">白名单</label>
+                        </div>
+                        <div class="form-group row">
+                            <label class="col-form-label col-md-2" for="words">邮箱后缀</label>
+                            <div class="col-md-9">
+                                <input type="text" class="form-control" name="words" id="words" placeholder="请填入邮箱后缀"/>
                             </div>
                         </div>
                     </div>
-                    <div class="form-group row">
-                        <label class="col-form-label col-md-2" for="words">邮箱后缀</label>
-                        <div class="col-md-9">
-                            <input type="text" class="form-control" name="words" id="words" placeholder="请填入邮箱后缀"/>
-                        </div>
+                    <div class="modal-footer">
+                        <button data-dismiss="modal" class="btn btn-danger"> 关 闭</button>
+                        <button data-dismiss="modal" class="btn btn-success" onclick="addEmailSuffix()"> 提 交</button>
                     </div>
                 </div>
-                <div class="modal-footer">
-                    <button data-dismiss="modal" class="btn btn-danger"> 关 闭</button>
-                    <button data-dismiss="modal" class="btn btn-success" onclick="addEmailSuffix()"> 提 交</button>
-                </div>
             </div>
         </div>
-    </div>
-
+    @endcan
 @endsection
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
     <script type="text/javascript">
-      // 添加邮箱后缀
-      function addEmailSuffix() {
-        const words = $('#words').val();
-        if (words.trim() === '') {
-          swal.fire({title: '邮箱后缀不能为空', icon: 'warning', timer: 1000, showConfirmButton: false});
-          $('#words').focus();
-          return false;
-        }
-
-        $.post('{{route('admin.config.filter.store')}}', {
-          _token: '{{csrf_token()}}',
-          type: $('input:radio[name=\'type\']:checked').val(),
-          words: words,
-        }, function(ret) {
-          if (ret.status === 'success') {
-            swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-          } else {
-            swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+        @can('admin.config.filter.store')
+        // 添加邮箱后缀
+        function addEmailSuffix() {
+          const words = $('#words').val();
+          if (words.trim() === '') {
+            swal.fire({title: '邮箱后缀不能为空', icon: 'warning', timer: 1000, showConfirmButton: false});
+            $('#words').focus();
+            return false;
           }
-        });
-      }
 
-      // 删除邮箱后缀
-      function delSuffix(id, name) {
-        swal.fire({
-          title: '警告',
-          text: '确定删除邮箱后缀 【' + name + '】 ?',
-          icon: 'warning',
-          showCancelButton: true,
-          cancelButtonText: '取消',
-          confirmButtonText: '确定',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: '{{route('admin.config.filter.destroy', '')}}/' + id,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
-            });
-          }
-        });
-      }
+          $.post('{{route('admin.config.filter.store')}}', {
+            _token: '{{csrf_token()}}',
+            type: $('input:radio[name=\'type\']:checked').val(),
+            words: words,
+          }, function(ret) {
+            if (ret.status === 'success') {
+              swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+            } else {
+              swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+            }
+          });
+        }
+        @endcan
+
+        @can('admin.config.filter.destroy')
+        // 删除邮箱后缀
+        function delSuffix(id, name) {
+          swal.fire({
+            title: '警告',
+            text: '确定删除邮箱后缀 【' + name + '】 ?',
+            icon: 'warning',
+            showCancelButton: true,
+            cancelButtonText: '取消',
+            confirmButtonText: '确定',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'DELETE',
+                url: '{{route('admin.config.filter.destroy', '')}}/' + id,
+                data: {_token: '{{csrf_token()}}'},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+              });
+            }
+          });
+        }
+        @endcan
     </script>
 @endsection

+ 0 - 32
resources/views/admin/config/profile.blade.php

@@ -1,32 +0,0 @@
-@extends('admin.layouts')
-@section('content')
-    <div class="page-content container">
-        <div class="panel">
-            <div class="panel-heading">
-                <h1 class="panel-title cyan-600"><i class="icon wb-user"></i>{{trans('home.profile')}}</h1>
-            </div>
-            @if (Session::has('successMsg'))
-                <x-alert type="success" :message="Session::get('successMsg')"/>
-            @endif
-            @if($errors->any())
-                <x-alert type="danger" :message="$errors->all()"/>
-            @endif
-            <div class="panel-body">
-                <form action="{{route('admin.profile')}}" method="post" enctype="multipart/form-data" class="form-bordered">
-                    @csrf
-                    <div class="form-group row">
-                        <label for="old_password" class="col-md-2 col-form-label"> 旧密码 </label>
-                        <input type="password" class="form-control col-md-5 round" name="old_password" id="old_password" autofocus required/>
-                    </div>
-                    <div class="form-group row">
-                        <label for="new_password" class="col-md-2 col-form-label"> 新密码 </label>
-                        <input type="password" class="form-control col-md-5 round" name="new_password" id="new_password" required/>
-                    </div>
-                    <div class="form-actions">
-                        <button type="submit" class="btn btn-success"> 提 交</button>
-                    </div>
-                </form>
-            </div>
-        </div>
-    </div>
-@endsection

+ 22 - 11
resources/views/admin/config/system.blade.php

@@ -957,7 +957,8 @@
                                                     <option value="serverChan">ServerChan</option>
                                                     <option value="bark">Bark</option>
                                                 </select>
-                                                <span class="text-help">推送节点离线提醒、用户流量异常警告、节点使用报告(<a href="javascript:sendTestNotification();">发送测试消息</a>)</span>
+                                                <span class="text-help">推送节点离线提醒、用户流量异常警告、节点使用报告 @can('admin.test.notify')(<a href="javascript:sendTestNotification();
+">发送测试消息</a>)@endcan </span>
                                             </div>
                                         </div>
                                     </div>
@@ -1357,9 +1358,11 @@
                                     <div class="row">
                                         <div class="form-group col-lg-6 d-flex">
                                             <label class="col-md-3 col-form-label">易支付</label>
-                                            <div class="col-md-7">
-                                                <button class="btn btn-primary" type="button" onclick="epayInfo()">查询</button>
-                                            </div>
+                                            @can('admin.test.epay')
+                                                <div class="col-md-7">
+                                                    <button class="btn btn-primary" type="button" onclick="epayInfo()">查询</button>
+                                                </div>
+                                            @endcan
                                         </div>
                                         <div class="form-group col-lg-6 d-flex">
                                             <label class="col-md-3 col-form-label" for="epay_url">接口对接地址</label>
@@ -1632,13 +1635,17 @@
 
       // 系统设置更新
       function systemUpdate(systemItem, value) {
-        $.post('{{route('admin.system.update')}}', {_token: '{{csrf_token()}}', name: systemItem, value: value}, function(ret) {
-          if (ret.status === 'success') {
-            swal.fire({title: ret.message, icon: 'success', timer: 1500, showConfirmButton: false});
-          } else {
-            swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-          }
-        });
+          @can('admin.system.update')
+          $.post('{{route('admin.system.update')}}', {_token: '{{csrf_token()}}', name: systemItem, value: value}, function(ret) {
+            if (ret.status === 'success') {
+              swal.fire({title: ret.message, icon: 'success', timer: 1500, showConfirmButton: false});
+            } else {
+              swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+            }
+          });
+          @else
+          swal.fire({title: '您没有权限修改系统参数!', icon: 'error', timer: 1500, showConfirmButton: false});
+          @endcan
       }
 
       // 正常input更新
@@ -1681,6 +1688,7 @@
       }
 
       // 发送Bark测试消息
+      @can('admin.test.notify')
       function sendTestNotification() {
         $.post('{{route('admin.test.notify')}}', {_token: '{{csrf_token()}}'}, function(ret) {
           if (ret.status === 'success') {
@@ -1690,6 +1698,7 @@
           }
         });
       }
+      @endcan
 
       // 生成网站安全码
       function makeWebsiteSecurityCode() {
@@ -1698,6 +1707,7 @@
         });
       }
 
+      @can('admin.test.epay')
       function epayInfo() {
         $.get('{{route('admin.test.epay')}}', function(ret) {
           if (ret.status === 'success') {
@@ -1713,5 +1723,6 @@
           }
         });
       }
+        @endcan
     </script>
 @endsection

+ 19 - 7
resources/views/admin/coupon/index.blade.php

@@ -7,10 +7,16 @@
         <div class="panel">
             <div class="panel-heading">
                 <h1 class="panel-title">卡券列表</h1>
-                <div class="panel-actions btn-group">
-                    <button class="btn btn-info" onclick="exportCoupon()"><i class="icon wb-code"></i>批量导出</button>
-                    <a href="{{route('admin.coupon.create')}}" class="btn btn-primary"><i class="icon wb-plus"></i>生成</a>
-                </div>
+                @canany(['admin.coupon.export', 'admin.coupon.create'])
+                    <div class="panel-actions btn-group">
+                        @can('admin.coupon.export')
+                            <button class="btn btn-info" onclick="exportCoupon()"><i class="icon wb-code"></i>批量导出</button>
+                        @endcan
+                        @can('admin.coupon.create')
+                            <a href="{{route('admin.coupon.create')}}" class="btn btn-primary"><i class="icon wb-plus"></i>生成</a>
+                        @endcan
+                    </div>
+                @endcanany
             </div>
             <div class="panel-body">
                 <div class="form-row">
@@ -85,9 +91,11 @@
                             </td>
                             <td>
                                 @if($coupon->status !== 1)
-                                    <button class="btn btn-danger" onclick="delCoupon('{{$coupon->id}}','{{$coupon->name}}')">
-                                        <i class="icon wb-close"></i>
-                                    </button>
+                                    @can('admin.coupon.destroy')
+                                        <button class="btn btn-danger" onclick="delCoupon('{{$coupon->id}}','{{$coupon->name}}')">
+                                            <i class="icon wb-close"></i>
+                                        </button>
+                                    @endcan
                                 @endif
                             </td>
                         </tr>
@@ -134,6 +142,7 @@
             $('#status').val();
       }
 
+      @can('admin.coupon.export')
       // 批量导出卡券
       function exportCoupon() {
         swal.fire({
@@ -149,7 +158,9 @@
           }
         });
       }
+      @endcan
 
+      @can('admin.coupon.destroy')
       // 删除卡券
       function delCoupon(id, name) {
         swal.fire({
@@ -177,5 +188,6 @@
           }
         });
       }
+        @endcan
     </script>
 @endsection

+ 244 - 230
resources/views/admin/index.blade.php

@@ -5,270 +5,284 @@
 @section('content')
     <div class="page-content container-fluid">
         <div class="row" data-by-row="true">
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.user.index')}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-primary">
-                            <i class="icon md-account"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">总用户</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$totalUserCount}}</span>
-                            @if ($todayRegister)
-                                <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
+            @can('admin.user.index')
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.user.index')}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-primary">
+                                <i class="icon md-account"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">总用户</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$totalUserCount}}</span>
+                                @if ($todayRegister)
+                                    <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
                                     <i class="icon wb-triangle-up" aria-hidden="true"></i> {{$todayRegister}}
                                 </span>
-                            @endif
+                                @endif
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.user.index', ['enable'=>1])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-info">
-                            <i class="icon md-account"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">有效用户</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$enableUserCount}}</span>
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.user.index', ['enable'=>1])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-info">
+                                <i class="icon md-account"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">有效用户</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$enableUserCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.user.index', ['active'=>1])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-success">
-                            <i class="icon md-account"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">{{$expireDays}}日内活跃用户</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$activeUserCount}}</span>
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.user.index', ['active'=>1])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-success">
+                                <i class="icon md-account"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">{{$expireDays}}日内活跃用户</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$activeUserCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.user.index', ['unActive'=>1])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-warning">
-                            <i class="icon md-account"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">{{$expireDays}}日以上不活跃用户</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$unActiveUserCount}}</span>
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.user.index', ['unActive'=>1])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-warning">
+                                <i class="icon md-account"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">{{$expireDays}}日以上不活跃用户</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$unActiveUserCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.user.index', ['online'=>1])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-success">
-                            <i class="icon md-account"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">当前在线</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$onlineUserCount}}</span>
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.user.index', ['online'=>1])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-success">
+                                <i class="icon md-account"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">当前在线</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$onlineUserCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.user.index', ['expireWarning'=>1])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-danger">
-                            <i class="icon md-account"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">临近到期</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$expireWarningUserCount}}</span>
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.user.index', ['expireWarning'=>1])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-danger">
+                                <i class="icon md-account"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">临近到期</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$expireWarningUserCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.user.index', ['largeTraffic'=>1])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-warning">
-                            <i class="icon md-account"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">流量大户(超过90%的用户)</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$largeTrafficUserCount}}</span>
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.user.index', ['largeTraffic'=>1])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-warning">
+                                <i class="icon md-account"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">流量大户(超过90%的用户)</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$largeTrafficUserCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.user.index', ['flowAbnormal'=>1])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-danger">
-                            <i class="icon md-account"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">1小时内流量异常</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$flowAbnormalUserCount}}</span>
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.user.index', ['flowAbnormal'=>1])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-danger">
+                                <i class="icon md-account"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">1小时内流量异常</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$flowAbnormalUserCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.node.index')}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-primary">
-                            <i class="icon md-cloud"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">节点</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$nodeCount}}</span>
+                    </a>
+                </div>
+            @endcan
+            @can('admin.node.index')
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.node.index')}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-primary">
+                                <i class="icon md-cloud"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">节点</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$nodeCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.node.index', ['status'=>0])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-info">
-                            <i class="icon md-cloud-off"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">维护中的节点</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$unnormalNodeCount}}</span>
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.node.index', ['status'=>0])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-info">
+                                <i class="icon md-cloud-off"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">维护中的节点</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$unnormalNodeCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.log.traffic')}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-primary">
-                            <i class="icon md-time-countdown"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">记录的消耗流量</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$totalFlowCount}}</span>
+                    </a>
+                </div>
+            @endcan
+            @can('admin.log.traffic')
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.log.traffic')}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-primary">
+                                <i class="icon md-time-countdown"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">记录的消耗流量</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$totalFlowCount}}</span>
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.log.traffic')}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-primary">
-                            <i class="icon md-time-countdown"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">30日内消耗流量</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$flowCount}}</span>
-                            @if($todayFlowCount !== '0B')
-                                <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.log.traffic')}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-primary">
+                                <i class="icon md-time-countdown"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">30日内消耗流量</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$flowCount}}</span>
+                                @if($todayFlowCount !== '0B')
+                                    <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
                                     <i class="icon wb-triangle-up" aria-hidden="true"></i> {{$todayFlowCount}}
                                 </span>
-                            @endif
+                                @endif
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.order')}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-primary">
-                            <i class="icon md-ticket-star"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">总订单数</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$totalOrder}}</span>
-                            @if($todayOrder)
-                                <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
+                    </a>
+                </div>
+            @endcan
+            @can('admin.order')
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.order')}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-primary">
+                                <i class="icon md-ticket-star"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">总订单数</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$totalOrder}}</span>
+                                @if($todayOrder)
+                                    <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
                                     <i class="icon wb-triangle-up" aria-hidden="true"></i> {{$todayOrder}}
                                 </span>
-                            @endif
+                                @endif
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.order')}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-info">
-                            <i class="icon md-ticket-star"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">在线支付订单数</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$totalOnlinePayOrder}}</span>
-                            @if($todayOnlinePayOrder)
-                                <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.order')}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-info">
+                                <i class="icon md-ticket-star"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">在线支付订单数</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$totalOnlinePayOrder}}</span>
+                                @if($todayOnlinePayOrder)
+                                    <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
                                     <i class="icon wb-triangle-up" aria-hidden="true"></i> {{$todayOnlinePayOrder}}
                                 </span>
-                            @endif
+                                @endif
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.order', ['status'=>2])}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-success">
-                            <i class="icon md-ticket-star"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">支付成功订单数</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$totalSuccessOrder}}</span>
-                            @if($todaySuccessOrder)
-                                <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
+                    </a>
+                </div>
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.order', ['status'=>2])}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-success">
+                                <i class="icon md-ticket-star"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">支付成功订单数</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$totalSuccessOrder}}</span>
+                                @if($todaySuccessOrder)
+                                    <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
                                     <i class="icon wb-triangle-up" aria-hidden="true"></i> {{$todaySuccessOrder}}
                                 </span>
-                            @endif
+                                @endif
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <div class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-primary">
-                            <i class="icon md-money"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">总余额</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$totalCredit}}</span>
+                    </a>
+                </div>
+            @endcan
+            @can('admin.log.credit')
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <div class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-primary">
+                                <i class="icon md-money"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">总余额</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$totalCredit}}</span>
+                            </div>
                         </div>
                     </div>
                 </div>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <a href="{{route('admin.aff.rebate')}}" class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-warning">
-                            <i class="icon md-money"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">待提现佣金</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$totalWaitRefAmount}}</span>
-                            @if($todayWaitRefAmount)
-                                <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
+            @endcan
+            @can('admin.aff.rebate')
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <a href="{{route('admin.aff.rebate')}}" class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-warning">
+                                <i class="icon md-money"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">待提现佣金</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$totalWaitRefAmount}}</span>
+                                @if($todayWaitRefAmount)
+                                    <span class="badge badge-success badge-round up font-size-20 m-0" style="top:-20px">
                                     <i class="icon wb-triangle-up" aria-hidden="true"></i> {{$todayWaitRefAmount}}
                                 </span>
-                            @endif
+                                @endif
+                            </div>
                         </div>
-                    </div>
-                </a>
-            </div>
-            <div class="col-xl-3 col-md-6 info-panel">
-                <div class="card card-shadow">
-                    <div class="card-block bg-white">
-                        <button type="button" class="btn btn-floating btn-sm btn-dark">
-                            <i class="icon md-money"></i>
-                        </button>
-                        <span class="ml-15 font-weight-400">已支出佣金</span>
-                        <div class="content-text text-center mb-0">
-                            <span class="font-size-40 font-weight-100">{{$totalRefAmount}}</span>
+                    </a>
+                </div>
+            @endcan
+            @can('admin.aff.index')
+                <div class="col-xl-3 col-md-6 info-panel">
+                    <div class="card card-shadow">
+                        <div class="card-block bg-white">
+                            <button type="button" class="btn btn-floating btn-sm btn-dark">
+                                <i class="icon md-money"></i>
+                            </button>
+                            <span class="ml-15 font-weight-400">已支出佣金</span>
+                            <div class="content-text text-center mb-0">
+                                <span class="font-size-40 font-weight-100">{{$totalRefAmount}}</span>
+                            </div>
                         </div>
                     </div>
                 </div>
-            </div>
+            @endcan
         </div>
     </div>
 @endsection

+ 63 - 56
resources/views/admin/inviteList.blade.php

@@ -12,9 +12,11 @@
                             {{trans('home.invite_code_make')}}
                         </h4>
                         <x-alert type="info" :message="trans('home.invite_code_tips', ['num'=>10, 'days' => sysConfig('user_invite_days')])"/>
-                        <button type="button" class="btn btn-primary btn-animate btn-animate-side" onclick="makeInvite()">
-                            <i class="icon wb-plus"></i> {{trans('home.invite_code_button')}}
-                        </button>
+                        @can('admin.invite.create')
+                            <button type="button" class="btn btn-primary btn-animate btn-animate-side" onclick="makeInvite()">
+                                <i class="icon wb-plus"></i> {{trans('home.invite_code_button')}}
+                            </button>
+                        @endcan
                     </div>
                 </div>
             </div>
@@ -24,9 +26,11 @@
                         <h4 class="panel-title cyan-600">
                             <i class="icon wb-extension"></i>{{trans('home.invite_code_my_codes')}}
                         </h4>
-                        <div class="panel-actions">
-                            <button class="btn btn-primary" onclick="exportInvite()">批量导出</button>
-                        </div>
+                        @can('admin.invite.export')
+                            <div class="panel-actions">
+                                <button class="btn btn-primary" onclick="exportInvite()">批量导出</button>
+                            </div>
+                        @endcan
                     </div>
                     <div class="panel-body">
                         <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
@@ -84,61 +88,64 @@
 @section('javascript')
     <script src="/assets/custom/Plugin/clipboardjs/clipboard.min.js" type="text/javascript"></script>
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
-    <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js"
-            type="text/javascript"></script>
+    <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
     <script type="text/javascript">
-      // 生成邀请码
-      function makeInvite() {
-        $.ajax({
-          method: 'POST',
-          url: '{{route('admin.invite.create')}}',
-          async: false,
-          data: {_token: '{{csrf_token()}}'},
-          dataType: 'json',
-          success: function(ret) {
-            if (ret.status === 'success') {
-              swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-            } else {
-              swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-            }
-          },
-        });
+        @can('admin.invite.create')
+        // 生成邀请码
+        function makeInvite() {
+          $.ajax({
+            method: 'POST',
+            url: '{{route('admin.invite.create')}}',
+            async: false,
+            data: {_token: '{{csrf_token()}}'},
+            dataType: 'json',
+            success: function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+              } else {
+                swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+              }
+            },
+          });
 
-        return false;
-      }
+          return false;
+        }
+        @endcan
 
-      // 导出邀请码
-      function exportInvite() {
-        swal.fire({
-          title: '提示',
-          text: '确定导出所有邀请码吗',
-          icon: 'question',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            window.location.href = '{{route('admin.invite.export')}}';
-          }
-        });
-      }
+        @can('admin.invite.export')
+        // 导出邀请码
+        function exportInvite() {
+          swal.fire({
+            title: '提示',
+            text: '确定导出所有邀请码吗',
+            icon: 'question',
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              window.location.href = '{{route('admin.invite.export')}}';
+            }
+          });
+        }
+        @endcan
 
-      const clipboard = new ClipboardJS('.mt-clipboard');
-      clipboard.on('success', function() {
-        swal.fire({
-          title: '复制成功',
-          icon: 'success',
-          timer: 1300,
-          showConfirmButton: false,
+        const clipboard = new ClipboardJS('.mt-clipboard');
+        clipboard.on('success', function() {
+          swal.fire({
+            title: '复制成功',
+            icon: 'success',
+            timer: 1300,
+            showConfirmButton: false,
+          });
         });
-      });
-      clipboard.on('error', function() {
-        swal.fire({
-          title: '复制失败,请手动复制',
-          icon: 'error',
-          timer: 1500,
-          showConfirmButton: false,
+        clipboard.on('error', function() {
+          swal.fire({
+            title: '复制失败,请手动复制',
+            icon: 'error',
+            timer: 1500,
+            showConfirmButton: false,
+          });
         });
-      });
     </script>
 @endsection

+ 477 - 439
resources/views/admin/layouts.blade.php

@@ -1,452 +1,490 @@
-<!DOCTYPE html>
-<!--[if IE 8]>
-<html lang="{{app()->getLocale()}}" class="ie8 no-js css-menubar"> <![endif]-->
-<!--[if IE 9]>
-<html lang="{{app()->getLocale()}}" class="ie9 no-js css-menubar"> <![endif]-->
-<!--[if !IE]><!-->
-<html lang="{{app()->getLocale()}}">
-<!--<![endif]-->
-<head>
-    <meta charset="utf-8">
-    <title>{{sysConfig('website_name')}}</title>
-    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <meta name="description" content="">
-    <meta name="keywords" content="">
-    <meta name="author" content="兔姬菌">
-    <meta name="copyright" content="2017-2020©兔姬菌">
-    <link href="{{asset('favicon.ico')}}" rel="shortcut icon apple-touch-icon">
-    <!-- 样式表/Stylesheets -->
-    <link href="/assets/global/css/bootstrap.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/css/bootstrap-extend.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/css/site.min.css" type="text/css" rel="stylesheet">
-    <!-- 插件/Plugins -->
-    <link href="/assets/global/vendor/animsition/animsition.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/asscrollable/asScrollable.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/slidepanel/slidePanel.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/flag-icon-css/flag-icon.min.css" type="text/css" rel="stylesheet">
-    <!-- 字体/Fonts -->
-    <link href="/assets/global/fonts/web-icons/web-icons.min.css" type="text/css" rel="stylesheet">
-    <link href="//fonts.loli.net/css?family=Roboto:300,400,500,300italic" type="text/css" rel="stylesheet">
-    <!--[if lt IE 9]>
-    <script src="/assets/global/vendor/html5shiv/html5shiv.min.js" type="text/javascript"></script>
-    <![endif]-->
-    <!--[if lt IE 10]>
-    <script src="/assets/global/vendor/media-match/media.match.min.js" type="text/javascript"></script>
-    <script src="/assets/global/vendor/respond/respond.min.js" type="text/javascript"></script>
-    <![endif]-->
-    <!-- Scripts -->
-    <script src="/assets/global/vendor/breakpoints/breakpoints.min.js" type="text/javascript"></script>
-    <script type="text/javascript">
-      Breakpoints();
-    </script>
+@extends('_layout')
+@section('title', sysConfig('website_name'))
+@section('layout_css')
     @yield('css')
-</head>
-
-<body class="animsition dashboard">
-<nav class="site-navbar navbar navbar-default navbar-fixed-top navbar-mega navbar-inverse bg-indigo-600" role="navigation">
-    <div class="navbar-header">
-        <button type="button" class="navbar-toggler hamburger hamburger-close navbar-toggler-left hided" data-toggle="menubar">
-            <span class="sr-only">Toggle navigation</span>
-            <span class="hamburger-bar"></span>
-        </button>
-        <button type="button" class="navbar-toggler collapsed" data-target="#site-navbar-collapse" data-toggle="collapse">
-            <i class="icon wb-more-horizontal" aria-hidden="true"></i>
-        </button>
-        <div class="navbar-brand navbar-brand-center">
-            <img src="{{sysConfig('website_logo')? :'/assets/images/logo64.png'}}" class="navbar-brand-logo" alt="logo"/>
-            <span class="navbar-brand-text hidden-xs-down"> {{sysConfig('website_name')}}</span>
-        </div>
-    </div>
-    <div class="navbar-container container-fluid">
-        <div class="collapse navbar-collapse navbar-collapse-toolbar" id="site-navbar-collapse">
-            <ul class="nav navbar-toolbar">
-                <li class="nav-item hidden-float" id="toggleMenubar">
-                    <a class="nav-link" data-toggle="menubar" href="#" role="button">
-                        <i class="icon hamburger hamburger-arrow-left">
-                            <span class="sr-only">菜单</span>
-                            <span class="hamburger-bar"></span>
-                        </i>
-                    </a>
-                </li>
-                <li class="nav-item hidden-sm-down">
-                    <a class="nav-link icon icon-fullscreen" data-toggle="fullscreen" href="#" role="button">
-                        <span class="sr-only">全屏</span>
-                    </a>
-                </li>
-            </ul>
-            <ul class="nav navbar-toolbar navbar-right navbar-toolbar-right">
-                <li class="nav-item dropdown">
-                    <a class="nav-link navbar-avatar" data-toggle="dropdown" href="#" aria-expanded="false" data-animation="scale-up" role="button">
-                        <span class="avatar avatar-online">
-                            <img src="/assets/images/avatar.svg" alt="..."/>
-                            <i></i>
-                        </span>
-                    </a>
-                    <div class="dropdown-menu" role="menu">
-                        <a class="dropdown-item" href="/" role="menuitem">
-                            <i class="icon wb-settings" aria-hidden="true"></i>
-                            个人中心
-                        </a>
-                        <a class="dropdown-item" href="{{route('admin.profile')}}" role="menuitem">
-                            <i class="icon wb-user" aria-hidden="true"></i>
-                            {{trans('home.profile')}}
-                        </a>
-                        <div class="dropdown-divider" role="presentation"></div>
-                        <a class="dropdown-item" href="{{route('logout')}}" role="menuitem">
-                            <i class="icon wb-power" aria-hidden="true"></i>
-                            {{trans('home.logout')}}
-                        </a>
-                    </div>
-                </li>
-            </ul>
+@endsection
+@section('body_class', 'dashboard')
+@section('layout_content')
+    <nav class="site-navbar navbar navbar-default navbar-fixed-top navbar-mega navbar-inverse bg-indigo-600" role="navigation">
+        <div class="navbar-header">
+            <button type="button" class="navbar-toggler hamburger hamburger-close navbar-toggler-left hided" data-toggle="menubar">
+                <span class="sr-only">Toggle navigation</span>
+                <span class="hamburger-bar"></span>
+            </button>
+            <button type="button" class="navbar-toggler collapsed" data-target="#site-navbar-collapse" data-toggle="collapse">
+                <i class="icon wb-more-horizontal" aria-hidden="true"></i>
+            </button>
+            <div class="navbar-brand navbar-brand-center">
+                <img src="{{sysConfig('website_logo')? :'/assets/images/logo64.png'}}" class="navbar-brand-logo" alt="logo"/>
+                <span class="navbar-brand-text hidden-xs-down"> {{sysConfig('website_name')}}</span>
+            </div>
         </div>
-    </div>
-</nav>
-<div class="site-menubar site-menubar-light">
-    <div class="site-menubar-body">
-        <ul class="site-menu" data-plugin="menu">
-            <li class="site-menu-item {{request()->routeIs('admin.index') ? 'active open' : ''}}">
-                <a href="{{route('admin.index')}}">
-                    <i class="site-menu-icon wb-dashboard" aria-hidden="true"></i>
-                    <span class="site-menu-title">管理中心</span>
-                </a>
-            </li>
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.user.*', 'admin.log.credit', 'admin.subscribe.*') ? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-user" aria-hidden="true"></i>
-                    <span class="site-menu-title">用户系统</span>
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.user.index', 'admin.user.edit', 'admin.user.monitor', 'admin.user.online', 'admin.user.online', 'admin.user.export') ? 'active open' : ''}}">
-                        <a href="{{route('admin.user.index')}}">
-                            <span class="site-menu-title">用户管理</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.user.group.*') ? 'active open' : ''}}">
-                        <a href="{{route('admin.user.group.index')}}">
-                            <span class="site-menu-title">用戶分组</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.log.credit') ? 'active open' : ''}}">
-                        <a href="{{route('admin.log.credit')}}">
-                            <span class="site-menu-title">余额变动</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.subscribe.*')? 'active open' : ''}}">
-                        <a href="{{route('admin.subscribe.index')}}">
-                            <span class="site-menu-title">订阅管理</span>
-                        </a>
-                    </li>
-                </ul>
-            </li>
-            @php
-                $openTicket = App\Models\Ticket::whereStatus(0)->count()
-            @endphp
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.ticket.*', 'admin.article.*', 'admin.marketing.*')? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-chat-working" aria-hidden="true"></i>
-                    <span class="site-menu-title">客服系统</span>
-                    @if($openTicket > 0)
-                        <div class="site-menu-badge">
-                            <span class="badge badge-pill badge-success">{{$openTicket}}</span>
-                        </div>
-                    @endif
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.ticket.*') ? 'active open' : ''}}">
-                        <a href="{{route('admin.ticket.index')}}">
-                            <span class="site-menu-title">服务工单</span>
-                            @if($openTicket > 0)
-                                <div class="site-menu-label">
-                                    <span class="badge badge-danger badge-round mr-25">{{$openTicket}}</span>
-                                </div>
-                            @endif
+        <div class="navbar-container container-fluid">
+            <div class="collapse navbar-collapse navbar-collapse-toolbar" id="site-navbar-collapse">
+                <ul class="nav navbar-toolbar">
+                    <li class="nav-item hidden-float" id="toggleMenubar">
+                        <a class="nav-link" data-toggle="menubar" href="#" role="button">
+                            <i class="icon hamburger hamburger-arrow-left">
+                                <span class="sr-only">菜单</span>
+                                <span class="hamburger-bar"></span>
+                            </i>
                         </a>
                     </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.article.*')? 'active open' : ''}}">
-                        <a href="{{route('admin.article.index')}}">
-                            <span class="site-menu-title">文章管理</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.marketing.push') ? 'active open' : ''}}">
-                        <a href="{{route('admin.marketing.push')}}">
-                            <span class="site-menu-title">消息推送</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.marketing.email') ? 'active open' : ''}}">
-                        <a href="{{route('admin.marketing.email')}}">
-                            <span class="site-menu-title">邮件群发</span>
-                        </a>
-                    </li>
-                </ul>
-            </li>
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.node.*') ? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-grid-4" aria-hidden="true"></i>
-                    <span class="site-menu-title">线路系统</span>
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.node.index', 'admin.node.create', 'admin.node.edit')? 'active open' : ''}}">
-                        <a href="{{route('admin.node.index')}}">
-                            <span class="site-menu-title">线路管理</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.node.auth.*') ? 'active open' : ''}}">
-                        <a href="{{route('admin.node.auth.index')}}">
-                            <span class="site-menu-title">线路授权</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.node.cert.*') ? 'active open' : ''}}">
-                        <a href="{{route('admin.node.cert.index')}}">
-                            <span class="site-menu-title">证书列表</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.node.pingLog') ? 'active open' : ''}}">
-                        <a href="{{route('admin.node.pingLog')}}">
-                            <span class="site-menu-title">测速日志</span>
-                        </a>
-                    </li>
-                </ul>
-            </li>
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.rule.*') ? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-eye" aria-hidden="true"></i>
-                    <span class="site-menu-title">审计规则</span>
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.rule.index') ? 'active open' : ''}}">
-                        <a href="{{route('admin.rule.index')}}">
-                            <span class="site-menu-title">规则列表</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.rule.group.*') ? 'active open' : ''}}">
-                        <a href="{{route('admin.rule.group.index')}}">
-                            <span class="site-menu-title">规则分组</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.rule.log') ? 'active open' : ''}}">
-                        <a href="{{route('admin.rule.log')}}">
-                            <span class="site-menu-title">触发记录</span>
+                    <li class="nav-item hidden-sm-down">
+                        <a class="nav-link icon icon-fullscreen" data-toggle="fullscreen" href="#" role="button">
+                            <span class="sr-only">全屏</span>
                         </a>
                     </li>
                 </ul>
-            </li>
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.goods.*', 'admin.coupon.*', 'admin.order') ? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-shopping-cart" aria-hidden="true"></i>
-                    <span class="site-menu-title">商品系统</span>
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.goods.*') ? 'active open' : ''}}">
-                        <a href="{{route('admin.goods.index')}}">
-                            <span class="site-menu-title">商品管理</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.coupon.*') ? 'active open' : ''}}">
-                        <a href="{{route('admin.coupon.index')}}">
-                            <span class="site-menu-title">卡券管理</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.order') ? 'active open' : ''}}">
-                        <a href="{{route('admin.order')}}">
-                            <span class="site-menu-title">商品订单</span>
+                <ul class="nav navbar-toolbar navbar-right navbar-toolbar-right">
+                    <li class="nav-item dropdown">
+                        <a class="nav-link navbar-avatar" data-toggle="dropdown" href="#" aria-expanded="false" data-animation="scale-up" role="button">
+                        <span class="avatar avatar-online">
+                            <img src="/assets/images/avatar.svg" alt="..."/>
+                            <i></i>
+                        </span>
                         </a>
-                    </li>
-                </ul>
-            </li>
-            @php
-                $openApply = App\Models\ReferralApply::whereStatus(0)->count()
-            @endphp
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.invite', 'admin.aff.*') ? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-thumb-up" aria-hidden="true"></i>
-                    <span class="site-menu-title">推广系统</span>
-                    @if($openApply > 0)
-                        <div class="site-menu-badge">
-                            <span class="badge badge-pill badge-success">{{$openApply}}</span>
+                        <div class="dropdown-menu" role="menu">
+                            <a class="dropdown-item" href="/" role="menuitem">
+                                <i class="icon wb-settings" aria-hidden="true"></i>
+                                个人中心
+                            </a>
+                            <div class="dropdown-divider" role="presentation"></div>
+                            <a class="dropdown-item" href="{{route('logout')}}" role="menuitem">
+                                <i class="icon wb-power" aria-hidden="true"></i>
+                                {{trans('home.logout')}}
+                            </a>
                         </div>
-                    @endif
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.invite') ? 'active open' : ''}}">
-                        <a href="{{route('admin.invite')}}">
-                            <span class="site-menu-title">邀请管理</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.aff.index', 'admin.aff.detail') ? 'active open' : ''}}">
-                        <a href="{{route('admin.aff.index')}}">
-                            <span class="site-menu-title">提现管理</span>
-                            @if($openApply > 0)
-                                <div class="site-menu-label">
-                                    <span class="badge badge-danger badge-round mr-25">{{$openApply}}</span>
-                                </div>
-                            @endif
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.aff.rebate') ? 'active open' : ''}}">
-                        <a href="{{route('admin.aff.rebate')}}">
-                            <span class="site-menu-title">返利流水</span>
-                        </a>
-                    </li>
-                </ul>
-            </li>
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.log.traffic', 'admin.log.flow', 'admin.log.ban', 'admin.log.ip', 'admin.log.online', 'admin.log.notify', 'admin.payment.callback') ? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-calendar" aria-hidden="true"></i>
-                    <span class="site-menu-title">日志系统</span>
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.log.traffic') ? 'active open' : ''}}">
-                        <a href="{{route('admin.log.traffic')}}">
-                            <span class="site-menu-title">流量使用</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.log.flow') ? 'active open' : ''}}">
-                        <a href="{{route('admin.log.flow')}}">
-                            <span class="site-menu-title">流量变动</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.log.ban') ? 'active open' : ''}}">
-                        <a href="{{route('admin.log.ban')}}">
-                            <span class="site-menu-title">封禁记录</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.log.ip') ? 'active open' : ''}}">
-                        <a href="{{route('admin.log.ip')}}">
-                            <span class="site-menu-title">在线记录</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.log.online') ? 'active open' : ''}}">
-                        <a href="{{route('admin.log.online')}}">
-                            <span class="site-menu-title">在线监控</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.log.notify') ? 'active open' : ''}}">
-                        <a href="{{route('admin.log.notify')}}">
-                            <span class="site-menu-title">通知记录</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.payment.callback') ? 'active open' : ''}}">
-                        <a href="{{route('admin.payment.callback')}}">
-                            <span class="site-menu-title">支付回调</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.log.viewer') ? 'active open' : ''}}">
-                        <a href="{{route('admin.log.viewer')}}" target="_blank">
-                            <span class="site-menu-title">系统运行</span>
-                        </a>
-                    </li>
-                </ul>
-            </li>
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.tools.*') ? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-briefcase" aria-hidden="true"></i>
-                    <span class="site-menu-title">工具箱</span>
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.tools.decompile') ? 'active open' : ''}}">
-                        <a href="{{route('admin.tools.decompile')}}">
-                            <span class="site-menu-title">反解析</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.tools.convert') ? 'active open' : ''}}">
-                        <a href="{{route('admin.tools.convert')}}">
-                            <span class="site-menu-title">格式转换</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.tools.import') ? 'active open' : ''}}">
-                        <a href="{{route('admin.tools.import')}}">
-                            <span class="site-menu-title">数据导入</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.tools.analysis') ? 'active open' : ''}}">
-                        <a href="{{route('admin.tools.analysis')}}">
-                            <span class="site-menu-title">日志分析</span>
-                        </a>
-                    </li>
-                </ul>
-            </li>
-            <li class="site-menu-item has-sub {{request()->routeIs('admin.config.*', 'admin.system') ? 'active open' : ''}}">
-                <a href="javascript:void(0)">
-                    <i class="site-menu-icon wb-settings" aria-hidden="true"></i>
-                    <span class="site-menu-title">设置</span>
-                </a>
-                <ul class="site-menu-sub">
-                    <li class="site-menu-item {{request()->routeIs('admin.config.filter.index') ? 'active open' : ''}}">
-                        <a href="{{route('admin.config.filter.index')}}">
-                            <span class="site-menu-title">邮箱后缀管理</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.config.common.*') ? 'active open' : ''}}">
-                        <a href="{{route('admin.config')}}">
-                            <span class="site-menu-title">通用配置</span>
-                        </a>
-                    </li>
-                    <li class="site-menu-item {{request()->routeIs('admin.system') ? 'active open' : ''}}">
-                        <a href="{{route('admin.system')}}">
-                            <span class="site-menu-title">系统设置</span>
-                        </a>
                     </li>
                 </ul>
-            </li>
-        </ul>
+            </div>
+        </div>
+    </nav>
+    <div class="site-menubar site-menubar-light">
+        <div class="site-menubar-body">
+            <ul class="site-menu" data-plugin="menu">
+                @can('admin.index')
+                    <li class="site-menu-item {{request()->routeIs('admin.index') ? 'active open' : ''}}">
+                        <a href="{{route('admin.index')}}">
+                            <i class="site-menu-icon wb-dashboard" aria-hidden="true"></i>
+                            <span class="site-menu-title">管理中心</span>
+                        </a>
+                    </li>
+                @endcan
+                @canany(['admin.user.index', 'admin.user.group.index', 'admin.log.credit', 'admin.subscribe.index'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.user.*', 'admin.log.credit', 'admin.subscribe.*') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-user" aria-hidden="true"></i>
+                            <span class="site-menu-title">用户系统</span>
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.user.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.user.index', 'admin.user.edit', 'admin.user.monitor', 'admin.user.online', 'admin.user.online', 'admin.user.export') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.user.index')}}">
+                                        <span class="site-menu-title">用户管理</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.user.group.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.user.group.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.user.group.index')}}">
+                                        <span class="site-menu-title">用戶分组</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.log.credit')
+                                <li class="site-menu-item {{request()->routeIs('admin.log.credit') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.log.credit')}}">
+                                        <span class="site-menu-title">余额变动</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.subscribe.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.subscribe.*')? 'active open' : ''}}">
+                                    <a href="{{route('admin.subscribe.index')}}">
+                                        <span class="site-menu-title">订阅管理</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.permission.index', 'admin.role.index'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.permission.*', 'admin.role.*') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-users" aria-hidden="true"></i>
+                            <span class="site-menu-title">权限系统</span>
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.permission.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.permission.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.permission.index')}}">
+                                        <span class="site-menu-title">权限列表</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.role.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.role.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.role.index')}}">
+                                        <span class="site-menu-title">角色列表</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.ticket.index', 'admin.article.index', 'admin.marketing.push', 'admin.marketing.email'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.ticket.*', 'admin.article.*', 'admin.marketing.*')? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-chat-working" aria-hidden="true"></i>
+                            <span class="site-menu-title">客服系统</span>
+                            @can('admin.ticket.index')
+                                @php
+                                    $openTicket = App\Models\Ticket::whereStatus(0)->count()
+                                @endphp
+                                @if($openTicket > 0)
+                                    <div class="site-menu-badge">
+                                        <span class="badge badge-pill badge-success">{{$openTicket}}</span>
+                                    </div>
+                                @endif
+                            @endcan
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.ticket.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.ticket.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.ticket.index')}}">
+                                        <span class="site-menu-title">服务工单</span>
+                                        @if($openTicket > 0)
+                                            <div class="site-menu-label">
+                                                <span class="badge badge-danger badge-round mr-25">{{$openTicket}}</span>
+                                            </div>
+                                        @endif
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.article.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.article.*')? 'active open' : ''}}">
+                                    <a href="{{route('admin.article.index')}}">
+                                        <span class="site-menu-title">文章管理</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.marketing.push')
+                                <li class="site-menu-item {{request()->routeIs('admin.marketing.push') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.marketing.push')}}">
+                                        <span class="site-menu-title">消息推送</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.marketing.email')
+                                <li class="site-menu-item {{request()->routeIs('admin.marketing.email') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.marketing.email')}}">
+                                        <span class="site-menu-title">邮件群发</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.node.index', 'admin.node.auth.index', 'admin.node.cert.index', 'admin.node.pingLog'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.node.*') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-grid-4" aria-hidden="true"></i>
+                            <span class="site-menu-title">线路系统</span>
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.node.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.node.index', 'admin.node.create', 'admin.node.edit')? 'active open' : ''}}">
+                                    <a href="{{route('admin.node.index')}}">
+                                        <span class="site-menu-title">线路管理</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.node.auth.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.node.auth.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.node.auth.index')}}">
+                                        <span class="site-menu-title">线路授权</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.node.cert.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.node.cert.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.node.cert.index')}}">
+                                        <span class="site-menu-title">证书列表</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.node.pingLog')
+                                <li class="site-menu-item {{request()->routeIs('admin.node.pingLog') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.node.pingLog')}}">
+                                        <span class="site-menu-title">测速日志</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.rule.index', 'admin.rule.group.index', 'admin.rule.log'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.rule.*') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-eye" aria-hidden="true"></i>
+                            <span class="site-menu-title">审计规则</span>
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.rule.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.rule.index') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.rule.index')}}">
+                                        <span class="site-menu-title">规则列表</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.rule.group.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.rule.group.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.rule.group.index')}}">
+                                        <span class="site-menu-title">规则分组</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.rule.log')
+                                <li class="site-menu-item {{request()->routeIs('admin.rule.log') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.rule.log')}}">
+                                        <span class="site-menu-title">触发记录</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.goods.index', 'admin.coupon.index', 'admin.order'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.goods.*', 'admin.coupon.*', 'admin.order') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-shopping-cart" aria-hidden="true"></i>
+                            <span class="site-menu-title">商品系统</span>
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.goods.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.goods.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.goods.index')}}">
+                                        <span class="site-menu-title">商品管理</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.coupon.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.coupon.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.coupon.index')}}">
+                                        <span class="site-menu-title">卡券管理</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.order')
+                                <li class="site-menu-item {{request()->routeIs('admin.order') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.order')}}">
+                                        <span class="site-menu-title">商品订单</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.invite.index', 'admin.aff.index', 'admin.aff.rebate'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.invite.*', 'admin.aff.*') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-thumb-up" aria-hidden="true"></i>
+                            <span class="site-menu-title">推广系统</span>
+                            @can('admin.aff.index')
+                                @php
+                                    $openApply = App\Models\ReferralApply::whereStatus(0)->count()
+                                @endphp
+                                @if($openApply > 0)
+                                    <div class="site-menu-badge">
+                                        <span class="badge badge-pill badge-success">{{$openApply}}</span>
+                                    </div>
+                                @endif
+                            @endcan
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.invite.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.invite.index') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.invite.index')}}">
+                                        <span class="site-menu-title">邀请管理</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.aff.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.aff.index', 'admin.aff.detail') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.aff.index')}}">
+                                        <span class="site-menu-title">提现管理</span>
+                                        @if($openApply > 0)
+                                            <div class="site-menu-label">
+                                                <span class="badge badge-danger badge-round mr-25">{{$openApply}}</span>
+                                            </div>
+                                        @endif
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.aff.rebate')
+                                <li class="site-menu-item {{request()->routeIs('admin.aff.rebate') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.aff.rebate')}}">
+                                        <span class="site-menu-title">返利流水</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.log.traffic', 'admin.log.flow', 'admin.log.ban', 'admin.log.ip', 'admin.log.online', 'admin.log.notify', 'admin.payment.callback'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.log.traffic', 'admin.log.flow', 'admin.log.ban', 'admin.log.ip', 'admin.log.online', 'admin.log.notify', 'admin.payment.callback') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-calendar" aria-hidden="true"></i>
+                            <span class="site-menu-title">日志系统</span>
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.log.traffic')
+                                <li class="site-menu-item {{request()->routeIs('admin.log.traffic') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.log.traffic')}}">
+                                        <span class="site-menu-title">流量使用</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.log.flow')
+                                <li class="site-menu-item {{request()->routeIs('admin.log.flow') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.log.flow')}}">
+                                        <span class="site-menu-title">流量变动</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.log.ban')
+                                <li class="site-menu-item {{request()->routeIs('admin.log.ban') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.log.ban')}}">
+                                        <span class="site-menu-title">封禁记录</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.log.ip')
+                                <li class="site-menu-item {{request()->routeIs('admin.log.ip') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.log.ip')}}">
+                                        <span class="site-menu-title">在线记录</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.log.online')
+                                <li class="site-menu-item {{request()->routeIs('admin.log.online') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.log.online')}}">
+                                        <span class="site-menu-title">在线监控</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.log.notify')
+                                <li class="site-menu-item {{request()->routeIs('admin.log.notify') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.log.notify')}}">
+                                        <span class="site-menu-title">通知记录</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.payment.callback')
+                                <li class="site-menu-item {{request()->routeIs('admin.payment.callback') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.payment.callback')}}">
+                                        <span class="site-menu-title">支付回调</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.log.viewer')
+                                <li class="site-menu-item {{request()->routeIs('admin.log.viewer') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.log.viewer')}}" target="_blank">
+                                        <span class="site-menu-title">系统运行</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.tools.decompile', 'admin.tools.convert', 'admin.tools.import', 'admin.tools.analysis'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.tools.*') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-briefcase" aria-hidden="true"></i>
+                            <span class="site-menu-title">工具箱</span>
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.tools.decompile')
+                                <li class="site-menu-item {{request()->routeIs('admin.tools.decompile') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.tools.decompile')}}">
+                                        <span class="site-menu-title">反解析</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.tools.convert')
+                                <li class="site-menu-item {{request()->routeIs('admin.tools.convert') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.tools.convert')}}">
+                                        <span class="site-menu-title">格式转换</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.tools.import')
+                                <li class="site-menu-item {{request()->routeIs('admin.tools.import') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.tools.import')}}">
+                                        <span class="site-menu-title">数据导入</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.tools.analysis')
+                                <li class="site-menu-item {{request()->routeIs('admin.tools.analysis') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.tools.analysis')}}">
+                                        <span class="site-menu-title">日志分析</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+                @canany(['admin.config.filter.index', 'admin.config', 'admin.system.index'])
+                    <li class="site-menu-item has-sub {{request()->routeIs('admin.config.*', 'admin.system.index') ? 'active open' : ''}}">
+                        <a href="javascript:void(0)">
+                            <i class="site-menu-icon wb-settings" aria-hidden="true"></i>
+                            <span class="site-menu-title">设置</span>
+                        </a>
+                        <ul class="site-menu-sub">
+                            @can('admin.config.filter.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.config.filter.index') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.config.filter.index')}}">
+                                        <span class="site-menu-title">邮箱后缀管理</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.config')
+                                <li class="site-menu-item {{request()->routeIs('admin.config.common.*') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.config')}}">
+                                        <span class="site-menu-title">通用配置</span>
+                                    </a>
+                                </li>
+                            @endcan
+                            @can('admin.system.index')
+                                <li class="site-menu-item {{request()->routeIs('admin.system.index') ? 'active open' : ''}}">
+                                    <a href="{{route('admin.system.index')}}">
+                                        <span class="site-menu-title">系统设置</span>
+                                    </a>
+                                </li>
+                            @endcan
+                        </ul>
+                    </li>
+                @endcanany
+            </ul>
+        </div>
+    </div>
+    <div class="page">
+        <!--[if lt IE 8]>
+        <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
+            <a href="http://browsehappy.com/">升级您的浏览器</a> <br/>You are using an <strong>outdated</strong> browser. Please
+            <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.
+        </p>
+        <![endif]-->
+        @yield('content')
     </div>
-</div>
-<div class="page">
-    <!--[if lt IE 8]>
-    <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
-        <a href="http://browsehappy.com/">升级您的浏览器</a> <br/>You are using an <strong>outdated</strong> browser. Please
-        <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.
-    </p>
+    @endsection
+@section('layout_javascript')
+    <!--[if lt IE 11]>
+    <script src="/assets/custom/Plugin/sweetalert2/polyfill.min.js" type="text/javascript"></script>
     <![endif]-->
-    @yield('content')
-</div>
-<!-- 核心/Core -->
-<script src="/assets/global/vendor/babel-external-helpers/babel-external-helpers.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/jquery/jquery.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/popper-js/umd/popper.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/bootstrap/bootstrap.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/animsition/animsition.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/mousewheel/jquery.mousewheel.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollbar/jquery-asScrollbar.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollable/jquery-asScrollable.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/ashoverscroll/jquery-asHoverScroll.min.js" type="text/javascript"></script>
-<!-- 插件/Plugins -->
-<script src="/assets/global/vendor/screenfull/screenfull.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/slidepanel/jquery-slidePanel.min.js" type="text/javascript"></script>
-<!--[if lt IE 11]>
-<script src="/assets/custom/Plugin/sweetalert2/polyfill.min.js" type="text/javascript"></script>
-<![endif]-->
-<script src="/assets/custom/Plugin/sweetalert2/sweetalert2.all.min.js" type="text/javascript"></script>
-<!-- 脚本/Scripts -->
-<script src="/assets/global/js/Component.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin.js" type="text/javascript"></script>
-<script src="/assets/global/js/Base.js" type="text/javascript"></script>
-<script src="/assets/global/js/Config.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Menubar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Sidebar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/PageAside.js" type="text/javascript"></script>
-<script src="/assets/js/Plugin/menu.js" type="text/javascript"></script>
-<!-- 设置/Config -->
-<script src="/assets/global/js/config/colors.js" type="text/javascript"></script>
-<script type="text/javascript">
-  Config.set('assets', '/assets');
-</script>
-<!-- 页面/Page -->
-<script src="/assets/js/Site.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/asscrollable.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/slidepanel.js" type="text/javascript"></script>
-<script src="/assets/custom/Plugin/js-cookie/js.cookie.min.js" type="text/javascript"></script>
-<script type="text/javascript">
-  (function(document, window, $) {
-    'use strict';
-    const Site = window.Site;
-    $(document).ready(function() {
-      Site.run();
-    });
-  })(document, window, jQuery);
-</script>
-@yield('javascript')
-</body>
-</html>
+    <script src="/assets/custom/Plugin/sweetalert2/sweetalert2.all.min.js" type="text/javascript"></script>
+    <script src="/assets/custom/Plugin/js-cookie/js.cookie.min.js" type="text/javascript"></script>
+    @yield('javascript')
+@endsection

+ 5 - 1
resources/views/admin/logs/callback.blade.php

@@ -61,7 +61,11 @@
                             <td> {{$vo->type_label}} </td>
                             <td> {{$vo->trade_no}} </td>
                             <td>
-                                <a href="{{route('admin.order', ['order_sn' => $vo->out_trade_no])}}" target="_blank"> {{$vo->out_trade_no}} </a>
+                                @can('admin.order')
+                                    <a href="{{route('admin.order', ['order_sn' => $vo->out_trade_no])}}" target="_blank"> {{$vo->out_trade_no}} </a>
+                                @else
+                                    {{$vo->out_trade_no}}
+                                @endcan
                             </td>
                             <td> {{$vo->amount}}元</td>
                             <td> {!! $vo->trade_status_label !!} </td>

+ 5 - 1
resources/views/admin/logs/order.blade.php

@@ -107,7 +107,11 @@
                                 @if(empty($order->user) )
                                     【账号不存在】
                                 @else
-                                    <a href="{{route('admin.user.index', ['id'=>$order->user->id])}}" target="_blank">{{$order->user->email}} </a>
+                                    @can('admin.user.index')
+                                        <a href="{{route('admin.user.index', ['id'=>$order->user->id])}}" target="_blank">{{$order->user->email}} </a>
+                                    @else
+                                        {{$order->user->email}}
+                                    @endcan
                                 @endif
                             </td>
                             <td> {{$order->order_sn}}</td>

+ 5 - 1
resources/views/admin/logs/traffic.blade.php

@@ -72,7 +72,11 @@
                                 @if(empty($vo->user))
                                     【账号已删除】
                                 @else
-                                    <a href="{{route('admin.user.index', ['id' => $vo->user->id])}}" target="_blank"> {{$vo->user->email}} </a>
+                                    @can('admin.user.index')
+                                        <a href="{{route('admin.user.index', ['id' => $vo->user->id])}}" target="_blank"> {{$vo->user->email}} </a>
+                                    @else
+                                        {{$vo->user->email}}
+                                    @endcan
                                 @endif
                             </td>
                             <td> {{$vo->node->name ?? '【节点已删除】'}} </td>

+ 5 - 1
resources/views/admin/logs/userBanHistory.blade.php

@@ -37,7 +37,11 @@
                             </td>
                             <td>
                                 @if ($vo->user)
-                                    <a href="{{route('admin.user.index', ['email'=>$vo->user->email])}}" target="_blank"> {{$vo->user->email}}</a>
+                                    @can('admin.user.index')
+                                        <a href="{{route('admin.user.index', ['email'=>$vo->user->email])}}" target="_blank"> {{$vo->user->email}}</a>
+                                    @else
+                                        {{$vo->user->email}}
+                                    @endcan
                                 @else
                                     【账号已删除】
                                 @endif

+ 5 - 1
resources/views/admin/logs/userTraffic.blade.php

@@ -44,7 +44,11 @@
                             <td>
                                 @if ($vo->order_id)
                                     @if($vo->order)
-                                        <a href="{{route('admin.order', ['id' => $vo->order_id])}}">{{$vo->order->goods->name}}</a>
+                                        @can('admin.order')
+                                            <a href="{{route('admin.order', ['id' => $vo->order_id])}}"></a>
+                                        @else
+                                            {{$vo->order->goods->name}}
+                                        @endcan
                                     @else
                                         【订单已删除】
                                     @endif

+ 47 - 41
resources/views/admin/marketing/pushList.blade.php

@@ -9,9 +9,11 @@
         <div class="panel">
             <div class="panel-heading">
                 <h3 class="panel-title">推送消息列表</h3>
-                <div class="panel-actions">
-                    <button class="btn btn-primary" data-toggle="modal" data-target="#send_modal"><i class="icon wb-plus"></i>推送消息</button>
-                </div>
+                @can('admin.marketing.add')
+                    <div class="panel-actions">
+                        <button class="btn btn-primary" data-toggle="modal" data-target="#send_modal"><i class="icon wb-plus"></i>推送消息</button>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <div class="form-row">
@@ -31,7 +33,7 @@
                 <div class="alert alert-info alert-dismissible" role="alert">
                     <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                         <span aria-hidden="true">×</span></button>
-                    仅会推送给关注了您的消息通道的用户 <a href="{{route('admin.system')}}" class="alert-link" target="_blank">设置PushBear</a>.
+                    仅会推送给关注了您的消息通道的用户 @can('admin.system.index')<a href="{{route('admin.system.index')}}" class="alert-link" target="_blank">设置PushBear</a> @else 设置PushBear @endcan
                 </div>
                 <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
                     <thead class="thead-default">
@@ -73,47 +75,49 @@
         </div>
     </div>
 
-    <!-- 推送消息 -->
-    <div id="send_modal" class="modal fade" tabindex="-1" data-focus-on="input:first" data-backdrop="static"
-         data-keyboard="false">
-        <div class="modal-dialog modal-lg  modal-center">
-            <div class="modal-content">
-                <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                        <span aria-hidden="true">×</span>
-                    </button>
-                    <h4 class="modal-title">推送消息</h4>
-                </div>
-                <div class="modal-body">
-                    <div class="alert alert-danger" style="display: none;" id="msg"></div>
-                    <form action="#" method="post" class="form-horizontal">
-                        <div class="form-body">
-                            <div class="form-group">
-                                <div class="row">
-                                    <label for="title" class="col-md-2 control-label"> 标题 </label>
-                                    <div class="col-md-6">
-                                        <input type="text" class="form-control" name="title" id="title"/>
+    @can('admin.marketing.add')
+        <!-- 推送消息 -->
+        <div id="send_modal" class="modal fade" tabindex="-1" data-focus-on="input:first" data-backdrop="static"
+             data-keyboard="false">
+            <div class="modal-dialog modal-lg  modal-center">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">×</span>
+                        </button>
+                        <h4 class="modal-title">推送消息</h4>
+                    </div>
+                    <div class="modal-body">
+                        <div class="alert alert-danger" style="display: none;" id="msg"></div>
+                        <form action="#" method="post" class="form-horizontal">
+                            <div class="form-body">
+                                <div class="form-group">
+                                    <div class="row">
+                                        <label for="title" class="col-md-2 control-label"> 标题 </label>
+                                        <div class="col-md-6">
+                                            <input type="text" class="form-control" name="title" id="title"/>
+                                        </div>
                                     </div>
                                 </div>
-                            </div>
-                            <div class="form-group">
-                                <div class="row">
-                                    <label for="content" class="col-md-2 control-label"> 内容 </label>
-                                    <div class="col-md-9">
-                                        <textarea class="form-control" rows="10" name="content" id="content" data-provide="markdown" data-iconlibrary="fa"></textarea>
+                                <div class="form-group">
+                                    <div class="row">
+                                        <label for="content" class="col-md-2 control-label"> 内容 </label>
+                                        <div class="col-md-9">
+                                            <textarea class="form-control" rows="10" name="content" id="content" data-provide="markdown" data-iconlibrary="fa"></textarea>
+                                        </div>
                                     </div>
                                 </div>
                             </div>
-                        </div>
-                    </form>
-                </div>
-                <div class="modal-footer">
-                    <button class="btn btn-danger" data-dismiss="modal">取消</button>
-                    <button class="btn btn-primary" onclick="return send();">推送</button>
+                        </form>
+                    </div>
+                    <div class="modal-footer">
+                        <button class="btn btn-danger" data-dismiss="modal">取消</button>
+                        <button class="btn btn-primary" onclick="return send();">推送</button>
+                    </div>
                 </div>
             </div>
         </div>
-    </div>
+    @endcan
 @endsection
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
@@ -126,6 +130,11 @@
         $('#status').val({{Request::input('status')}});
       });
 
+      function Search() {
+        window.location.href = '{{route('admin.marketing.push')}}?status=' + $('#status').val();
+      }
+
+      @can('admin.marketing.add')
       // 发送通道消息
       function send() {
         const title = $('#title').val();
@@ -164,9 +173,6 @@
       $('#send_modal').on('hide.bs.modal', function() {
         window.location.reload();
       });
-
-      function Search() {
-        window.location.href = '{{route('admin.marketing.push')}}?status=' + $('#status').val();
-      }
+        @endcan
     </script>
 @endsection

+ 23 - 11
resources/views/admin/node/auth.blade.php

@@ -7,11 +7,13 @@
         <div class="panel">
             <div class="panel-heading">
                 <h2 class="panel-title">节点授权列表<small>WEBAPI</small></h2>
-                <div class="panel-actions">
-                    <button class="btn btn-primary" onclick="addAuth()">
-                        <i class="icon wb-plus" aria-hidden="true"></i>生成授权
-                    </button>
-                </div>
+                @can('admin.node.auth.store')
+                    <div class="panel-actions">
+                        <button class="btn btn-primary" onclick="addAuth()">
+                            <i class="icon wb-plus" aria-hidden="true"></i>生成授权
+                        </button>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
@@ -44,12 +46,16 @@
                                     <button data-target="#install_{{$vo->node->type}}_{{$vo->id}}" data-toggle="modal" class="btn btn-primary">
                                         <i class="icon wb-code" aria-hidden="true"></i>部署后端
                                     </button>
-                                    <button onclick="refreshAuth('{{$vo->id}}')" class="btn btn-danger">
-                                        <i class="icon wb-reload" aria-hidden="true"></i> 重置密钥
-                                    </button>
-                                    <button onclick="deleteAuth('{{$vo->id}}')" class="btn btn-primary">
-                                        <i class="icon wb-trash" aria-hidden="true"></i> 删除
-                                    </button>
+                                    @can('admin.node.auth.update')
+                                        <button onclick="refreshAuth('{{$vo->id}}')" class="btn btn-danger">
+                                            <i class="icon wb-reload" aria-hidden="true"></i> 重置密钥
+                                        </button>
+                                    @endcan
+                                    @can('admin.node.auth.destroy')
+                                        <button onclick="deleteAuth('{{$vo->id}}')" class="btn btn-primary">
+                                            <i class="icon wb-trash" aria-hidden="true"></i> 删除
+                                        </button>
+                                    @endcan
                                 </div>
                             </td>
                         </tr>
@@ -210,6 +216,7 @@
 
     <script type="text/javascript">
       // 生成授权KEY
+      @can('admin.node.auth.store')
       function addAuth() {
         swal.fire({
           title: '提示',
@@ -230,7 +237,9 @@
           }
         });
       }
+      @endcan
 
+      @can('admin.node.auth.destroy')
       // 删除授权
       function deleteAuth(id) {
         swal.fire({
@@ -258,7 +267,9 @@
           }
         });
       }
+      @endcan
 
+      @can('admin.node.auth.update')
       // 重置授权认证KEY
       function refreshAuth(id) {
         swal.fire({
@@ -286,5 +297,6 @@
           }
         });
       }
+        @endcan
     </script>
 @endsection

+ 50 - 41
resources/views/admin/node/cert/index.blade.php

@@ -7,11 +7,13 @@
         <div class="panel">
             <div class="panel-heading">
                 <h2 class="panel-title">域名证书列表<small>(V2Ray节点的伪装域名)</small></h2>
-                <div class="panel-actions">
-                    <a href="{{route('admin.node.cert.create')}}" class="btn btn-primary">
-                        <i class="icon wb-plus" aria-hidden="true"></i>添加域名证书
-                    </a>
-                </div>
+                @can('admin.node.cert.create')
+                    <div class="panel-actions">
+                        <a href="{{route('admin.node.cert.create')}}" class="btn btn-primary">
+                            <i class="icon wb-plus" aria-hidden="true"></i>添加域名证书
+                        </a>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
@@ -38,14 +40,20 @@
                             <td> {{$vo->from}} </td>
                             <td> {{$vo->to}} </td>
                             <td>
-                                <div class="btn-group">
-                                    <a href="{{route('admin.node.cert.edit', $vo->id)}}" class="btn btn-primary">
-                                        <i class="icon wb-edit" aria-hidden="true"></i>
-                                    </a>
-                                    <button onclick="delCertificate('{{$vo->id}}')" class="btn btn-danger">
-                                        <i class="icon wb-trash" aria-hidden="true"></i>
-                                    </button>
-                                </div>
+                                @canany(['admin.node.cert.edit', 'admin.node.cert.destroy'])
+                                    <div class="btn-group">
+                                        @can('admin.node.cert.edit')
+                                            <a href="{{route('admin.node.cert.edit', $vo->id)}}" class="btn btn-primary">
+                                                <i class="icon wb-edit" aria-hidden="true"></i>
+                                            </a>
+                                        @endcan
+                                        @can('admin.node.cert.destroy')
+                                            <button onclick="delCertificate('{{$vo->id}}')" class="btn btn-danger">
+                                                <i class="icon wb-trash" aria-hidden="true"></i>
+                                            </button>
+                                        @endcan
+                                    </div>
+                                @endcanany
                             </td>
                         </tr>
                     @endforeach
@@ -70,34 +78,35 @@
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
-
-    <script type="text/javascript">
-      // 删除授权
-      function delCertificate(id) {
-        swal.fire({
-          title: '提示',
-          text: '确定删除该证书吗?',
-          icon: 'info',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: '{{route('admin.node.cert.destroy', '')}}/' + id,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
+    @can('admin.node.cert.destroy')
+        <script type="text/javascript">
+          // 删除授权
+          function delCertificate(id) {
+            swal.fire({
+              title: '提示',
+              text: '确定删除该证书吗?',
+              icon: 'info',
+              showCancelButton: true,
+              cancelButtonText: '{{trans('home.ticket_close')}}',
+              confirmButtonText: '{{trans('home.ticket_confirm')}}',
+            }).then((result) => {
+              if (result.value) {
+                $.ajax({
+                  method: 'DELETE',
+                  url: '{{route('admin.node.cert.destroy', '')}}/' + id,
+                  data: {_token: '{{csrf_token()}}'},
+                  dataType: 'json',
+                  success: function(ret) {
+                    if (ret.status === 'success') {
+                      swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                    } else {
+                      swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                    }
+                  },
+                });
+              }
             });
           }
-        });
-      }
-    </script>
+        </script>
+    @endcan
 @endsection

+ 199 - 167
resources/views/admin/node/index.blade.php

@@ -16,12 +16,18 @@
         <div class="panel">
             <div class="panel-heading">
                 <h3 class="panel-title">节点列表</h3>
-                <div class="panel-actions btn-group">
-                    <button type="button" onclick="refreshGeo()" class="btn btn-info">
-                        <i class="icon wb-map"></i> 刷新节点地理信息
-                    </button>
-                    <a href="{{route('admin.node.create')}}" class="btn btn-primary"><i class="icon wb-plus"></i> 添加节点</a>
-                </div>
+                @canany(['admin.node.geo', 'admin.node.create'])
+                    <div class="panel-actions btn-group">
+                        @can('admin.node.geo')
+                            <button type="button" onclick="refreshGeo()" class="btn btn-info">
+                                <i class="icon wb-map"></i> 刷新节点地理信息
+                            </button>
+                        @endcan
+                        @can('admin.node.create')
+                            <a href="{{route('admin.node.create')}}" class="btn btn-primary"><i class="icon wb-plus"></i> 添加节点</a>
+                        @endcan
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
@@ -75,38 +81,54 @@
                                 @if(!$node->is_subscribe)<span class="badge badge-lg badge-danger"><del>订</del></span> @endif
                             </td>
                             <td>
-                                <div class="btn-group" role="group">
-                                    <button type="button" class="btn btn-primary dropdown-toggle" data-boundary="viewport" data-toggle="dropdown" aria-expanded="false">
-                                        <i class="icon wb-wrench" aria-hidden="true"></i>
-                                    </button>
-                                    <div class="dropdown-menu" role="menu">
-                                        <a class="dropdown-item" href="{{route('admin.node.edit', [$node->id, 'page' => Request::input('page', 1)])}}" role="menuitem">
-                                            <i class="icon wb-edit" aria-hidden="true"></i> 编辑
-                                        </a>
-                                        <a class="dropdown-item" href="javascript:delNode('{{$node->id}}', '{{$node->name}}')" role="menuitem">
-                                            <i class="icon wb-trash" aria-hidden="true"></i> 删除
-                                        </a>
-                                        <a class="dropdown-item" href="{{route('admin.node.monitor', $node)}}" role="menuitem">
-                                            <i class="icon wb-stats-bars" aria-hidden="true"></i> 流量统计
-                                        </a>
-                                        <hr/>
-                                        <a class="dropdown-item" href="javascript:refreshGeo('{{$node->id}}')" role="menuitem">
-                                            <i id="geo{{$node->id}}" class="icon wb-map" aria-hidden="true"></i> 刷新地理
-                                        </a>
-                                        <a class="dropdown-item" href="javascript:pingNode('{{$node->id}}')" role="menuitem">
-                                            <i id="ping{{$node->id}}" class="icon wb-order" aria-hidden="true"></i> 检测延迟
-                                        </a>
-                                        <a class="dropdown-item" href="javascript:checkNode('{{$node->id}}')" role="menuitem">
-                                            <i id="node{{$node->id}}" class="icon wb-signal" aria-hidden="true"></i> 连通性检测
-                                        </a>
-                                        @if($node->type === 4)
+                                @canany(['admin.node.edit', 'admin.node.destroy', 'admin.node.monitor', 'admin.node.geo', 'admin.node.ping', 'admin.node.check', 'admin.node.reload'])
+                                    <div class="btn-group" role="group">
+                                        <button type="button" class="btn btn-primary dropdown-toggle" data-boundary="viewport" data-toggle="dropdown" aria-expanded="false">
+                                            <i class="icon wb-wrench" aria-hidden="true"></i>
+                                        </button>
+                                        <div class="dropdown-menu" role="menu">
+                                            @can('admin.node.edit')
+                                                <a class="dropdown-item" href="{{route('admin.node.edit', [$node->id, 'page' => Request::input('page', 1)])}}" role="menuitem">
+                                                    <i class="icon wb-edit" aria-hidden="true"></i> 编辑
+                                                </a>
+                                            @endcan
+                                            @can('admin.node.destroy')
+                                                <a class="dropdown-item" href="javascript:delNode('{{$node->id}}', '{{$node->name}}')" role="menuitem">
+                                                    <i class="icon wb-trash" aria-hidden="true"></i> 删除
+                                                </a>
+                                            @endcan
+                                            @can('admin.node.monitor')
+                                                <a class="dropdown-item" href="{{route('admin.node.monitor', $node)}}" role="menuitem">
+                                                    <i class="icon wb-stats-bars" aria-hidden="true"></i> 流量统计
+                                                </a>
+                                            @endcan
                                             <hr/>
-                                            <a class="dropdown-item" href="javascript:reload('{{$node->id}}')" role="menuitem">
-                                                <i id="reload{{$node->id}}" class="icon wb-reload" aria-hidden="true"></i> 重载后端
-                                            </a>
-                                        @endif
+                                            @can('admin.node.geo')
+                                                <a class="dropdown-item" href="javascript:refreshGeo('{{$node->id}}')" role="menuitem">
+                                                    <i id="geo{{$node->id}}" class="icon wb-map" aria-hidden="true"></i> 刷新地理
+                                                </a>
+                                            @endcan
+                                            @can('admin.node.ping')
+                                                <a class="dropdown-item" href="javascript:pingNode('{{$node->id}}')" role="menuitem">
+                                                    <i id="ping{{$node->id}}" class="icon wb-order" aria-hidden="true"></i> 检测延迟
+                                                </a>
+                                            @endcan
+                                            @can('admin.node.check')
+                                                <a class="dropdown-item" href="javascript:checkNode('{{$node->id}}')" role="menuitem">
+                                                    <i id="node{{$node->id}}" class="icon wb-signal" aria-hidden="true"></i> 连通性检测
+                                                </a>
+                                            @endcan
+                                            @if($node->type === 4)
+                                                @can('admin.node.reload')
+                                                    <hr/>
+                                                    <a class="dropdown-item" href="javascript:reload('{{$node->id}}')" role="menuitem">
+                                                        <i id="reload{{$node->id}}" class="icon wb-reload" aria-hidden="true"></i> 重载后端
+                                                    </a>
+                                                @endcan
+                                            @endif
+                                        </div>
                                     </div>
-                                </div>
+                                @endcan
                             </td>
                         </tr>
                     @endforeach
@@ -133,142 +155,152 @@
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
 
     <script type="text/javascript">
-      // 节点连通性测试
-      function checkNode(id) {
-        $.ajax({
-          method: 'POST',
-          url: '{{route('admin.node.check', '')}}/' + id,
-          data: {_token: '{{csrf_token()}}'},
-          beforeSend: function() {
-            $('#node' + id).removeClass('wb-signal').addClass('wb-loop icon-spin');
-          },
-          success: function(ret) {
-            if (ret.status === 'success') {
-              swal.fire({
-                title: ret.title,
-                icon: 'info',
-                html: '<table class="my-20"><thead class="thead-default"><tr><th> ICMP </th> <th> TCP </th></thead><tbody><tr><td>' +
-                    ret.message[0] + '</td><td>' + ret.message[1] + '</td></tr></tbody></table>',
-                showConfirmButton: false,
-              });
-            } else {
-              swal.fire({title: ret.title, text: ret.message, icon: 'error'});
-            }
-          },
-          complete: function() {
-            $('#node' + id).removeClass('wb-loop icon-spin').addClass('wb-signal');
-          },
-        });
-      }
+        @can('admin.node.check')
+        // 节点连通性测试
+        function checkNode(id) {
+          $.ajax({
+            method: 'POST',
+            url: '{{route('admin.node.check', '')}}/' + id,
+            data: {_token: '{{csrf_token()}}'},
+            beforeSend: function() {
+              $('#node' + id).removeClass('wb-signal').addClass('wb-loop icon-spin');
+            },
+            success: function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({
+                  title: ret.title,
+                  icon: 'info',
+                  html: '<table class="my-20"><thead class="thead-default"><tr><th> ICMP </th> <th> TCP </th></thead><tbody><tr><td>' +
+                      ret.message[0] + '</td><td>' + ret.message[1] + '</td></tr></tbody></table>',
+                  showConfirmButton: false,
+                });
+              } else {
+                swal.fire({title: ret.title, text: ret.message, icon: 'error'});
+              }
+            },
+            complete: function() {
+              $('#node' + id).removeClass('wb-loop icon-spin').addClass('wb-signal');
+            },
+          });
+        }
+        @endcan
+
+        @can('admin.node.ping')
+        // Ping节点获取延迟
+        function pingNode(id) {
+          $.ajax({
+            method: 'POST',
+            url: '{{route('admin.node.ping', '')}}/' + id,
+            data: {_token: '{{csrf_token()}}'},
+            beforeSend: function() {
+              $('#ping' + id).removeClass('wb-order').addClass('wb-loop icon-spin');
+            },
+            success: function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({
+                  icon: 'info',
+                  html: '<table class="my-20"><thead class="thead-default"><tr><th> 电信 </th> <th> 联通 </th> <th> 移动 </th> <th> 香港 </th></thead><tbody><tr><td>' +
+                      ret.message[0] + '</td><td>' + ret.message[1] + '</td><td>' + ret.message[2] + '</td><td>' +
+                      ret.message[3] + '</td></tr></tbody></table>',
+                  showConfirmButton: false,
+                });
+              } else {
+                swal.fire({title: ret.message, icon: 'error'});
+              }
+            },
+            complete: function() {
+              $('#ping' + id).removeClass('wb-loop icon-spin').addClass('wb-order');
+            },
+          });
+        }
+        @endcan
 
-      // Ping节点获取延迟
-      function pingNode(id) {
-        $.ajax({
-          method: 'POST',
-          url: '{{route('admin.node.ping', '')}}/' + id,
-          data: {_token: '{{csrf_token()}}'},
-          beforeSend: function() {
-            $('#ping' + id).removeClass('wb-order').addClass('wb-loop icon-spin');
-          },
-          success: function(ret) {
-            if (ret.status === 'success') {
-              swal.fire({
-                icon: 'info',
-                html: '<table class="my-20"><thead class="thead-default"><tr><th> 电信 </th> <th> 联通 </th> <th> 移动 </th> <th> 香港 </th></thead><tbody><tr><td>' +
-                    ret.message[0] + '</td><td>' + ret.message[1] + '</td><td>' + ret.message[2] + '</td><td>' +
-                    ret.message[3] + '</td></tr></tbody></table>',
-                showConfirmButton: false,
+        @can('admin.node.reload')
+        // 发送节点重载请求
+        function reload(id) {
+          swal.fire({
+            text: '确定重载节点?',
+            icon: 'question',
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'POST',
+                url: '{{route('admin.node.reload', '')}}/' + id,
+                data: {_token: '{{csrf_token()}}'},
+                beforeSend: function() {
+                  $('#reload' + id).removeClass('wb-reload').addClass('wb-loop icon-spin');
+                },
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({title: ret.message, icon: 'info', showConfirmButton: false});
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'});
+                  }
+                },
+                complete: function() {
+                  $('#reload' + id).removeClass('wb-loop icon-spin').addClass('wb-reload');
+                },
               });
-            } else {
-              swal.fire({title: ret.message, icon: 'error'});
             }
-          },
-          complete: function() {
-            $('#ping' + id).removeClass('wb-loop icon-spin').addClass('wb-order');
-          },
-        });
-      }
+          });
+        }
+        @endcan
 
-      // 发送节点重载请求
-      function reload(id) {
-        swal.fire({
-          text: '确定重载节点?',
-          icon: 'question',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'POST',
-              url: '{{route('admin.node.reload', '')}}/' + id,
-              data: {_token: '{{csrf_token()}}'},
-              beforeSend: function() {
-                $('#reload' + id).removeClass('wb-reload').addClass('wb-loop icon-spin');
-              },
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'info', showConfirmButton: false});
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'});
-                }
-              },
-              complete: function() {
-                $('#reload' + id).removeClass('wb-loop icon-spin').addClass('wb-reload');
-              },
-            });
-          }
-        });
-      }
+        @can('admin.node.geo')
+        // 刷新节点地理信息
+        function refreshGeo(id = 0) {
+          $.ajax({
+            method: 'GET',
+            url: '{{route('admin.node.geo', '')}}/' + id,
+            data: {_token: '{{csrf_token()}}'},
+            beforeSend: function() {
+              $('#geo' + id).removeClass('wb-map').addClass('wb-loop icon-spin');
+            },
+            success: function(ret) {
+              if (ret.status === 'success') {
+                swal.fire({title: ret.message, icon: 'info', showConfirmButton: false});
+              } else {
+                swal.fire({title: ret.message, icon: 'error'});
+              }
+            },
+            complete: function() {
+              $('#geo' + id).removeClass('wb-loop icon-spin').addClass('wb-map');
+            },
+          });
+        }
+        @endcan
 
-      // 刷新节点地理信息
-      function refreshGeo(id = 0) {
-        $.ajax({
-          method: 'GET',
-          url: '{{route('admin.node.geo', '')}}/' + id,
-          data: {_token: '{{csrf_token()}}'},
-          beforeSend: function() {
-            $('#geo' + id).removeClass('wb-map').addClass('wb-loop icon-spin');
-          },
-          success: function(ret) {
-            if (ret.status === 'success') {
-              swal.fire({title: ret.message, icon: 'info', showConfirmButton: false});
-            } else {
-              swal.fire({title: ret.message, icon: 'error'});
+        @can('admin.node.destroy')
+        // 删除节点
+        function delNode(id, name) {
+          swal.fire({
+            title: '警告',
+            text: '确定删除节点 【' + name + '】 ?',
+            icon: 'warning',
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'DELETE',
+                url: '{{route('admin.node.destroy', '')}}/' + id,
+                data: {_token: '{{csrf_token()}}'},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+              });
             }
-          },
-          complete: function() {
-            $('#geo' + id).removeClass('wb-loop icon-spin').addClass('wb-map');
-          },
-        });
-      }
-
-      // 删除节点
-      function delNode(id, name) {
-        swal.fire({
-          title: '警告',
-          text: '确定删除节点 【' + name + '】 ?',
-          icon: 'warning',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: '{{route('admin.node.destroy', '')}}/' + id,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
-            });
-          }
-        });
-      }
+          });
+        }
+        @endcan
     </script>
 @endsection

+ 102 - 0
resources/views/admin/permission/index.blade.php

@@ -0,0 +1,102 @@
+@extends('admin.layouts')
+@section('css')
+    <link href="/assets/global/vendor/bootstrap-table/bootstrap-table.min.css" type="text/css" rel="stylesheet">
+@endsection
+@section('content')
+    <div class="page-content container">
+        <div class="panel">
+            <div class="panel-heading">
+                <h2 class="panel-title">权限行为列表</h2>
+                @can('admin.permission.create')
+                    <div class="panel-actions">
+                        <a href="{{route('admin.permission.create')}}" class="btn btn-outline-primary">
+                            <i class="icon wb-plus" aria-hidden="true"></i>添加权限行为
+                        </a>
+                    </div>
+                @endcan
+            </div>
+            <div class="panel-body">
+                <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
+                    <thead class="thead-default">
+                    <tr>
+                        <th> #</th>
+                        <th> 名称</th>
+                        <th> 行为</th>
+                        <th> 操作</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    @foreach ($permissions as $permission)
+                        <tr>
+                            <td>{{$permission->id}}</td>
+                            <td>{{$permission->description}}</td>
+                            <td>{{$permission->name}}</td>
+                            <td>
+                                @canany(['admin.permission.edit', 'admin.permission.destroy'])
+                                    <div class="btn-group">
+                                        @can('admin.permission.edit')
+                                            <a class="btn btn-sm btn-outline-primary" href="{{route('admin.permission.edit', $permission->id)}}">
+                                                <i class="icon wb-edit"></i></a>
+                                        @endcan
+                                        @can('admin.permission.destroy')
+                                            <button class="btn btn-sm btn-outline-danger"
+                                                    onclick="delPermission('{{route('admin.permission.destroy', $permission->id)}}','{{$permission->name}}')">
+                                                <i class="icon wb-trash"></i></button>
+                                        @endcan
+                                    </div>
+                                @endcanany
+                            </td>
+                        </tr>
+                    @endforeach
+                    </tbody>
+                </table>
+            </div>
+            <div class="panel-footer">
+                <div class="row">
+                    <div class="col-sm-4">
+                        共 <code>{{$permissions->total()}}</code> 条权限行为
+                    </div>
+                    <div class="col-sm-8">
+                        <nav class="Page navigation float-right">
+                            {{$permissions->links()}}
+                        </nav>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+@endsection
+@section('javascript')
+    <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
+    <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
+    @can('admin.permission.destroy')
+        <script type="text/javascript">
+          function delPermission(url, name) {
+            swal.fire({
+              title: '警告',
+              text: '确定删除 【' + name + '】 权限行为?',
+              icon: 'warning',
+              showCancelButton: true,
+              cancelButtonText: '{{trans('home.close')}}',
+              confirmButtonText: '{{trans('home.ticket_confirm')}}',
+            }).then((result) => {
+              if (result.value) {
+                $.ajax({
+                  method: 'DELETE',
+                  url: url,
+                  data: {_token: '{{csrf_token()}}'},
+                  dataType: 'json',
+                  success: function(ret) {
+                    if (ret.status === 'success') {
+                      swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                    } else {
+                      swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                    }
+                  },
+                });
+              }
+            });
+          }
+        </script>
+    @endcan
+@endsection

+ 54 - 0
resources/views/admin/permission/info.blade.php

@@ -0,0 +1,54 @@
+@extends('admin.layouts')
+@section('content')
+    <div class="page-content container">
+        <div class="panel">
+            <div class="panel-heading">
+                <h2 class="panel-title">@isset($permission)编辑@else添加@endisset权限行为</h2>
+                <div class="panel-actions">
+                    <a href="{{route('admin.permission.index')}}" class="btn btn-danger">返 回</a>
+                </div>
+            </div>
+            @if (Session::has('successMsg'))
+                <x-alert type="success" :message="Session::get('successMsg')"/>
+            @endif
+            @if($errors->any())
+                <x-alert type="danger" :message="$errors->all()"/>
+            @endif
+            <div class="panel-body">
+                <form action="@isset($permission){{route('admin.permission.update',$permission)}}@else{{route('admin.permission.store')}}@endisset"
+                      method="POST" enctype="multipart/form-data" class="form-horizontal">
+                    @isset($permission)@method('PUT')@endisset
+                    @csrf
+                    <div class="form-group row">
+                        <label class="col-md-2 col-sm-3 col-form-label" for="description">名称</label>
+                        <div class="col-md-7 col-sm-8">
+                            <input type="text" class="form-control" name="description" id="description"/>
+                            <span class="text-help"> 填写名称,例:【A系统】编辑A </span>
+                        </div>
+                    </div>
+                    <div class="form-group row">
+                        <label class="col-md-2 col-sm-3 col-form-label" for="name">行为</label>
+                        <div class="col-md-7 col-sm-8">
+                            <input type="text" class="form-control" name="name" id="name"/>
+                            <span class="text-help"> 填写路由名称,例:admin.permission.create,update </span>
+                        </div>
+                    </div>
+
+                    <div class="form-actions text-right">
+                        <button type="submit" class="btn btn-success">提 交</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+@endsection
+@section('javascript')
+    <script type="text/javascript">
+        @isset($permission)
+        $(document).ready(function() {
+          $('#description').val('{{$permission->description}}');
+          $('#name').val('{{$permission->name}}');
+        });
+        @endisset
+    </script>
+@endsection

+ 109 - 0
resources/views/admin/role/index.blade.php

@@ -0,0 +1,109 @@
+@extends('admin.layouts')
+@section('css')
+    <link href="/assets/global/vendor/bootstrap-table/bootstrap-table.min.css" type="text/css" rel="stylesheet">
+@endsection
+@section('content')
+    <div class="page-content container">
+        <div class="panel">
+            <div class="panel-heading">
+                <h2 class="panel-title">权限角色列表</h2>
+                @can('admin.role.create')
+                    <div class="panel-actions">
+                        <a href="{{route('admin.role.create')}}" class="btn btn-outline-primary">
+                            <i class="icon wb-plus" aria-hidden="true"></i>添加角色
+                        </a>
+                    </div>
+                @endcan
+            </div>
+            <div class="panel-body">
+                <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
+                    <thead class="thead-default">
+                    <tr>
+                        <th> #</th>
+                        <th> 名称</th>
+                        <th> 权限</th>
+                        <th> 操作</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    @foreach ($roles as $role)
+                        <tr>
+                            <td>{{$role->id}}</td>
+                            <td>{{$role->description}}</td>
+                            <td>
+                                @if ($role->name === 'Super Admin')
+                                    <span class="badge badge-info">全部权限</span>
+                                @else
+                                    @foreach($role->permissions()->pluck('description') as $description)
+                                        <span class="badge badge-info">{{ $description }}</span>
+                                    @endforeach
+                                @endif
+                            </td>
+                            <td>
+                                @canany(['admin.role.edit', 'admin.role.destroy'])
+                                    <div class="btn-group">
+                                        @can('admin.role.edit')
+                                            <a class="btn btn-sm btn-outline-primary" href="{{route('admin.role.edit', $role->id)}}">
+                                                <i class="icon wb-edit"></i></a>
+                                        @endcan
+                                        @can('admin.role.destroy')
+                                            <button class="btn btn-sm btn-outline-danger" onclick="delRole('{{route('admin.role.destroy', $role->id)}}','{{$role->name}}')">
+                                                <i class="icon wb-trash"></i></button>
+                                        @endcan
+                                    </div>
+                                @endcanany
+                            </td>
+                        </tr>
+                    @endforeach
+                    </tbody>
+                </table>
+            </div>
+            <div class="panel-footer">
+                <div class="row">
+                    <div class="col-sm-4">
+                        共 <code>{{$roles->total()}}</code> 个权限角色
+                    </div>
+                    <div class="col-sm-8">
+                        <nav class="Page navigation float-right">
+                            {{$roles->links()}}
+                        </nav>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+@endsection
+@section('javascript')
+    <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
+    <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
+    @can('admin.role.destroy')
+        <script type="text/javascript">
+          function delRole(url, name) {
+            swal.fire({
+              title: '警告',
+              text: '确定删除 【' + name + '】 权限角色?',
+              icon: 'warning',
+              showCancelButton: true,
+              cancelButtonText: '{{trans('home.close')}}',
+              confirmButtonText: '{{trans('home.ticket_confirm')}}',
+            }).then((result) => {
+              if (result.value) {
+                $.ajax({
+                  method: 'DELETE',
+                  url: url,
+                  data: {_token: '{{csrf_token()}}'},
+                  dataType: 'json',
+                  success: function(ret) {
+                    if (ret.status === 'success') {
+                      swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                    } else {
+                      swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                    }
+                  },
+                });
+              }
+            });
+          }
+        </script>
+    @endcan
+@endsection

+ 125 - 0
resources/views/admin/role/info.blade.php

@@ -0,0 +1,125 @@
+@extends('admin.layouts')
+@section('css')
+    <link href="/assets/global/vendor/multi-select/multi-select.min.css" type="text/css" rel="stylesheet">
+    <style>
+        .ms-container{
+            width:auto;
+        }
+    </style>
+@endsection
+@section('content')
+    <div class="page-content container">
+        <div class="panel">
+            <div class="panel-heading">
+                <h2 class="panel-title">@isset($role)编辑@else添加@endisset角色</h2>
+                <div class="panel-actions">
+                    <a href="{{route('admin.role.index')}}" class="btn btn-danger">返 回</a>
+                </div>
+            </div>
+            @if (Session::has('successMsg'))
+                <x-alert type="success" :message="Session::get('successMsg')"/>
+            @endif
+            @if($errors->any())
+                <x-alert type="danger" :message="$errors->all()"/>
+            @endif
+            <div class="panel-body">
+                <form action="@isset($role){{route('admin.role.update',$role)}}@else{{route('admin.role.store')}}@endisset"
+                      method="POST" enctype="multipart/form-data" class="form-horizontal">
+                    @isset($role)@method('PUT')@endisset
+                    @csrf
+                    <div class="form-group row">
+                        <label class="col-md-2 col-sm-3 col-form-label" for="description">显示名称</label>
+                        <div class="col-md-5 col-sm-9">
+                            <input type="text" class="form-control" name="description" id="description"/>
+                            <span class="text-help"> 名称,例如:管理员 </span>
+                        </div>
+                    </div>
+                    <div class="form-group row">
+                        <label class="col-md-2 col-sm-3 col-form-label" for="name">内部名称</label>
+                        <div class="col-md-5 col-sm-9">
+                            <input type="text" class="form-control" name="name" id="name"/>
+                            <span class="text-help"> 名称,例如:Administrator </span>
+                        </div>
+                    </div>
+                    <div class="form-group row">
+                        <label class="col-md-2 col-sm-3 col-form-label" for="permissions">选择权限</label>
+                        <div class="col-md-9 col-sm-9">
+                            <div class="btn-group mb-20">
+                                <button type="button" class="btn btn-primary" id="select-all">全 选</button>
+                                <button type="button" class="btn btn-danger" id="deselect-all">清 空</button>
+                            </div>
+                            <select class="form-control mx-auto w-p100" name="permissions[]" id="permissions" data-plugin="multiSelect" multiple>
+                                @foreach($permissions as $key => $description)
+                                    <option value="{{ $key }}">{{ $description .' - '. $key }}</option>
+                                @endforeach
+                            </select>
+                        </div>
+                    </div>
+                    <div class="form-actions text-right">
+                        <button type="submit" class="btn btn-success">提 交</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+@endsection
+@section('javascript')
+    <script src="/assets/global/vendor/multi-select/jquery.multi-select.js" type="text/javascript"></script>
+    <script src="/assets/global/js/Plugin/multi-select.js"></script>
+    <script src="/assets/global/js/jquery.quicksearch.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        @isset($role)
+        $(document).ready(function() {
+          $('#description').val('{{$role->description}}');
+          $('#name').val('{{$role->name}}');
+          $('#permissions').multiSelect('select',@json($role->permissions->pluck('name')));
+        });
+        @endisset
+        // 权限列表
+        $('#permissions').multiSelect({
+          selectableHeader: '<input type=\'text\' class=\'search-input form-control\' autocomplete=\'off\' placeholder=\'待分配规则,此处可搜索\'>',
+          selectionHeader: '<input type=\'text\' class=\'search-input form-control\' autocomplete=\'off\' placeholder=\'已分配规则,此处可搜索\'>',
+          afterInit: function() {
+            const that = this,
+                $selectableSearch = that.$selectableUl.prev(),
+                $selectionSearch = that.$selectionUl.prev(),
+                selectableSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selectable:not(.ms-selected)',
+                selectionSearchString = '#' + that.$container.attr('id') + ' .ms-elem-selection.ms-selected';
+
+            that.qs1 = $selectableSearch.quicksearch(selectableSearchString).on('keydown', function(e) {
+              if (e.which === 40) {
+                that.$selectableUl.focus();
+                return false;
+              }
+            });
+
+            that.qs2 = $selectionSearch.quicksearch(selectionSearchString).on('keydown', function(e) {
+              if (e.which === 40) {
+                that.$selectionUl.focus();
+                return false;
+              }
+            });
+          },
+          afterSelect: function() {
+            this.qs1.cache();
+            this.qs2.cache();
+          },
+          afterDeselect: function() {
+            this.qs1.cache();
+            this.qs2.cache();
+          },
+        });
+
+        // 全选
+        $('#select-all').click(function() {
+          $('#permissions').multiSelect('select_all');
+          return false;
+        });
+
+        // 反选
+        $('#deselect-all').click(function() {
+          $('#permissions').multiSelect('deselect_all');
+          return false;
+        });
+    </script>
+@endsection

+ 57 - 44
resources/views/admin/rule/group/index.blade.php

@@ -7,11 +7,13 @@
         <div class="panel">
             <div class="panel-heading">
                 <h2 class="panel-title">规则分组</h2>
-                <div class="panel-actions">
-                    <a href="{{route('admin.rule.group.create')}}" class="btn btn-outline-primary">
-                        <i class="icon wb-plus" aria-hidden="true"></i>添加分组
-                    </a>
-                </div>
+                @can('admin.rule.group.create')
+                    <div class="panel-actions">
+                        <a href="{{route('admin.rule.group.create')}}" class="btn btn-outline-primary">
+                            <i class="icon wb-plus" aria-hidden="true"></i>添加分组
+                        </a>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
@@ -30,18 +32,26 @@
                             <td> {{$ruleGroup->name}} </td>
                             <td> {!! $ruleGroup->type_label !!} </td>
                             <td>
-                                <div class="btn-group">
-                                    <a href="{{route('admin.rule.group.editNode', $ruleGroup->id)}}" class="btn btn-sm btn-outline-primary">
-                                        <i class="icon wb-plus" aria-hidden="true"></i>分配节点
-                                    </a>
-                                    <a href="{{route('admin.rule.group.edit', $ruleGroup->id)}}" class="btn btn-sm btn-outline-primary">
-                                        <i class="icon wb-edit"></i>编辑
-                                    </a>
-                                    <button onclick="delRuleGroup('{{route('admin.rule.group.destroy', $ruleGroup->id)}}', '{{$ruleGroup->name}}')"
-                                            class="btn btn-sm btn-outline-danger">
-                                        <i class="icon wb-trash"></i>删除
-                                    </button>
-                                </div>
+                                @canany(['admin.rule.group.editNode', 'admin.rule.group.edit', 'admin.rule.group.destroy'])
+                                    <div class="btn-group">
+                                        @can('admin.rule.group.editNode')
+                                            <a href="{{route('admin.rule.group.editNode', $ruleGroup->id)}}" class="btn btn-sm btn-outline-primary">
+                                                <i class="icon wb-plus" aria-hidden="true"></i>分配节点
+                                            </a>
+                                        @endcan
+                                        @can('admin.rule.group.edit')
+                                            <a href="{{route('admin.rule.group.edit', $ruleGroup->id)}}" class="btn btn-sm btn-outline-primary">
+                                                <i class="icon wb-edit"></i>编辑
+                                            </a>
+                                        @endcan
+                                        @can('admin.rule.group.destroy')
+                                            <button onclick="delRuleGroup('{{route('admin.rule.group.destroy', $ruleGroup->id)}}', '{{$ruleGroup->name}}')"
+                                                    class="btn btn-sm btn-outline-danger">
+                                                <i class="icon wb-trash"></i>删除
+                                            </button>
+                                        @endcan
+                                    </div>
+                                @endcanany
                             </td>
                         </tr>
                     @endforeach
@@ -66,33 +76,36 @@
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
-    <script type="text/javascript">
-      // 删除规则分组
-      function delRuleGroup(url, name) {
-        swal.fire({
-          title: '警告',
-          text: '确定删除分组 【' + name + '】 ?',
-          icon: 'warning',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: url,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
+    @can('admin.rule.group.destroy')
+        <script type="text/javascript">
+          // 删除规则分组
+          function delRuleGroup(url, name) {
+            swal.fire({
+              title: '警告',
+              text: '确定删除分组 【' + name + '】 ?',
+              icon: 'warning',
+              showCancelButton: true,
+              cancelButtonText: '{{trans('home.ticket_close')}}',
+              confirmButtonText: '{{trans('home.ticket_confirm')}}',
+            }).then((result) => {
+              if (result.value) {
+                $.ajax({
+                  method: 'DELETE',
+                  url: url,
+                  data: {_token: '{{csrf_token()}}'},
+                  dataType: 'json',
+                  success: function(ret) {
+                    if (ret.status === 'success') {
+                      swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                    } else {
+                      swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                    }
+                  },
+                });
+              }
             });
           }
-        });
-      }
-    </script>
+        </script>
+    @endcan
 @endsection
+

+ 65 - 47
resources/views/admin/rule/index.blade.php

@@ -8,11 +8,13 @@
         <div class="panel">
             <div class="panel-heading">
                 <h2 class="panel-title">规则列表</h2>
-                <div class="panel-actions">
-                    <button data-toggle="modal" data-target="#add" class="btn btn-outline-primary">
-                        <i class="icon wb-plus" aria-hidden="true"></i>添加规则
-                    </button>
-                </div>
+                @can('admin.rule.store')
+                    <div class="panel-actions">
+                        <button data-toggle="modal" data-target="#add" class="btn btn-outline-primary">
+                            <i class="icon wb-plus" aria-hidden="true"></i>添加规则
+                        </button>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <div class="form-row">
@@ -51,10 +53,18 @@
                                 <input type="text" class="form-control" name="rule_pattern" id="rule_pattern_{{$rule->id}}" value="{{$rule->pattern}}"/>
                             </td>
                             <td>
-                                <button class="btn btn-sm btn-outline-primary" onclick="editRule('{{$rule->id}}')">
-                                    <i class="icon wb-edit"></i></button>
-                                <button class="btn btn-sm btn-outline-danger" onclick="delRule('{{route('admin.rule.destroy',$rule)}}','{{$rule->name}}')">
-                                    <i class="icon wb-trash"></i></button>
+                                @canany(['admin.rule.update', 'admin.rule.destroy'])
+                                    <div class="btn-group">
+                                        @can('admin.rule.update')
+                                            <button class="btn btn-sm btn-outline-primary" onclick="editRule('{{$rule->id}}')">
+                                                <i class="icon wb-edit"></i></button>
+                                        @endcan
+                                        @can('admin.rule.destroy')
+                                            <button class="btn btn-sm btn-outline-danger" onclick="delRule('{{route('admin.rule.destroy',$rule)}}','{{$rule->name}}')">
+                                                <i class="icon wb-trash"></i></button>
+                                        @endcan
+                                    </div>
+                                @endcanany
                             </td>
                         </tr>
                     @endforeach
@@ -76,54 +86,56 @@
         </div>
     </div>
 
-    <div class="modal fade" id="add" aria-hidden="true" role="dialog" tabindex="-1">
-        <div class="modal-dialog modal-simple modal-center">
-            <div class="modal-content">
-                <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                        <span aria-hidden="true">×</span>
-                    </button>
-                    <h4 class="modal-title">添加规则</h4>
-                </div>
-                <form action="#" method="post" class="modal-body">
-                    <div class="alert alert-danger" style="display: none;" id="msg"></div>
-                    <div class="form-row">
-                        <div class="col-12">
-                            <div class="form-group row">
-                                <label class="col-md-2 col-sm-3 col-form-label" for="add_type">类型</label>
-                                <div class="col-xl-4 col-sm-8">
-                                    <select class="form-control" name="add_type" id="add_type" data-plugin="selectpicker" data-style="btn-outline btn-primary">
-                                        <option value="1">正则表达式</option>
-                                        <option value="2">域名</option>
-                                        <option value="3">IP</option>
-                                        <option value="4">协议</option>
-                                    </select>
+    @can('admin.rule.store')
+        <div class="modal fade" id="add" aria-hidden="true" role="dialog" tabindex="-1">
+            <div class="modal-dialog modal-simple modal-center">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">×</span>
+                        </button>
+                        <h4 class="modal-title">添加规则</h4>
+                    </div>
+                    <form action="#" method="post" class="modal-body">
+                        <div class="alert alert-danger" style="display: none;" id="msg"></div>
+                        <div class="form-row">
+                            <div class="col-12">
+                                <div class="form-group row">
+                                    <label class="col-md-2 col-sm-3 col-form-label" for="add_type">类型</label>
+                                    <div class="col-xl-4 col-sm-8">
+                                        <select class="form-control" name="add_type" id="add_type" data-plugin="selectpicker" data-style="btn-outline btn-primary">
+                                            <option value="1">正则表达式</option>
+                                            <option value="2">域名</option>
+                                            <option value="3">IP</option>
+                                            <option value="4">协议</option>
+                                        </select>
+                                    </div>
                                 </div>
-                            </div>
-                            <div class="form-group row">
-                                <label class="col-md-2 col-sm-3 col-form-label" for="name">描述</label>
-                                <div class="col-xl-6 col-sm-8">
-                                    <input type="text" class="form-control" name="name" id="name" required/>
+                                <div class="form-group row">
+                                    <label class="col-md-2 col-sm-3 col-form-label" for="name">描述</label>
+                                    <div class="col-xl-6 col-sm-8">
+                                        <input type="text" class="form-control" name="name" id="name" required/>
+                                    </div>
                                 </div>
                             </div>
-                        </div>
-                        <div class="col-12">
-                            <div class="form-group row">
-                                <label class="col-md-2 col-sm-3 col-form-label" for="pattern">值</label>
-                                <div class="col-xl-6 col-sm-8">
-                                    <input type="text" class="form-control" name="pattern" id="pattern" required/>
+                            <div class="col-12">
+                                <div class="form-group row">
+                                    <label class="col-md-2 col-sm-3 col-form-label" for="pattern">值</label>
+                                    <div class="col-xl-6 col-sm-8">
+                                        <input type="text" class="form-control" name="pattern" id="pattern" required/>
+                                    </div>
                                 </div>
                             </div>
                         </div>
+                    </form>
+                    <div class="modal-footer">
+                        <button data-dismiss="modal" class="btn btn-danger">关 闭</button>
+                        <button type="button" class="btn btn-primary" onclick="addRule()">添 加</button>
                     </div>
-                </form>
-                <div class="modal-footer">
-                    <button data-dismiss="modal" class="btn btn-danger">关 闭</button>
-                    <button type="button" class="btn btn-primary" onclick="addRule()">添 加</button>
                 </div>
             </div>
         </div>
-    </div>
+    @endcan
 @endsection
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
@@ -135,6 +147,7 @@
         $('#type').selectpicker('val', {{Request::input('type')}});
       });
 
+      @can('admin.rule.store')
       // 添加规则
       function addRule() {
         $.post("{{route('admin.rule.store')}}", {
@@ -151,7 +164,9 @@
           }
         });
       }
+      @endcan
 
+      @can('admin.rule.update')
       // 编辑规则
       function editRule(id) {
         $.ajax({
@@ -172,7 +187,9 @@
           },
         });
       }
+      @endcan
 
+      @can('admin.rule.destroy')
       // 删除规则
       function delRule(url, name) {
         swal.fire({
@@ -200,6 +217,7 @@
           }
         });
       }
+      @endcan
 
       //回车检测
       $(document).on('keypress', 'input', function(e) {

+ 9 - 5
resources/views/admin/rule/log.blade.php

@@ -7,11 +7,13 @@
         <div class="panel">
             <div class="panel-heading">
                 <h2 class="panel-title">触发记录</h2>
-                <div class="panel-actions">
-                    <a href="javascript:clearLog();" class="btn btn-outline-primary">
-                        <i class="icon wb-rubber" aria-hidden="true"></i>清空记录
-                    </a>
-                </div>
+                @can('admin.rule.clear')
+                    <div class="panel-actions">
+                        <a href="javascript:clearLog();" class="btn btn-outline-primary">
+                            <i class="icon wb-rubber" aria-hidden="true"></i>清空记录
+                        </a>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <div class="form-row">
@@ -95,6 +97,7 @@
             '&node_id=' + $('#node_id option:selected').val() + '&rule_id=' + $('#rule_id option:selected').val();
       }
 
+      @can('admin.rule.clear')
       // 清除所有记录
       function clearLog() {
         swal.fire({
@@ -116,5 +119,6 @@
           }
         });
       }
+        @endcan
     </script>
 @endsection

+ 22 - 13
resources/views/admin/shop/index.blade.php

@@ -7,9 +7,11 @@
         <div class="panel panel-bordered">
             <div class="panel-heading">
                 <h1 class="panel-title"><i class="icon wb-shopping-cart" aria-hidden="true"></i>商品列表</h1>
-                <div class="panel-actions">
-                    <a href="{{route('admin.goods.create')}}" class="btn btn-primary"><i class="icon wb-plus"></i>添加商品</a>
-                </div>
+                @can('admin.goods.create')
+                    <div class="panel-actions">
+                        <a href="{{route('admin.goods.create')}}" class="btn btn-primary"><i class="icon wb-plus"></i>添加商品</a>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <div class="form-row">
@@ -90,14 +92,20 @@
                                 @endif
                             </td>
                             <td>
-                                <div class="btn-group">
-                                    <a href="{{route('admin.goods.edit', $goods)}}" class="btn btn-primary">
-                                        <i class="icon wb-edit"></i>
-                                    </a>
-                                    <button onclick="delGoods('{{route('admin.goods.destroy', $goods)}}','{{$goods->name}}')" class="btn btn-danger">
-                                        <i class="icon wb-trash"></i>
-                                    </button>
-                                </div>
+                                @canany(['admin.goods.edit', 'admin.goods.destroy'])
+                                    <div class="btn-group">
+                                        @can('admin.goods.edit')
+                                            <a href="{{route('admin.goods.edit', $goods)}}" class="btn btn-primary">
+                                                <i class="icon wb-edit"></i>
+                                            </a>
+                                        @endcan
+                                        @can('admin.goods.destroy')
+                                            <button onclick="delGoods('{{route('admin.goods.destroy', $goods)}}','{{$goods->name}}')" class="btn btn-danger">
+                                                <i class="icon wb-trash"></i>
+                                            </button>
+                                        @endcan
+                                    </div>
+                                @endcanany
                             </td>
                         </tr>
                     @endforeach
@@ -121,8 +129,7 @@
 @endsection
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
-    <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js"
-            type="text/javascript"></script>
+    <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
     <script type="text/javascript">
       $(document).ready(function() {
         $('#type').val({{Request::input('type')}});
@@ -135,6 +142,7 @@
             $('#status option:selected').val();
       }
 
+      @can('admin.goods.destroy')
       // 删除商品
       function delGoods(url, name) {
         swal.fire({
@@ -162,5 +170,6 @@
           }
         });
       }
+        @endcan
     </script>
 @endsection

+ 16 - 6
resources/views/admin/subscribe/index.blade.php

@@ -51,21 +51,29 @@
                                 @if(empty($subscribe->user))
                                     【账号已删除】
                                 @else
-                                    <a href="{{route('admin.user.index', ['id'=>$subscribe->user->id])}}" target="_blank">{{$subscribe->user->email}}</a>
+                                    @can('admin.user.index')
+                                        <a href="{{route('admin.user.index', ['id'=>$subscribe->user->id])}}" target="_blank">{{$subscribe->user->email}}</a>
+                                    @else
+                                        {{$subscribe->user->email}}
+                                    @endcan
                                 @endif
                             </td>
                             <td> {{$subscribe->code}} </td>
                             <td>
-                                <a href="{{route('admin.subscribe.log', $subscribe->id)}}" target="_blank">{{$subscribe->times}}</a>
+                                @can('admin.subscribe.log')
+                                    <a href="{{route('admin.subscribe.log', $subscribe->id)}}" target="_blank">{{$subscribe->times}}</a>
+                                @endcan
                             </td>
                             <td> {{$subscribe->updated_at}} </td>
                             <td> {{$subscribe->ban_time ? date('Y-m-d H:i', $subscribe->ban_time): ''}} </td>
                             <td> {{$subscribe->ban_desc}} </td>
                             <td>
-                                <button class="btn btn-sm @if($subscribe->status == 0) btn-outline-success @else btn-sm btn-outline-danger @endif"
-                                        onclick="setSubscribeStatus('{{route('admin.subscribe.set', $subscribe->id)}}')">
-                                    @if($subscribe->status == 0) 启用 @else 禁用 @endif
-                                </button>
+                                @can('admin.subscribe.set')
+                                    <button class="btn btn-sm @if($subscribe->status == 0) btn-outline-success @else btn-sm btn-outline-danger @endif"
+                                            onclick="setSubscribeStatus('{{route('admin.subscribe.set', $subscribe->id)}}')">
+                                        @if($subscribe->status == 0) 启用 @else 禁用 @endif
+                                    </button>
+                                @endcan
                             </td>
                         </tr>
                     @endforeach
@@ -109,6 +117,7 @@
             $('#status option:selected').val();
       }
 
+      @can('admin.subscribe.set')
       // 启用禁用用户的订阅
       function setSubscribeStatus(url) {
         $.post(url, {_token: '{{csrf_token()}}'}, function(ret) {
@@ -123,5 +132,6 @@
           }
         });
       }
+        @endcan
     </script>
 @endsection

+ 53 - 41
resources/views/admin/ticket/index.blade.php

@@ -7,13 +7,15 @@
         <div class="panel">
             <div class="panel-heading">
                 <h3 class="panel-title">工单列表</h3>
-                <div class="panel-actions">
-                    <button class="btn btn-primary btn-animate btn-animate-side" data-toggle="modal" data-target="#add_ticket_modal">
+                @can('admin.ticket.store')
+                    <div class="panel-actions">
+                        <button class="btn btn-primary btn-animate btn-animate-side" data-toggle="modal" data-target="#add_ticket_modal">
                         <span>
                             <i class="icon wb-plus" aria-hidden="true"></i> {{trans('home.ticket_table_new_button')}}
                         </span>
-                    </button>
-                </div>
+                        </button>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <div class="form-row">
@@ -43,7 +45,11 @@
                                 @if(!$ticket->user)
                                     【账号已删除】
                                 @else
-                                    <a href="{{route('admin.user.index', ['id'=>$ticket->user->id])}}" target="_blank">{{$ticket->user->email}}</a>
+                                    @can('admin.user.index')
+                                        <a href="{{route('admin.user.index', ['id'=>$ticket->user->id])}}" target="_blank">{{$ticket->user->email}}</a>
+                                    @else
+                                        {{$ticket->user->email}}
+                                    @endcan
                                 @endif
                             </td>
 
@@ -54,15 +60,17 @@
                                 {!!$ticket->status_label!!}
                             </td>
                             <td>
-                                <a href="{{route('admin.ticket.edit',$ticket->id)}}" class="btn btn-animate btn-animate-vertical btn-outline-info">
-                                    <span>
-                                        @if($ticket->status === 2)
-                                            <i class="icon wb-eye" aria-hidden="true" style="left: 40%"> </i>{{trans('home.ticket_table_view')}}
-                                        @else
-                                            <i class="icon wb-check" aria-hidden="true" style="left: 40%"> </i>{{trans('home.ticket_open')}}
-                                        @endif
-                                    </span>
-                                </a>
+                                @can('admin.ticket.edit')
+                                    <a href="{{route('admin.ticket.edit',$ticket->id)}}" class="btn btn-animate btn-animate-vertical btn-outline-info">
+                                        <span>
+                                            @if($ticket->status === 2)
+                                                <i class="icon wb-eye" aria-hidden="true" style="left: 40%"> </i>{{trans('home.ticket_table_view')}}
+                                            @else
+                                                <i class="icon wb-check" aria-hidden="true" style="left: 40%"> </i>{{trans('home.ticket_open')}}
+                                            @endif
+                                        </span>
+                                    </a>
+                                @endcan
                             </td>
                         </tr>
                     @endforeach
@@ -84,40 +92,42 @@
         </div>
     </div>
 
-    <div id="add_ticket_modal" class="modal fade" tabindex="-1" data-focus-on="input:first" data-keyboard="false">
-        <div class="modal-dialog modal-simple modal-center modal-lg">
-            <div class="modal-content">
-                <div class="modal-header">
-                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                        <span aria-hidden="true">×</span>
-                    </button>
-                    <h4 class="modal-title"> {{trans('home.ticket_table_new_button')}} </h4>
-                </div>
-                <div class="modal-body">
-                    <div class="form-group row">
-                        <label for="domain" class="col-2 col-form-label">域名</label>
-                        <div class="input-group col-10">
-                            <input type="number" class="form-control col-md-4" name="user_id" id="user_id" placeholder="用户ID"/>
-                            <div class="input-group-prepend">
-                                <span class="input-group-text">或</span>
+    @can('admin.ticket.store')
+        <div id="add_ticket_modal" class="modal fade" tabindex="-1" data-focus-on="input:first" data-keyboard="false">
+            <div class="modal-dialog modal-simple modal-center modal-lg">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">×</span>
+                        </button>
+                        <h4 class="modal-title"> {{trans('home.ticket_table_new_button')}} </h4>
+                    </div>
+                    <div class="modal-body">
+                        <div class="form-group row">
+                            <label for="domain" class="col-2 col-form-label">域名</label>
+                            <div class="input-group col-10">
+                                <input type="number" class="form-control col-md-4" name="user_id" id="user_id" placeholder="用户ID"/>
+                                <div class="input-group-prepend">
+                                    <span class="input-group-text">或</span>
+                                </div>
+                                <input type="email" class="form-control col-md-8" name="user_email" id="user_email" placeholder="用户邮箱"/>
                             </div>
-                            <input type="email" class="form-control col-md-8" name="user_email" id="user_email" placeholder="用户邮箱"/>
+                        </div>
+                        <div class="form-group">
+                            <input type="text" class="form-control" name="title" id="title" placeholder="标题">
+                        </div>
+                        <div class="form-group">
+                            <textarea type="text" class="form-control" rows="5" name="content" id="content" placeholder="内容"></textarea>
                         </div>
                     </div>
-                    <div class="form-group">
-                        <input type="text" class="form-control" name="title" id="title" placeholder="标题">
-                    </div>
-                    <div class="form-group">
-                        <textarea type="text" class="form-control" rows="5" name="content" id="content" placeholder="内容"></textarea>
+                    <div class="modal-footer">
+                        <button type="button" data-dismiss="modal" class="btn btn-danger"> {{trans('home.ticket_cancel')}} </button>
+                        <button type="button" data-dismiss="modal" class="btn btn-success" onclick="createTicket()"> {{trans('home.ticket_confirm')}} </button>
                     </div>
                 </div>
-                <div class="modal-footer">
-                    <button type="button" data-dismiss="modal" class="btn btn-danger"> {{trans('home.ticket_cancel')}} </button>
-                    <button type="button" data-dismiss="modal" class="btn btn-success" onclick="createTicket()"> {{trans('home.ticket_confirm')}} </button>
-                </div>
             </div>
         </div>
-    </div>
+    @endcan
 @endsection
 @section('javascript')
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
@@ -136,6 +146,7 @@
         window.location.href = '{{route('admin.ticket.index')}}?email=' + $('#email').val();
       }
 
+      @can('admin.ticket.store')
       // 发起工单
       function createTicket() {
         const id = $('#user_id').val();
@@ -182,5 +193,6 @@
           }
         });
       }
+        @endcan
     </script>
 @endsection

+ 90 - 82
resources/views/admin/ticket/reply.blade.php

@@ -7,7 +7,9 @@
                 <div class="panel-actions">
                     <a href="{{route('admin.ticket.index')}}" class="btn btn-default">返 回</a>
                     @if($ticket->status !== 2)
-                        <button class="btn btn-danger" onclick="closeTicket()"> {{trans('home.ticket_close')}} </button>
+                        @can('admin.ticket.destroy')
+                            <button class="btn btn-danger" onclick="closeTicket()"> {{trans('home.ticket_close')}} </button>
+                        @endcan
                     @endif
                 </div>
             </div>
@@ -22,101 +24,107 @@
                 </div>
             </div>
             @if($ticket->status !== 2)
-                <div class="panel-footer pb-30">
-                    <form>
-                        <div class="input-group">
-                            <input type="text" class="form-control" id="editor" placeholder="{{trans('home.ticket_reply_placeholder')}}"/>
-                            <span class="input-group-btn">
+                @can('admin.ticket.update')
+                    <div class="panel-footer pb-30">
+                        <form>
+                            <div class="input-group">
+                                <input type="text" class="form-control" id="editor" placeholder="{{trans('home.ticket_reply_placeholder')}}"/>
+                                <span class="input-group-btn">
                                 <button type="button" class="btn btn-primary" onclick="replyTicket()"> {{trans('home.ticket_reply')}}</button>
                             </span>
-                        </div>
-                    </form>
-                </div>
+                            </div>
+                        </form>
+                    </div>
+                @endcan
             @endif
         </div>
     </div>
 @endsection
 @section('javascript')
     <script type="text/javascript">
-      //回车检测
-      $(document).on('keypress', 'input', function(e) {
-        if (e.which === 13) {
-          replyTicket();
-          return false;
+        @can('admin.ticket.destroy')
+        // 关闭工单
+        function closeTicket() {
+          swal.fire({
+            title: '确定关闭工单?',
+            icon: 'question',
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'DELETE',
+                url: '{{route('admin.ticket.destroy', $ticket->id)}}',
+                async: true,
+                data: {_token: '{{csrf_token()}}'},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({
+                      title: ret.message,
+                      icon: 'success',
+                      timer: 1000,
+                      showConfirmButton: false,
+                    }).then(() => window.location.href = '{{route('admin.ticket.index')}}');
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+                error: function() {
+                  swal.fire({title: '未知错误!请通知客服', icon: 'error'});
+                },
+              });
+            }
+          });
         }
-      });
+        @endcan
 
-      // 关闭工单
-      function closeTicket() {
-        swal.fire({
-          title: '确定关闭工单?',
-          icon: 'question',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: '{{route('admin.ticket.destroy', $ticket->id)}}',
-              async: true,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({
-                    title: ret.message,
-                    icon: 'success',
-                    timer: 1000,
-                    showConfirmButton: false,
-                  }).then(() => window.location.href = '{{route('admin.ticket.index')}}');
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
-              error: function() {
-                swal.fire({title: '未知错误!请通知客服', icon: 'error'});
-              },
-            });
+        @can('admin.ticket.update')
+        //回车检测
+        $(document).on('keypress', 'input', function(e) {
+          if (e.which === 13) {
+            replyTicket();
+            return false;
           }
         });
-      }
 
-      // 回复工单
-      function replyTicket() {
-        const content = document.getElementById('editor').value;
+        // 回复工单
+        function replyTicket() {
+          const content = document.getElementById('editor').value;
 
-        if (content.trim() === '') {
-          swal.fire({title: '您未填写工单内容!', icon: 'warning', timer: 1500});
-          return false;
-        }
-        swal.fire({
-          title: '确定回复工单?',
-          icon: 'question',
-          allowEnterKey: false,
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'PUT',
-              url: '{{route('admin.ticket.update', $ticket->id)}}',
-              data: {_token: '{{csrf_token()}}', content: content},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
-              error: function() {
-                swal.fire({title: '未知错误!请查看运行日志', icon: 'error'});
-              },
-            });
+          if (content.trim() === '') {
+            swal.fire({title: '您未填写工单内容!', icon: 'warning', timer: 1500});
+            return false;
           }
-        });
-      }
+          swal.fire({
+            title: '确定回复工单?',
+            icon: 'question',
+            allowEnterKey: false,
+            showCancelButton: true,
+            cancelButtonText: '{{trans('home.ticket_close')}}',
+            confirmButtonText: '{{trans('home.ticket_confirm')}}',
+          }).then((result) => {
+            if (result.value) {
+              $.ajax({
+                method: 'PUT',
+                url: '{{route('admin.ticket.update', $ticket->id)}}',
+                data: {_token: '{{csrf_token()}}', content: content},
+                dataType: 'json',
+                success: function(ret) {
+                  if (ret.status === 'success') {
+                    swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                  } else {
+                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                  }
+                },
+                error: function() {
+                  swal.fire({title: '未知错误!请查看运行日志', icon: 'error'});
+                },
+              });
+            }
+          });
+        }
+        @endcan
     </script>
 @endsection

+ 60 - 53
resources/views/admin/user/export.blade.php

@@ -26,7 +26,11 @@
                         <tr>
                             <td>{{$loop->iteration}}</td>
                             <td>
-                                <a href="{{route('admin.node.edit', $node)}}" target="_blank"> {{$node->name}} </a>
+                                @can('admin.node.edit')
+                                    <a href="{{route('admin.node.edit', $node)}}" target="_blank"> {{$node->name}} </a>
+                                @else
+                                    {{$node->name}}
+                                @endcan
                             </td>
                             <td>
                                 @if($node->compatible) <span class="label label-info">兼</span> @endif
@@ -36,17 +40,19 @@
                             <td>{{$node->server}}</td>
                             <td>{{$node->ip}}</td>
                             <td>
-                                <div class="btn-group">
-                                    <button class="btn btn-sm btn-outline-info"
-                                            onclick="getInfo('{{$node->id}}','code')"><i class="icon fa-code"></i>
-                                    </button>
-                                    <button class="btn btn-sm btn-outline-info"
-                                            onclick="getInfo('{{$node->id}}','qrcode')"><i class="icon fa-qrcode"></i>
-                                    </button>
-                                    <button class="btn btn-sm btn-outline-info"
-                                            onclick="getInfo('{{$node->id}}','text')"><i class="icon fa-list"></i>
-                                    </button>
-                                </div>
+                                @can('admin.user.exportProxy')
+                                    <div class="btn-group">
+                                        <button class="btn btn-sm btn-outline-info"
+                                                onclick="getInfo('{{$node->id}}','code')"><i class="icon fa-code"></i>
+                                        </button>
+                                        <button class="btn btn-sm btn-outline-info"
+                                                onclick="getInfo('{{$node->id}}','qrcode')"><i class="icon fa-qrcode"></i>
+                                        </button>
+                                        <button class="btn btn-sm btn-outline-info"
+                                                onclick="getInfo('{{$node->id}}','text')"><i class="icon fa-list"></i>
+                                        </button>
+                                    </div>
+                                @endcan
                             </td>
                         </tr>
                     @endforeach
@@ -72,45 +78,46 @@
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
     <script src="/assets/custom/Plugin/jquery-qrcode/jquery.qrcode.min.js" type="text/javascript"></script>
     <script src="/assets/global/js/Plugin/webui-popover.js" type="text/javascript"></script>
-
-    <script type="text/javascript">
-      function getInfo(id, type) {
-        $.post("{{route('admin.user.exportProxy', $user)}}", {_token: '{{csrf_token()}}', id: id, type: type},
-            function(ret) {
-              if (ret.status === 'success') {
-                switch (type) {
-                  case 'code':
-                    swal.fire({
-                      html: '<textarea class="form-control" rows="8" readonly="readonly">' + ret.data +
-                          '</textarea>' +
-                          '<a href="' + ret.data + '" class="btn btn-danger btn-block mt-10">打开' +
-                          ret.title + '</a>',
-                      showConfirmButton: false,
-                    });
-                    break;
-                  case 'qrcode':
-                    swal.fire({
-                      title: '{{trans('home.scan_qrcode')}}',
-                      html: '<div id="qrcode"></div>',
-                      onBeforeOpen: () => {
-                        $('#qrcode').qrcode({text: ret.data});
-                      },
-                      showConfirmButton: false,
-                    });
-                    break;
-                  case 'text':
-                    swal.fire({
-                      title: '{{trans('home.setting_info')}}',
-                      html: '<textarea class="form-control" rows="12" readonly="readonly">' + ret.data +
-                          '</textarea>',
-                      showConfirmButton: false,
-                    });
-                    break;
-                  default:
-                    swal.fire({title: ret.title, text: ret.data});
-                }
-              }
-            });
-      }
-    </script>
+    @can('admin.user.exportProxy')
+        <script type="text/javascript">
+          function getInfo(id, type) {
+            $.post("{{route('admin.user.exportProxy', $user)}}", {_token: '{{csrf_token()}}', id: id, type: type},
+                function(ret) {
+                  if (ret.status === 'success') {
+                    switch (type) {
+                      case 'code':
+                        swal.fire({
+                          html: '<textarea class="form-control" rows="8" readonly="readonly">' + ret.data +
+                              '</textarea>' +
+                              '<a href="' + ret.data + '" class="btn btn-danger btn-block mt-10">打开' +
+                              ret.title + '</a>',
+                          showConfirmButton: false,
+                        });
+                        break;
+                      case 'qrcode':
+                        swal.fire({
+                          title: '{{trans('home.scan_qrcode')}}',
+                          html: '<div id="qrcode"></div>',
+                          onBeforeOpen: () => {
+                            $('#qrcode').qrcode({text: ret.data});
+                          },
+                          showConfirmButton: false,
+                        });
+                        break;
+                      case 'text':
+                        swal.fire({
+                          title: '{{trans('home.setting_info')}}',
+                          html: '<textarea class="form-control" rows="12" readonly="readonly">' + ret.data +
+                              '</textarea>',
+                          showConfirmButton: false,
+                        });
+                        break;
+                      default:
+                        swal.fire({title: ret.title, text: ret.data});
+                    }
+                  }
+                });
+          }
+        </script>
+    @endcan
 @endsection

+ 50 - 40
resources/views/admin/user/group/index.blade.php

@@ -7,11 +7,13 @@
         <div class="panel">
             <div class="panel-heading">
                 <h2 class="panel-title">用户分组控制<small>(同一节点可分配至多个分组,一个用户只能属于一个分组;对于用户可见/可用节点:先按分组后按等级)</small></h2>
-                <div class="panel-actions">
-                    <a class="btn btn-primary" href="{{route('admin.user.group.create')}}">
-                        <i class="icon wb-plus" aria-hidden="true"></i>添加分组
-                    </a>
-                </div>
+                @can('admin.user.group.create')
+                    <div class="panel-actions">
+                        <a class="btn btn-primary" href="{{route('admin.user.group.create')}}">
+                            <i class="icon wb-plus" aria-hidden="true"></i>添加分组
+                        </a>
+                    </div>
+                @endcan
             </div>
             <div class="panel-body">
                 <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
@@ -28,14 +30,20 @@
                             <td> {{$vo->id}} </td>
                             <td> {{$vo->name}} </td>
                             <td>
-                                <div class="btn-group">
-                                    <a href="{{route('admin.user.group.edit',$vo)}}" class="btn btn-primary">
-                                        <i class="icon wb-edit" aria-hidden="true"></i>
-                                    </a>
-                                    <button onclick="deleteUserGroup('{{route('admin.user.group.destroy',$vo)}}')" class="btn btn-danger">
-                                        <i class="icon wb-trash" aria-hidden="true"></i>
-                                    </button>
-                                </div>
+                                @canany(['admin.user.group.edit', 'admin.user.group.destroy'])
+                                    <div class="btn-group">
+                                        @can('admin.user.group.edit')
+                                            <a href="{{route('admin.user.group.edit',$vo)}}" class="btn btn-primary">
+                                                <i class="icon wb-edit" aria-hidden="true"></i>
+                                            </a>
+                                        @endcan
+                                        @can('admin.user.group.destroy')
+                                            <button onclick="deleteUserGroup('{{route('admin.user.group.destroy',$vo)}}')" class="btn btn-danger">
+                                                <i class="icon wb-trash" aria-hidden="true"></i>
+                                            </button>
+                                        @endcan
+                                    </div>
+                                @endcanany
                             </td>
                         </tr>
                     @endforeach
@@ -61,33 +69,35 @@
     <script src="/assets/global/vendor/bootstrap-table/bootstrap-table.min.js" type="text/javascript"></script>
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js" type="text/javascript"></script>
 
-    <script type="text/javascript">
-      // 删除用户分组
-      function deleteUserGroup(url) {
-        swal.fire({
-          title: '提示',
-          text: '确定删除该分组吗?',
-          icon: 'info',
-          showCancelButton: true,
-          cancelButtonText: '{{trans('home.ticket_close')}}',
-          confirmButtonText: '{{trans('home.ticket_confirm')}}',
-        }).then((result) => {
-          if (result.value) {
-            $.ajax({
-              method: 'DELETE',
-              url: url,
-              data: {_token: '{{csrf_token()}}'},
-              dataType: 'json',
-              success: function(ret) {
-                if (ret.status === 'success') {
-                  swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
-                } else {
-                  swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                }
-              },
+    @can('admin.user.group.edit')
+        <script type="text/javascript">
+          // 删除用户分组
+          function deleteUserGroup(url) {
+            swal.fire({
+              title: '提示',
+              text: '确定删除该分组吗?',
+              icon: 'info',
+              showCancelButton: true,
+              cancelButtonText: '{{trans('home.ticket_close')}}',
+              confirmButtonText: '{{trans('home.ticket_confirm')}}',
+            }).then((result) => {
+              if (result.value) {
+                $.ajax({
+                  method: 'DELETE',
+                  url: url,
+                  data: {_token: '{{csrf_token()}}'},
+                  dataType: 'json',
+                  success: function(ret) {
+                    if (ret.status === 'success') {
+                      swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+                    } else {
+                      swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
+                    }
+                  },
+                });
+              }
             });
           }
-        });
-      }
-    </script>
+        </script>
+    @endcan
 @endsection

+ 11 - 12
resources/views/admin/user/group/info.blade.php

@@ -6,7 +6,7 @@
     <div class="page-content container">
         <div class="panel">
             <div class="panel-heading">
-                <h2 class="panel-title">@isset($userGroup)编辑@else添加@endisset用戶分组</h2>
+                <h2 class="panel-title">@isset($group)编辑@else添加@endisset用戶分组</h2>
                 <div class="panel-actions">
                     <a href="{{route('admin.user.group.index')}}" class="btn btn-danger">返 回</a>
                 </div>
@@ -18,10 +18,9 @@
                 <x-alert type="danger" :message="$errors->all()"/>
             @endif
             <div class="panel-body">
-                <form action="@isset($userGroup){{route('admin.user.group.update',$userGroup)}}@else{{route('admin.user.group.store')}}@endisset" method="POST"
-                      enctype="multipart/form-data"
-                      class="form-horizontal">
-                    @isset($userGroup)@method('PUT')@endisset
+                <form action="@isset($group){{route('admin.user.group.update',$group)}}@else{{route('admin.user.group.store')}}@endisset"
+                      method="POST" enctype="multipart/form-data" class="form-horizontal">
+                    @isset($group)@method('PUT')@endisset
                     @csrf
                     <div class="form-group row">
                         <label class="col-md-2 col-sm-3 col-form-label" for="name">分组名称</label>
@@ -37,8 +36,8 @@
                                 <button type="button" class="btn btn-danger" id="deselect-all">清 空</button>
                             </div>
                             <select class="form-control" name="nodes[]" id="nodes" data-plugin="multiSelect" multiple>
-                                @foreach($nodeList as $node)
-                                    <option value="{{$node->id}}">{{$node->id . ' - ' . $node->name}}</option>
+                                @foreach($nodes as $id => $name)
+                                    <option value="{{$id}}">{{$id . ' - ' . $name}}</option>
                                 @endforeach
                             </select>
                         </div>
@@ -56,10 +55,10 @@
     <script src="/assets/global/js/Plugin/multi-select.js"></script>
     <script src="/assets/global/js/jquery.quicksearch.js" type="text/javascript"></script>
     <script type="text/javascript">
-        @isset($userGroup)
+        @isset($group)
         $(document).ready(function() {
-          $('#name').val('{{$userGroup->name}}');
-          $('#nodes').multiSelect('select',@json($userGroup->nodes));
+          $('#name').val('{{$group->name}}');
+          $('#nodes').multiSelect('select',@json($group->nodes));
         });
         @endisset
         // 权限列表
@@ -99,13 +98,13 @@
 
         // 全选
         $('#select-all').click(function() {
-          $('#node').multiSelect('select_all');
+          $('#nodes').multiSelect('select_all');
           return false;
         });
 
         // 反选
         $('#deselect-all').click(function() {
-          $('#node').multiSelect('deselect_all');
+          $('#nodes').multiSelect('deselect_all');
           return false;
         });
     </script>

+ 81 - 51
resources/views/admin/user/index.blade.php

@@ -14,14 +14,20 @@
         <div class="panel">
             <div class="panel-heading">
                 <h2 class="panel-title">用户列表</h2>
-                <div class="panel-actions">
-                    <button class="btn btn-outline-default" onclick="batchAddUsers()">
-                        <i class="icon wb-plus" aria-hidden="true"></i>批量生成
-                    </button>
-                    <a href="{{route('admin.user.create')}}" class="btn btn-outline-default">
-                        <i class="icon wb-user-add" aria-hidden="true"></i>添加用户
-                    </a>
-                </div>
+                @canany(['admin.user.batch', 'admin.user.create'])
+                    <div class="panel-actions">
+                        @can('admin.user.batch')
+                            <button class="btn btn-outline-default" onclick="batchAddUsers()">
+                                <i class="icon wb-plus" aria-hidden="true"></i>批量生成
+                            </button>
+                        @endcan
+                        @can('admin.user.create')
+                            <a href="{{route('admin.user.create')}}" class="btn btn-outline-default">
+                                <i class="icon wb-user-add" aria-hidden="true"></i>添加用户
+                            </a>
+                        @endcan
+                    </div>
+                @endcanany
             </div>
             <div class="panel-body">
                 <div class="form-row">
@@ -140,34 +146,50 @@
                                 </span>
                             </td>
                             <td>
-                                <div class="btn-group" role="group">
-                                    <button type="button" class="btn btn-primary dropdown-toggle" data-boundary="viewport" data-toggle="dropdown" aria-expanded="false">
-                                        <i class="icon wb-wrench" aria-hidden="true"></i>
-                                    </button>
-                                    <div class="dropdown-menu" role="menu">
-                                        <a class="dropdown-item" href="{{route('admin.user.edit', ['user'=>$user->id, Request::getQueryString()])}}" role="menuitem">
-                                            <i class="icon wb-edit" aria-hidden="true"></i> 编辑
-                                        </a>
-                                        <a class="dropdown-item" href="javascript:delUser('{{route('admin.user.destroy', $user->id)}}','{{$user->email}}')" role="menuitem">
-                                            <i class="icon wb-trash" aria-hidden="true"></i> 删除
-                                        </a>
-                                        <a class="dropdown-item" href="{{route('admin.user.export', $user)}}" role="menuitem">
-                                            <i class="icon wb-code" aria-hidden="true"></i> 配置信息
-                                        </a>
-                                        <a class="dropdown-item" href="{{route('admin.user.monitor', $user)}}" role="menuitem">
-                                            <i class="icon wb-stats-bars" aria-hidden="true"></i> 流量统计
-                                        </a>
-                                        <a class="dropdown-item" href="{{route('admin.user.online', $user)}}" role="menuitem">
-                                            <i class="icon wb-cloud" aria-hidden="true"></i> 在线巡查
-                                        </a>
-                                        <a class="dropdown-item" href="javascript:resetTraffic('{{$user->id}}','{{$user->email}}')" role="menuitem">
-                                            <i class="icon wb-reload" aria-hidden="true"></i> 重置流量
-                                        </a>
-                                        <a class="dropdown-item" href="javascript:switchToUser('{{$user->id}}')" role="menuitem">
-                                            <i class="icon wb-user" aria-hidden="true"></i> 用户视角
-                                        </a>
+                                @canany(['admin.user.edit', 'admin.user.destroy', 'admin.user.export', 'admin.user.monitor', 'admin.user.online', 'admin.user.reset', 'admin.user.switch'])
+                                    <div class="btn-group" role="group">
+                                        <button type="button" class="btn btn-primary dropdown-toggle" data-boundary="viewport" data-toggle="dropdown" aria-expanded="false">
+                                            <i class="icon wb-wrench" aria-hidden="true"></i>
+                                        </button>
+                                        <div class="dropdown-menu" role="menu">
+                                            @can('admin.user.edit')
+                                                <a class="dropdown-item" href="{{route('admin.user.edit', ['user'=>$user->id, Request::getQueryString()])}}" role="menuitem">
+                                                    <i class="icon wb-edit" aria-hidden="true"></i> 编辑
+                                                </a>
+                                            @endcan
+                                            @can('admin.user.destroy')
+                                                <a class="dropdown-item" href="javascript:delUser('{{route('admin.user.destroy', $user->id)}}','{{$user->email}}')" role="menuitem">
+                                                    <i class="icon wb-trash" aria-hidden="true"></i> 删除
+                                                </a>
+                                            @endcan
+                                            @can('admin.user.export')
+                                                <a class="dropdown-item" href="{{route('admin.user.export', $user)}}" role="menuitem">
+                                                    <i class="icon wb-code" aria-hidden="true"></i> 配置信息
+                                                </a>
+                                            @endcan
+                                            @can('admin.user.monitor')
+                                                <a class="dropdown-item" href="{{route('admin.user.monitor', $user)}}" role="menuitem">
+                                                    <i class="icon wb-stats-bars" aria-hidden="true"></i> 流量统计
+                                                </a>
+                                            @endcan
+                                            @can('admin.user.online')
+                                                <a class="dropdown-item" href="{{route('admin.user.online', $user)}}" role="menuitem">
+                                                    <i class="icon wb-cloud" aria-hidden="true"></i> 在线巡查
+                                                </a>
+                                            @endcan
+                                            @can('admin.user.reset')
+                                                <a class="dropdown-item" href="javascript:resetTraffic('{{$user->id}}','{{$user->email}}')" role="menuitem">
+                                                    <i class="icon wb-reload" aria-hidden="true"></i> 重置流量
+                                                </a>
+                                            @endcan
+                                            @can('admin.user.switch')
+                                                <a class="dropdown-item" href="javascript:switchToUser('{{$user->id}}')" role="menuitem">
+                                                    <i class="icon wb-user" aria-hidden="true"></i> 用户视角
+                                                </a>
+                                            @endcan
+                                        </div>
                                     </div>
-                                </div>
+                                @endcanany
                             </td>
                         </tr>
                     @endforeach
@@ -202,6 +224,22 @@
         $('#enable').val({{Request::input('enable')}});
       });
 
+      //回车检测
+      $(document).on('keypress', 'input', function(e) {
+        if (e.which === 13) {
+          Search();
+          return false;
+        }
+      });
+
+      // 搜索
+      function Search() {
+        window.location.href = '{{route('admin.user.index')}}' + '?id=' + $('#id').val() + '&email=' + $('#email').val() + '&wechat=' +
+            $('#wechat').val() + '&qq=' + $('#qq').val() + '&port=' + $('#port').val() + '&group=' + $('#group option:selected').val() + '&level='
+            + $('#level option:selected').val() + '&status=' + $('#status option:selected').val() + '&enable=' + $('#enable option:selected').val();
+      }
+
+      @can('admin.user.batch')
       // 批量生成账号
       function batchAddUsers() {
         swal.fire({
@@ -225,22 +263,9 @@
           }
         });
       }
+      @endcan
 
-      //回车检测
-      $(document).on('keypress', 'input', function(e) {
-        if (e.which === 13) {
-          Search();
-          return false;
-        }
-      });
-
-      // 搜索
-      function Search() {
-        window.location.href = '{{route('admin.user.index')}}' + '?id=' + $('#id').val() + '&email=' + $('#email').val() + '&wechat=' +
-            $('#wechat').val() + '&qq=' + $('#qq').val() + '&port=' + $('#port').val() + '&group=' + $('#group option:selected').val() + '&level='
-            + $('#level option:selected').val() + '&status=' + $('#status option:selected').val() + '&enable=' + $('#enable option:selected').val();
-      }
-
+      @can('admin.user.destroy')
       // 删除账号
       function delUser(url, email) {
         swal.fire({
@@ -268,7 +293,9 @@
           }
         });
       }
+      @endcan
 
+      @can('admin.user.reset')
       // 重置流量
       function resetTraffic(id, email) {
         swal.fire({
@@ -290,17 +317,20 @@
           }
         });
       }
+      @endcan
 
+      @can('admin.user.switch')
       // 切换用户身份
       function switchToUser(id) {
         $.post('{{route('admin.user.switch')}}', {_token: '{{csrf_token()}}', user_id: id}, function(ret) {
           if (ret.status === 'success') {
-            swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
+            swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.href = '/');
           } else {
             swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
           }
         });
       }
+      @endcan
 
       const clipboard = new ClipboardJS('.copySubscribeLink');
       clipboard.on('success', function() {

+ 54 - 57
resources/views/admin/user/info.blade.php

@@ -9,9 +9,11 @@
             <div class="panel-heading">
                 <h2 class="panel-title">@isset($user) 编辑用户 @else 添加用户 @endisset</h2>
                 @isset($user)
-                    <div class="panel-actions">
-                        <button type="button" class="btn btn-sm btn-danger" onclick="switchToUser()">切换身份</button>
-                    </div>
+                    @can('admin.user.switch')
+                        <div class="panel-actions">
+                            <button type="button" class="btn btn-sm btn-danger" onclick="switchToUser()">切换身份</button>
+                        </div>
+                    @endcan
                 @endisset
             </div>
             <div class="panel-body">
@@ -42,7 +44,7 @@
                                 <label class="col-md-2 col-sm-3 col-form-label" for="level">级别</label>
                                 <div class="col-xl-4 col-sm-8">
                                     <select class="form-control" name="level" id="level" data-plugin="selectpicker" data-style="btn-outline btn-primary">
-                                        @foreach($levelList as $level)
+                                        @foreach($levels as $level)
                                             <option value="{{$level->level}}">{{$level->name}}</option>
                                         @endforeach
                                     </select>
@@ -53,7 +55,7 @@
                                 <div class="col-xl-4 col-sm-8">
                                     <select class="form-control" name="group" id="group" data-plugin="selectpicker" data-style="btn-outline btn-primary">
                                         <option value="0">无分组</option>
-                                        @foreach($groupList as $group)
+                                        @foreach($userGroups as $group)
                                             <option value="{{$group->id}}">{{$group->name}}</option>
                                         @endforeach
                                     </select>
@@ -65,12 +67,11 @@
                                     <div class="col-xl-4 col-sm-8">
                                         <div class="input-group">
                                             <p class="form-control"> {{$user->credit}} </p>
-                                            <span class="input-group-append">
-                                                <button class="btn btn-danger" type="button" data-toggle="modal"
-                                                        data-target="#handle_user_credit">
-                                                    充值
-                                                </button>
-                                            </span>
+                                            @can('admin.user.updateCredit')
+                                                <div class="input-group-append">
+                                                    <button class="btn btn-danger" type="button" data-toggle="modal" data-target="#handle_user_credit">充值</button>
+                                                </div>
+                                            @endcan
                                         </div>
                                     </div>
                                 </div>
@@ -135,27 +136,16 @@
                                     </ul>
                                 </div>
                             </div>
-                            @isset($user)
-                                <div class="form-group row">
-                                    <label class="col-md-2 col-sm-3 col-form-label">管理员</label>
-                                    <div class="col-md-10 col-sm-8">
-                                        <ul class="list-unstyled list-inline">
-                                            <li class="list-inline-item">
-                                                <div class="radio-custom radio-primary">
-                                                    <input type="radio" name="is_admin" id="admin" value="1"/>
-                                                    <label for="admin">是</label>
-                                                </div>
-                                            </li>
-                                            <li class="list-inline-item">
-                                                <div class="radio-custom radio-primary">
-                                                    <input type="radio" name="is_admin" id="customer" value="0"/>
-                                                    <label for="customer">否</label>
-                                                </div>
-                                            </li>
-                                        </ul>
-                                    </div>
+                            <div class="form-group row">
+                                <label class="col-md-2 col-sm-3 col-form-label" for="roles">角色权限</label>
+                                <div class="col-xl-4 col-sm-8">
+                                    <select class="form-control show-tick" name="roles[]" id="roles" data-plugin="selectpicker" data-style="btn-outline btn-primary" multiple>
+                                        @foreach($roles as $key => $description)
+                                            <option value="{{ $key }}">{{ $description }}</option>
+                                        @endforeach
+                                    </select>
                                 </div>
-                            @endisset
+                            </div>
                             <hr>
                             <div class="form-group row">
                                 <label class="col-md-2 col-sm-3 col-form-label" for="wechat">微信</label>
@@ -203,11 +193,11 @@
                                 <div class="col-xl-5 col-sm-8">
                                     <div class="input-group">
                                         <input type="text" class="form-control" name="uuid" id="uuid" placeholder="留空则自动生成随机UUID"/>
-                                        <span class="input-group-append">
+                                        <div class="input-group-append">
                                             <button class="btn btn-success" type="button" onclick="makeUUID()">
                                                 <i class="icon wb-refresh"></i>
                                             </button>
-                                        </span>
+                                        </div>
                                     </div>
                                     <span class="text-help"> V2Ray的账户ID </span>
                                 </div>
@@ -297,6 +287,7 @@
                             </div>
                         </div>
                         <div class="col-12 form-actions text-right">
+                            <a href="{{route('admin.user.index')}}" class="btn btn-secondary">返 回</a>
                             <button type="submit" class="btn btn-success">提 交</button>
                         </div>
                     </div>
@@ -305,31 +296,33 @@
         </div>
     </div>
     @isset($user)
-        <!-- 余额充值 -->
-        <div class="modal fade" id="handle_user_credit" aria-hidden="true" role="dialog" tabindex="-1">
-            <div class="modal-dialog modal-simple modal-center">
-                <div class="modal-content">
-                    <div class="modal-header">
-                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                            <span aria-hidden="true">×</span>
-                        </button>
-                        <h4 class="modal-title">充值</h4>
-                    </div>
-                    <form action="#" method="post" class="modal-body">
-                        <div class="alert alert-danger" style="display: none;" id="msg"></div>
-                        <div class="form-group row">
-                            <label class="col-md-2 col-sm-3 col-form-label" for="amount"> 充值金额 </label>
-                            <input type="number" class="col-sm-4 form-control" name="amount" id="amount" placeholder="填入负值则会扣余额" step="0.01"
-                                   onkeydown="if(event.keyCode===13){return false;}"/>
+        @can('admin.user.updateCredit')
+            <!-- 余额充值 -->
+            <div class="modal fade" id="handle_user_credit" aria-hidden="true" role="dialog" tabindex="-1">
+                <div class="modal-dialog modal-simple modal-center">
+                    <div class="modal-content">
+                        <div class="modal-header">
+                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                                <span aria-hidden="true">×</span>
+                            </button>
+                            <h4 class="modal-title">充值</h4>
+                        </div>
+                        <form action="#" method="post" class="modal-body">
+                            <div class="alert alert-danger" style="display: none;" id="msg"></div>
+                            <div class="form-group row">
+                                <label class="col-md-2 col-sm-3 col-form-label" for="amount"> 充值金额 </label>
+                                <input type="number" class="col-sm-4 form-control" name="amount" id="amount" placeholder="填入负值则会扣余额" step="0.01"
+                                       onkeydown="if(event.keyCode===13){return false;}"/>
+                            </div>
+                        </form>
+                        <div class="modal-footer">
+                            <button data-dismiss="modal" class="btn btn-danger">关闭</button>
+                            <button type="button" class="btn btn-primary" onclick="handleUserCredit()">充值</button>
                         </div>
-                    </form>
-                    <div class="modal-footer">
-                        <button data-dismiss="modal" class="btn btn-danger">关闭</button>
-                        <button type="button" class="btn btn-primary" onclick="handleUserCredit()">充值</button>
                     </div>
                 </div>
             </div>
-        </div>
+        @endcan
     @endisset
 @endsection
 @section('javascript')
@@ -349,7 +342,6 @@
         $('#reset_time').val('{{$user->reset_time}}');
         $('#expired_at').val('{{$user->expired_at}}');
         $("input[name='status'][value='{{$user->status}}']").click();
-        $("input[name='is_admin'][value='{{$user->is_admin}}']").click();
         $('#wechat').val('{{$user->wechat}}');
         $('#qq').val('{{$user->qq}}');
         $('#remark').val('{{$user->remark}}');
@@ -362,6 +354,7 @@
         $('#obfs').selectpicker('val', '{{$user->obfs}}');
         $('#speed_limit').val('{{$user->speed_limit}}');
         $('#uuid').val('{{$user->vmess_id}}');
+        $('#roles').selectpicker('val', @json($user->roles()->pluck('name')));
           @else
           $('#level').selectpicker('val', '0');
           @endisset
@@ -372,6 +365,7 @@
       });
 
       @isset($user)
+      @can('admin.user.switch')
       // 切换用户身份
       function switchToUser() {
         $.ajax({
@@ -391,7 +385,9 @@
           },
         });
       }
+      @endcan
 
+      @can('admin.user.updateCredit')
       // 余额充值
       function handleUserCredit() {
         const amount = $('#amount').val();
@@ -432,6 +428,7 @@
           },
         });
       }
+      @endcan
       @endisset
 
       // ajax同步提交
@@ -468,7 +465,7 @@
             remark: $('#remark').val(),
             level: $('#level').val(),
             group_id: $('#group').val(),
-            is_admin: $('input:radio[name=\'is_admin\']:checked').val(),
+            roles: $('#roles').val(),
             reset_time: $('#reset_time').val(),
             invite_num: $('#invite_num').val(),
             status: $('input:radio[name=\'status\']:checked').val(),
@@ -510,7 +507,7 @@
 
       // 生成随机端口
       function makePort() {
-        $.get('{{route('admin.getPort')}}', function(ret) {
+        $.get('{{route('getPort')}}', function(ret) {
           $('#port').val(ret);
         });
       }

+ 20 - 103
resources/views/auth/error.blade.php

@@ -1,107 +1,24 @@
-<!DOCTYPE html>
-<!--[if IE 8]>
-<html lang="{{app()->getLocale()}}" class="ie8 no-js"> <![endif]-->
-<!--[if IE 9]>
-<html lang="{{app()->getLocale()}}" class="ie9 no-js"> <![endif]-->
-<!--[if !IE]><!-->
-<html lang="{{app()->getLocale()}}">
-<!--<![endif]-->
-<!-- BEGIN HEAD -->
-
-<head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
-    <meta name="description" content="错误">
-    <meta name="author" content="兔姬菌">
-    <meta name="copyright" content="2017-2020©兔姬菌">
-    <title>{{trans('error.title')}}</title>
-
-    <link href="{{asset('favicon.ico')}}" rel="shortcut icon apple-touch-icon">
-    <!-- 样式表/Stylesheets -->
-    <link href="/assets/global/css/bootstrap.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/css/bootstrap-extend.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/css/site.min.css" type="text/css" rel="stylesheet">
-    <!-- 插件/Plugins -->
-    <link href="/assets/global/vendor/animsition/animsition.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/asscrollable/asScrollable.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/slidepanel/slidePanel.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/flag-icon-css/flag-icon.min.css" type="text/css" rel="stylesheet">
+@extends('_layout')
+@section('title', sysConfig('website_name').' - '.trans('error.title'))
+@section('layout_css')
     <link href="/assets/css/examples/errors.min.css" type="text/css" rel="stylesheet">
-    <!-- 字体/Fonts -->
-    <link href="/assets/global/fonts/web-icons/web-icons.min.css" type="text/css" rel="stylesheet">
-    <link href="//fonts.loli.net/css?family=Roboto:300,400,500,300italic" type="text/css" rel="stylesheet">
-    <!--[if lt IE 9]>
-    <script src="/assets/global/vendor/html5shiv/html5shiv.min.js" type="text/javascript"></script>
-    <![endif]-->
-    <!--[if lt IE 10]>
-    <script src="/assets/global/vendor/media-match/media.match.min.js" type="text/javascript"></script>
-    <script src="/assets/global/vendor/respond/respond.min.js" type="text/javascript"></script>
+    @endsection
+@section('body_class', 'page-error page-error-400 layout-full')
+@section('layout_content')
+    <!--[if lt IE 8]>
+    <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
+        <a href="http://browsehappy.com/" target="_blank">升级您的浏览器</a> <br/>You are using an <strong>outdated</strong>
+        browser. Please
+        <a href="http://browsehappy.com/" target="_blank">upgrade your browser</a> to improve your experience.</p>
     <![endif]-->
-    <!-- Scripts -->
-    <script src="/assets/global/vendor/breakpoints/breakpoints.min.js" type="text/javascript"></script>
-    <script type="text/javascript">
-      Breakpoints();
-    </script>
-</head>
-<body class="animsition page-error page-error-400 layout-full">
-<!--[if lt IE 8]>
-<p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
-    <a href="http://browsehappy.com/" target="_blank">升级您的浏览器</a> <br/>You are using an <strong>outdated</strong>
-    browser. Please
-    <a href="http://browsehappy.com/" target="_blank">upgrade your browser</a> to improve your experience.</p>
-<![endif]-->
 
-<div class="page vertical-align text-center" data-animsition-in="fade-in" data-animsition-out="fade-out">
-    <div class="page-content vertical-align-middle">
-        <header>
-            <h3 class="animation-slide-top">(。・_・。)ノI’m sorry~</h3>
-            <p>{{trans('error.title')}}</p>
-        </header>
-        <p class="error-advise">{!! $message !!}</p>
+    <div class="page vertical-align text-center" data-animsition-in="fade-in" data-animsition-out="fade-out">
+        <div class="page-content vertical-align-middle">
+            <header>
+                <h3 class="animation-slide-top">(。・_・。)ノI’m sorry~</h3>
+                <p>{{trans('error.title')}}</p>
+            </header>
+            <p class="error-advise">{!! $message !!}</p>
+        </div>
     </div>
-</div>
-
-<!-- 核心/Core -->
-<script src="/assets/global/vendor/babel-external-helpers/babel-external-helpers.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/jquery/jquery.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/popper-js/umd/popper.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/bootstrap/bootstrap.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/animsition/animsition.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/mousewheel/jquery.mousewheel.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollbar/jquery-asScrollbar.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollable/jquery-asScrollable.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/ashoverscroll/jquery-asHoverScroll.min.js" type="text/javascript"></script>
-<!-- 插件/Plugins -->
-<script src="/assets/global/vendor/screenfull/screenfull.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/slidepanel/jquery-slidePanel.min.js" type="text/javascript"></script>
-<!-- 脚本/Scripts -->
-<script src="/assets/global/js/Component.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin.js" type="text/javascript"></script>
-<script src="/assets/global/js/Base.js" type="text/javascript"></script>
-<script src="/assets/global/js/Config.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Menubar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Sidebar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/PageAside.js" type="text/javascript"></script>
-<script src="/assets/js/Plugin/menu.js" type="text/javascript"></script>
-<!-- 设置/Config -->
-<script src="/assets/global/js/config/colors.js" type="text/javascript"></script>
-<script type="text/javascript">
-  Config.set('assets', '/assets');
-</script>
-<!-- 页面/Page -->
-<script src="/assets/js/Site.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/asscrollable.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/slidepanel.js" type="text/javascript"></script>
-<script type="text/javascript">
-  (function(document, window, $) {
-    'use strict';
-
-    const Site = window.Site;
-    $(document).ready(function() {
-      Site.run();
-    });
-  })(document, window, jQuery);
-</script>
-</body>
-</html>
+@endsection

+ 75 - 150
resources/views/auth/layouts.blade.php

@@ -1,159 +1,84 @@
-<!DOCTYPE html>
-<!--[if IE 8]>
-<html lang="{{app()->getLocale()}}" class="ie8 no-js css-menubar"> <![endif]-->
-<!--[if IE 9]>
-<html lang="{{app()->getLocale()}}" class="ie9 no-js css-menubar"> <![endif]-->
-<!--[if !IE]><!-->
-<html lang="{{app()->getLocale()}}">
-<!--<![endif]-->
-<head>
-    <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="description" content="">
-    <meta name="keywords" content="">
-    <meta name="author" content="兔姬菌">
-    <meta name="copyright" content="2017-2020©兔姬菌">
-    <title>@yield('title')</title>
-    <link href="{{asset('favicon.ico')}}" rel="shortcut icon apple-touch-icon">
-    <!-- Stylesheets -->
-    <link href="/assets/global/css/bootstrap.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/css/bootstrap-extend.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/css/site.min.css" type="text/css" rel="stylesheet">
-    <!-- Plugins -->
-    <link href="/assets/global/vendor/animsition/animsition.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/asscrollable/asScrollable.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/slidepanel/slidePanel.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/flag-icon-css/flag-icon.min.css" type="text/css" rel="stylesheet">
+@extends('_layout')
+@section('title', sysConfig('website_name'))
+@section('layout_css')
     <link href="/assets/css/examples/login-v3.min.css" type="text/css" rel="stylesheet">
-@yield('css')
-<!-- Fonts -->
-    <link href="/assets/global/fonts/web-icons/web-icons.min.css" type="text/css" rel="stylesheet">
-    <link href="//fonts.loli.net/css?family=Roboto:300,400,500,300italic" type="text/css" rel="stylesheet">
-    <!--[if lt IE 9]>
-    <script src="/assets/global/vendor/html5shiv/html5shiv.min.js" type="text/javascript"></script> <![endif]-->
-    <!--[if lt IE 10]>
-    <script src="/assets/global/vendor/media-match/media.match.min.js" type="text/javascript"></script>
-    <script src="/assets/global/vendor/respond/respond.min.js" type="text/javascript"></script> <![endif]-->
-    <!-- Scripts -->
-    <script src="/assets/global/vendor/breakpoints/breakpoints.min.js" type="text/javascript"></script>
-    <script type="text/javascript">
-      Breakpoints();
-    </script>
-</head>
-<body class="animsition page-login-v3 layout-full" style="position: relative;">
-<!--[if lt IE 8]> <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
-    <a href="http://browsehappy.com/" target="_blank">升级您的浏览器</a> <br/>You are using an <strong>outdated</strong>
-    browser. Please
-    <a href="http://browsehappy.com/" target="_blank">upgrade your browser</a> to improve your experience.</p>
-<![endif]-->
-@if(Agent::isMobile() && Agent::is('iOS') && strpos(Agent::getUserAgent(), 'MicroMessenger') !== false)
-    <style type="text/css">
-        .cover-up {
-            opacity: 0.1;
-            filter: alpha(opacity=10);
-        }
-    </style>
-    <div class="m-0 p-0 w-full h-full text-white" style="z-index: 10; position: absolute;">
-        <div class="font-size-16 h-p33 pl-20 pt-20"
-             style="line-height: 1.8; background: url(//gw.alicdn.com/tfs/TB1eSZaNFXXXXb.XXXXXXXXXXXX-750-234.png) center top/contain no-repeat">
-            <p>点击右上角 <i class="icon wb-more-horizontal"></i>,选择在
-                <img src="//gw.alicdn.com/tfs/TB1xwiUNpXXXXaIXXXXXXXXXXXX-55-55.png" class="w-30 h-30 vertical-align-middle m-3" alt="Safari"/>
-                Safari 中打开
-            </p>
-            <p>您就可以正常访问本站了呦~</p>
+    @yield('css')
+    @endsection
+@section('body_class', 'page-login-v3 layout-full position-relative')
+@section('layout_content')
+    <!--[if lt IE 8]> <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
+        <a href="http://browsehappy.com/" target="_blank">升级您的浏览器</a> <br/>You are using an <strong>outdated</strong>
+        browser. Please
+        <a href="http://browsehappy.com/" target="_blank">upgrade your browser</a> to improve your experience.</p>
+    <![endif]-->
+    @if(Agent::isMobile() && Agent::is('iOS') && strpos(Agent::getUserAgent(), 'MicroMessenger') !== false)
+        <style type="text/css">
+            .cover-up {
+                opacity: 0.1;
+                filter: alpha(opacity=10);
+            }
+        </style>
+        <div class="m-0 p-0 w-full h-full text-white" style="z-index: 10; position: absolute;">
+            <div class="font-size-16 h-p33 pl-20 pt-20"
+                 style="line-height: 1.8; background: url(//gw.alicdn.com/tfs/TB1eSZaNFXXXXb.XXXXXXXXXXXX-750-234.png) center top/contain no-repeat">
+                <p>点击右上角 <i class="icon wb-more-horizontal"></i>,选择在
+                    <img src="//gw.alicdn.com/tfs/TB1xwiUNpXXXXaIXXXXXXXXXXXX-55-55.png" class="w-30 h-30 vertical-align-middle m-3" alt="Safari"/>
+                    Safari 中打开
+                </p>
+                <p>您就可以正常访问本站了呦~</p>
+            </div>
         </div>
-    </div>
-@endif
-<div class="page vertical-align text-center cover-up" data-animsition-in="fade-in" data-animsition-out="fade-out">
-    <div class="page-content vertical-align-middle">
-        <div class="animation-slide-top animation-duration-1">
-            <div class="panel">
-                <div class="panel-heading">
-                    <div class="panel-title">
-                        <div class="brand">
-                            <img src="{{sysConfig('website_home_logo')? :'/assets/images/logo64.png'}}" class="brand-img" alt="logo"/>
-                            <h3 class="brand-text">{{sysConfig('website_name')}}</h3>
+    @endif
+    <div class="page vertical-align text-center cover-up" data-animsition-in="fade-in" data-animsition-out="fade-out">
+        <div class="page-content vertical-align-middle">
+            <div class="animation-slide-top animation-duration-1">
+                <div class="panel">
+                    <div class="panel-heading">
+                        <div class="panel-title">
+                            <div class="brand">
+                                <img src="{{sysConfig('website_home_logo')? :'/assets/images/logo64.png'}}" class="brand-img" alt="logo"/>
+                                <h3 class="brand-text">{{sysConfig('website_name')}}</h3>
+                            </div>
                         </div>
-                    </div>
-                    <div class="ribbon ribbon-reverse ribbon-info ribbon-clip">
-                        <button class="ribbon-inner btn dropdown-toggle pt-0" id="language" data-toggle="dropdown" aria-expanded="false">
-                            <i class="font-size-20 wb-globe"></i>
-                        </button>
-                        <div class="dropdown-menu dropdown-menu-bullet" aria-labelledby="language" role="menu">
-                            <a class="dropdown-item" href="{{route('lang', ['locale' => 'zh-CN'])}}" role="menuitem">
-                                <span class="flag-icon flag-icon-cn"></span>
-                                简体中文</a>
-                            <a class="dropdown-item" href="{{route('lang', ['locale' => 'zh-tw'])}}" role="menuitem">
-                                <span class="flag-icon flag-icon-tw"></span>
-                                繁體中文</a>
-                            <a class="dropdown-item" href="{{route('lang', ['locale' => 'en'])}}" role="menuitem">
-                                <span class="flag-icon flag-icon-gb"></span>
-                                English</a>
-                            <a class="dropdown-item" href="{{route('lang', ['locale' => 'ja'])}}" role="menuitem">
-                                <span class="flag-icon flag-icon-jp"></span>
-                                日本語</a>
-                            <a class="dropdown-item" href="{{route('lang', ['locale' => 'ko'])}}" role="menuitem">
-                                <span class="flag-icon flag-icon-kr"></span>
-                                한국어</a>
+                        <div class="ribbon ribbon-reverse ribbon-info ribbon-clip">
+                            <button class="ribbon-inner btn dropdown-toggle pt-0" id="language" data-toggle="dropdown" aria-expanded="false">
+                                <i class="font-size-20 wb-globe"></i>
+                            </button>
+                            <div class="dropdown-menu dropdown-menu-bullet" aria-labelledby="language" role="menu">
+                                <a class="dropdown-item" href="{{route('lang', ['locale' => 'zh-CN'])}}" role="menuitem">
+                                    <span class="flag-icon flag-icon-cn"></span>
+                                    简体中文</a>
+                                <a class="dropdown-item" href="{{route('lang', ['locale' => 'zh-tw'])}}" role="menuitem">
+                                    <span class="flag-icon flag-icon-tw"></span>
+                                    繁體中文</a>
+                                <a class="dropdown-item" href="{{route('lang', ['locale' => 'en'])}}" role="menuitem">
+                                    <span class="flag-icon flag-icon-gb"></span>
+                                    English</a>
+                                <a class="dropdown-item" href="{{route('lang', ['locale' => 'ja'])}}" role="menuitem">
+                                    <span class="flag-icon flag-icon-jp"></span>
+                                    日本語</a>
+                                <a class="dropdown-item" href="{{route('lang', ['locale' => 'ko'])}}" role="menuitem">
+                                    <span class="flag-icon flag-icon-kr"></span>
+                                    한국어</a>
+                            </div>
                         </div>
                     </div>
-                </div>
-                <div class="panel-body">
-                    @yield('content')
+                    <div class="panel-body">
+                        @yield('content')
+                    </div>
                 </div>
             </div>
+            @yield('modal')
         </div>
-        @yield('modal')
     </div>
-</div>
-<!-- 核心/Core -->
-<script src="/assets/global/vendor/babel-external-helpers/babel-external-helpers.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/jquery/jquery.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/popper-js/umd/popper.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/bootstrap/bootstrap.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/animsition/animsition.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/mousewheel/jquery.mousewheel.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollbar/jquery-asScrollbar.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollable/jquery-asScrollable.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/ashoverscroll/jquery-asHoverScroll.min.js" type="text/javascript"></script>
-<!-- 插件/Plugins -->
-<script src="/assets/global/vendor/screenfull/screenfull.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/slidepanel/jquery-slidePanel.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/jquery-placeholder/jquery.placeholder.js" type="text/javascript"></script>
-<!-- 脚本/Scripts -->
-<script src="/assets/global/js/Component.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin.js" type="text/javascript"></script>
-<script src="/assets/global/js/Base.js" type="text/javascript"></script>
-<script src="/assets/global/js/Config.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Menubar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Sidebar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/PageAside.js" type="text/javascript"></script>
-<script src="/assets/js/Plugin/menu.js" type="text/javascript"></script>
-<!-- 设置/Config -->
-<script src="/assets/global/js/config/colors.js" type="text/javascript"></script>
-<script type="text/javascript">
-  Config.set('assets', '/assets');
-</script>
-<!-- Page -->
-@yield('javascript')
-<script src="/assets/js/Site.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/asscrollable.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/slidepanel.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/jquery-placeholder.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/material.js" type="text/javascript"></script>
-<!-- 统计 -->
-{!! sysConfig('website_analytics') !!}
-<!-- 客服 -->
-{!! sysConfig('website_customer_service') !!}
-<script type="text/javascript">
-  (function(document, window, $) {
-    'use strict';
-    const Site = window.Site;
-    $(document).ready(function() {
-      Site.run();
-    });
-  })(document, window, jQuery);
-</script>
-</body>
-</html>
+@endsection
+@section('layout_javascript')
+    <script src="/assets/global/vendor/jquery-placeholder/jquery.placeholder.js" type="text/javascript"></script>
+    <script src="/assets/global/js/Plugin/jquery-placeholder.js" type="text/javascript"></script>
+    <script src="/assets/global/js/Plugin/material.js" type="text/javascript"></script>
+    @yield('javascript')
+    <!-- 统计 -->
+    {!! sysConfig('website_analytics') !!}
+    <!-- 客服 -->
+    {!! sysConfig('website_customer_service') !!}
+@endsection

+ 40 - 120
resources/views/auth/maintenance.blade.php

@@ -1,124 +1,44 @@
-<!DOCTYPE html>
-<!--[if IE 8]>
-<html lang="{{app()->getLocale()}}" class="ie8 no-js css-menubar"> <![endif]-->
-<!--[if IE 9]>
-<html lang="{{app()->getLocale()}}" class="ie9 no-js css-menubar"> <![endif]-->
-<!--[if !IE]><!-->
-<html lang="{{app()->getLocale()}}">
-<!--<![endif]-->
-<head>
-    <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="description" content="">
-    <meta name="keywords" content="">
-    <meta name="author" content="兔姬菌">
-    <meta name="copyright" content="2017-2020©兔姬菌">
-    <title>维护 | Maintenance</title>
-    <link href="{{asset('favicon.ico')}}" rel="shortcut icon apple-touch-icon">
-    <!-- Stylesheets -->
-    <link href="/assets/global/css/bootstrap.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/css/bootstrap-extend.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/css/site.min.css" type="text/css" rel="stylesheet">
-    <!-- Plugins -->
-    <link href="/assets/global/vendor/animsition/animsition.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/asscrollable/asScrollable.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/slidepanel/slidePanel.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/flag-icon-css/flag-icon.min.css" type="text/css" rel="stylesheet">
+@extends('_layout')
+@section('title', '维护 | Maintenance')
+@section('layout_css')
     <link href="/assets/css/examples/maintenance.min.css" type="text/css" rel="stylesheet">
-    <!-- Fonts -->
-    <link href="/assets/global/fonts/web-icons/web-icons.min.css" type="text/css" rel="stylesheet">
-    <link href="//fonts.loli.net/css?family=Roboto:300,400,500,300italic" type="text/css" rel="stylesheet">
+    @endsection
+@section('layout_content')
+   <!--[if lt IE 8]> <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
+        <a href="http://browsehappy.com/" target="_blank">升级您的浏览器</a> <br/>You are using an <strong>outdated</strong>
+        browser. Please
+        <a href="http://browsehappy.com/" target="_blank">upgrade your browser</a> to improve your experience.</p>
+    <![endif]-->
 
-    <!--[if lt IE 9]>
-    <script src="/assets/global/vendor/html5shiv/html5shiv.min.js" type="text/javascript"></script> <![endif]-->
-    <!--[if lt IE 10]>
-    <script src="/assets/global/vendor/media-match/media.match.min.js" type="text/javascript"></script>
-    <script src="/assets/global/vendor/respond/respond.min.js" type="text/javascript"></script> <![endif]-->
-
-    <!-- Scripts -->
-    <script src="/assets/global/vendor/breakpoints/breakpoints.js" type="text/javascript"></script>
+    <!-- Page -->
+    <div class="page vertical-align text-center" data-animsition-in="fade-in" data-animsition-out="fade-out">>
+        <div class="page-content vertical-align-middle">
+            <i class="icon wb-settings icon-spin page-maintenance-icon" aria-hidden="true"></i>
+            <h2>维护建设中</h2>
+            {!! $message !!}
+            <footer class="page-copyright">
+                <p id="countdown"></p>
+            </footer>
+        </div>
+    </div>
+    <!-- End Page -->
+@endsection
+@section('layout_javascript')
     <script>
-      Breakpoints();
+      // 每秒更新计时器
+      const countDownDate = new Date("{{$time}}").getTime();
+      const x = setInterval(function() {
+        const distance = countDownDate - new Date().getTime();
+        const days = Math.floor(distance / 86400000);
+        const hours = Math.floor(distance % 86400000 / 3600000);
+        const minutes = Math.floor(distance % 3600000 / 60000);
+        const seconds = Math.floor(distance % 60000 / 1000);
+        document.getElementById('countdown').innerHTML = '<h2>' + days + ' <span> 天 </span>: ' + hours +
+            ' <span>时</span>: ' + minutes + ' <span>分 </span>: ' + seconds + '<span> 秒</span> </h2>';
+        if (distance <= 0) {
+          clearInterval(x);
+          document.getElementById('countdown').remove();
+        }
+      }, 1000);
     </script>
-</head>
-<body class="animsition page-login-v3 layout-full" style="position: relative;">
-<!--[if lt IE 8]> <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
-    <a href="http://browsehappy.com/" target="_blank">升级您的浏览器</a> <br/>You are using an <strong>outdated</strong>
-    browser. Please
-    <a href="http://browsehappy.com/" target="_blank">upgrade your browser</a> to improve your experience.</p>
-<![endif]-->
-
-<!-- Page -->
-<div class="page vertical-align text-center" data-animsition-in="fade-in" data-animsition-out="fade-out">>
-    <div class="page-content vertical-align-middle">
-        <i class="icon wb-settings icon-spin page-maintenance-icon" aria-hidden="true"></i>
-        <h2>维护建设中</h2>
-        {!! $message !!}
-        <footer class="page-copyright">
-            <p id="countdown"></p>
-        </footer>
-    </div>
-</div>
-<!-- End Page -->
-
-<!-- 核心/Core -->
-<script src="/assets/global/vendor/babel-external-helpers/babel-external-helpers.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/jquery/jquery.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/popper-js/umd/popper.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/bootstrap/bootstrap.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/animsition/animsition.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/mousewheel/jquery.mousewheel.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollbar/jquery-asScrollbar.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollable/jquery-asScrollable.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/ashoverscroll/jquery-asHoverScroll.min.js" type="text/javascript"></script>
-<!-- 插件/Plugins -->
-<script src="/assets/global/vendor/screenfull/screenfull.js"></script>
-<script src="/assets/global/vendor/slidepanel/jquery-slidePanel.js"></script>
-<!-- 脚本/Scripts -->
-<script src="/assets/global/js/Component.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin.js" type="text/javascript"></script>
-<script src="/assets/global/js/Base.js" type="text/javascript"></script>
-<script src="/assets/global/js/Config.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Menubar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Sidebar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/PageAside.js" type="text/javascript"></script>
-<script src="/assets/js/Plugin/menu.js" type="text/javascript"></script>
-<!-- 设置/Config -->
-<script src="/assets/global/js/config/colors.js" type="text/javascript"></script>
-<script type="text/javascript">
-  Config.set('assets', '/assets');
-</script>
-<!-- Page -->
-<script src="/assets/js/Site.js"></script>
-<script src="/assets/global/js/Plugin/asscrollable.js"></script>
-<script src="/assets/global/js/Plugin/slidepanel.js"></script>
-
-<script>
-  (function(document, window, $) {
-    'use strict';
-
-    var Site = window.Site;
-    $(document).ready(function() {
-      Site.run();
-    });
-  })(document, window, jQuery);
-
-  // 每秒更新计时器
-  const countDownDate = new Date("{{$time}}").getTime();
-  const x = setInterval(function() {
-    const distance = countDownDate - new Date().getTime();
-    const days = Math.floor(distance / 86400000);
-    const hours = Math.floor(distance % 86400000 / 3600000);
-    const minutes = Math.floor(distance % 3600000 / 60000);
-    const seconds = Math.floor(distance % 60000 / 1000);
-    document.getElementById('countdown').innerHTML = '<h2>' + days + ' <span> 天 </span>: ' + hours +
-        '    <span>时</span>: ' + minutes + ' <span>分 </span>: ' + seconds + '<span> 秒</span> </h2>';
-    if (distance <= 0) {
-      clearInterval(x);
-      document.getElementById('countdown').remove();
-    }
-  }, 1000);
-</script>
-</body>
-</html>
+@endsection

+ 1 - 1
resources/views/components/chat-unit.blade.php

@@ -1,4 +1,4 @@
-<div class="chat @if(($ticket->admin && !$user->is_admin) ||(!$ticket->admin && $user->is_admin)) chat-left @endif">
+<div class="chat @if(($ticket->admin_id && $ticket->admin_id !== $user->id) ||($ticket->user_id && $ticket->user_id !== $user->id)) chat-left @endif">
     <div class="chat-avatar">
         <p class="avatar" data-toggle="tooltip" href="#" data-placement="right" title="" data-original-title="{{($ticket->admin ?? $ticket->user)->email}}">
             <x-avatar :user="$ticket->admin ?? $ticket->user"/>

+ 218 - 301
resources/views/user/layouts.blade.php

@@ -1,317 +1,234 @@
-<!DOCTYPE html>
-<!--[if IE 8]>
-<html lang="{{app()->getLocale()}}" class="ie8 no-js css-menubar"> <![endif]-->
-<!--[if IE 9]>
-<html lang="{{app()->getLocale()}}" class="ie9 no-js css-menubar"> <![endif]-->
-<!--[if !IE]><!-->
-<html lang="{{app()->getLocale()}}" class="no-js css-menubar">
-<!--<![endif]-->
-<head>
-    <meta charset="utf-8">
-    <title>{{sysConfig('website_name')}}</title>
-    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <meta name="description" content="">
-    <meta name="keywords" content="">
-    <meta name="author" content="兔姬菌">
-    <meta name="copyright" content="2017-2020©兔姬菌">
-    <link href="{{asset('favicon.ico')}}" rel="shortcut icon apple-touch-icon">
-    <!-- 样式表/Stylesheets -->
-    <link href="/assets/global/css/bootstrap.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/css/bootstrap-extend.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/css/site.min.css" type="text/css" rel="stylesheet">
-    <!-- 插件/Plugins -->
-    <link href="/assets/global/vendor/animsition/animsition.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/asscrollable/asScrollable.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/slidepanel/slidePanel.min.css" type="text/css" rel="stylesheet">
-    <link href="/assets/global/vendor/flag-icon-css/flag-icon.min.css" type="text/css" rel="stylesheet">
-    <!-- 字体/Fonts -->
-    <link href="/assets/global/fonts/web-icons/web-icons.min.css" type="text/css" rel="stylesheet">
-    <link href="//fonts.loli.net/css?family=Roboto:300,400,500,300italic" type="text/css" rel="stylesheet">
-    <!--[if lt IE 9]>
-    <script src="/assets/global/vendor/html5shiv/html5shiv.min.js" type="text/javascript"></script>
-    <![endif]-->
-    <!--[if lt IE 10]>
-    <script src="/assets/global/vendor/media-match/media.match.min.js" type="text/javascript"></script>
-    <script src="/assets/global/vendor/respond/respond.min.js" type="text/javascript"></script>
-    <![endif]-->
-    <!-- Scripts -->
-    <script src="/assets/global/vendor/breakpoints/breakpoints.min.js" type="text/javascript"></script>
-    <script type="text/javascript">
-      Breakpoints();
-    </script>
+@extends('_layout')
+@section('title', sysConfig('website_name'))
+@section('layout_css')
     @yield('css')
-</head>
-
-<body class="animsition dashboard">
-<nav class="site-navbar navbar navbar-default navbar-fixed-top navbar-mega navbar-inverse bg-indigo-600"
-     role="navigation">
-    <div class="navbar-header">
-        <button type="button" class="navbar-toggler hamburger hamburger-close navbar-toggler-left hided"
-                data-toggle="menubar">
-            <span class="sr-only">Toggle navigation切换导航</span>
-            <span class="hamburger-bar"></span>
-        </button>
-        <button type="button" class="navbar-toggler collapsed" data-target="#site-navbar-collapse"
-                data-toggle="collapse">
-            <i class="icon wb-more-horizontal" aria-hidden="true"></i>
-        </button>
-        <div class="navbar-brand navbar-brand-center">
-            <img src="{{sysConfig('website_logo') ?: '/assets/images/logo64.png'}}"
-                 class="navbar-brand-logo" alt="logo"/>
-            <span
-                    class="navbar-brand-text hidden-xs-down"> {{sysConfig('website_name')}}</span>
+@endsection
+@section('body_class', 'dashboard')
+@section('layout_content')
+    <nav class="site-navbar navbar navbar-default navbar-fixed-top navbar-mega navbar-inverse bg-indigo-600"
+         role="navigation">
+        <div class="navbar-header">
+            <button type="button" class="navbar-toggler hamburger hamburger-close navbar-toggler-left hided"
+                    data-toggle="menubar">
+                <span class="sr-only">Toggle navigation切换导航</span>
+                <span class="hamburger-bar"></span>
+            </button>
+            <button type="button" class="navbar-toggler collapsed" data-target="#site-navbar-collapse"
+                    data-toggle="collapse">
+                <i class="icon wb-more-horizontal" aria-hidden="true"></i>
+            </button>
+            <div class="navbar-brand navbar-brand-center">
+                <img src="{{sysConfig('website_logo') ?: '/assets/images/logo64.png'}}"
+                     class="navbar-brand-logo" alt="logo"/>
+                <span
+                        class="navbar-brand-text hidden-xs-down"> {{sysConfig('website_name')}}</span>
+            </div>
         </div>
-    </div>
-    <div class="navbar-container container-fluid">
-        <div class="collapse navbar-collapse navbar-collapse-toolbar" id="site-navbar-collapse">
-            <ul class="nav navbar-toolbar">
-                <li class="nav-item hidden-float" id="toggleMenubar">
-                    <a class="nav-link" data-toggle="menubar" href="#" role="button">
-                        <i class="icon hamburger hamburger-arrow-left">
-                            <span class="sr-only">Toggle menubar | 切换菜单栏</span>
-                            <span class="hamburger-bar"></span>
-                        </i>
+        <div class="navbar-container container-fluid">
+            <div class="collapse navbar-collapse navbar-collapse-toolbar" id="site-navbar-collapse">
+                <ul class="nav navbar-toolbar">
+                    <li class="nav-item hidden-float" id="toggleMenubar">
+                        <a class="nav-link" data-toggle="menubar" href="#" role="button">
+                            <i class="icon hamburger hamburger-arrow-left">
+                                <span class="sr-only">Toggle menubar | 切换菜单栏</span>
+                                <span class="hamburger-bar"></span>
+                            </i>
+                        </a>
+                    </li>
+                    <li class="nav-item hidden-sm-down" id="toggleFullscreen">
+                        <a class="nav-link icon icon-fullscreen" data-toggle="fullscreen" href="#" role="button">
+                            <span class="sr-only">Toggle fullscreen | 切换全屏</span>
+                        </a>
+                    </li>
+                </ul>
+                <ul class="nav navbar-toolbar navbar-right navbar-toolbar-right">
+                    <li class="nav-item dropdown">
+                        <a href="javascript:void(0)" class="nav-link" data-toggle="dropdown" data-animation="scale-up"
+                           aria-expanded="false" role="button">
+                            <span class="flag-icon wb-flag"></span>
+                            <span class="flag-icon icon wb-chevron-down-mini"></span>
+                        </a>
+                        <div class="dropdown-menu" role="menu">
+                            <a href="{{route('lang', ['locale' => 'zh-CN'])}}" class="dropdown-item" role="menuitem">
+                                <span class="flag-icon flag-icon-cn"></span>
+                                简体中文</a>
+                            <a href="{{route('lang', ['locale' => 'zh-tw'])}}" class="dropdown-item" role="menuitem">
+                                <span class="flag-icon flag-icon-tw"></span>
+                                繁體中文</a>
+                            <a href="{{route('lang', ['locale' => 'en'])}}" class="dropdown-item" role="menuitem">
+                                <span class="flag-icon flag-icon-gb"></span>
+                                English</a>
+                            <a href="{{route('lang', ['locale' => 'ja'])}}" class="dropdown-item" role="menuitem">
+                                <span class="flag-icon flag-icon-jp"></span>
+                                日本語</a>
+                            <a href="{{route('lang', ['locale' => 'ko'])}}" class="dropdown-item" role="menuitem">
+                                <span class="flag-icon flag-icon-kr"></span>
+                                한국어</a>
+                        </div>
+                    </li>
+                    <li class="nav-item dropdown">
+                        <a href="#" aria-expanded="false" class="nav-link navbar-avatar" data-animation="scale-up"
+                           data-toggle="dropdown" role="button">
+                        <span class="avatar avatar-online">
+                            <x-avatar :user="Auth::getUser()"/><i></i>
+                        </span>
+                        </a>
+                        <div class="dropdown-menu" role="menu">
+                            @can('admin.index')
+                                <a href="{{route('admin.index')}}" class="dropdown-item" role="menuitem">
+                                    <i class="icon wb-user" aria-hidden="true"></i>
+                                    {{trans('home.console')}}
+                                </a>
+                            @endcan
+                            <a href="{{route('profile')}}" class="dropdown-item" role="menuitem">
+                                <i class="icon wb-user" aria-hidden="true"></i>
+                                {{trans('home.profile')}}
+                            </a>
+                            <div class="dropdown-divider" role="presentation"></div>
+                            <a href="{{route('logout')}}" class="dropdown-item" role="menuitem">
+                                <i class="icon wb-power" aria-hidden="true"></i>
+                                {{trans('home.logout')}}
+                            </a>
+                        </div>
+                    </li>
+                </ul>
+            </div>
+        </div>
+    </nav>
+    <div class="site-menubar site-menubar-light">
+        <div class="site-menubar-body">
+            <ul class="site-menu" data-plugin="menu">
+                <li class="site-menu-item {{request()->routeIs('home', 'profile' ,'article') ? 'active open' : ''}}">
+                    <a href="{{route('home')}}">
+                        <i class="site-menu-icon wb-dashboard" aria-hidden="true"></i>
+                        <span class="site-menu-title">{{trans('home.home')}}</span>
                     </a>
                 </li>
-                <li class="nav-item hidden-sm-down" id="toggleFullscreen">
-                    <a class="nav-link icon icon-fullscreen" data-toggle="fullscreen" href="#" role="button">
-                        <span class="sr-only">Toggle fullscreen | 切换全屏</span>
+                <li class="site-menu-item {{request()->routeIs('shop', 'buy', 'orderDetail') ? 'active open' : ''}}">
+                    <a href="{{route('shop')}}">
+                        <i class="site-menu-icon wb-shopping-cart" aria-hidden="true"></i>
+                        <span class="site-menu-title">{{trans('home.services')}}</span>
                     </a>
                 </li>
-            </ul>
-            <ul class="nav navbar-toolbar navbar-right navbar-toolbar-right">
-                <li class="nav-item dropdown">
-                    <a href="javascript:void(0)" class="nav-link" data-toggle="dropdown" data-animation="scale-up"
-                       aria-expanded="false" role="button">
-                        <span class="flag-icon wb-flag"></span>
-                        <span class="flag-icon icon wb-chevron-down-mini"></span>
+                <li class="site-menu-item {{request()->routeIs('node') ? 'active open' : ''}}">
+                    <a href="{{route('node')}}">
+                        <i class="site-menu-icon wb-grid-4" aria-hidden="true"></i>
+                        <span class="site-menu-title">{{trans('home.nodeList')}}</span>
                     </a>
-                    <div class="dropdown-menu" role="menu">
-                        <a href="{{route('lang', ['locale' => 'zh-CN'])}}" class="dropdown-item" role="menuitem">
-                            <span class="flag-icon flag-icon-cn"></span>
-                            简体中文</a>
-                        <a href="{{route('lang', ['locale' => 'zh-tw'])}}" class="dropdown-item" role="menuitem">
-                            <span class="flag-icon flag-icon-tw"></span>
-                            繁體中文</a>
-                        <a href="{{route('lang', ['locale' => 'en'])}}" class="dropdown-item" role="menuitem">
-                            <span class="flag-icon flag-icon-gb"></span>
-                            English</a>
-                        <a href="{{route('lang', ['locale' => 'ja'])}}" class="dropdown-item" role="menuitem">
-                            <span class="flag-icon flag-icon-jp"></span>
-                            日本語</a>
-                        <a href="{{route('lang', ['locale' => 'ko'])}}" class="dropdown-item" role="menuitem">
-                            <span class="flag-icon flag-icon-kr"></span>
-                            한국어</a>
-                    </div>
                 </li>
-                <li class="nav-item dropdown">
-                    <a href="#" aria-expanded="false" class="nav-link navbar-avatar" data-animation="scale-up"
-                       data-toggle="dropdown" role="button">
-                        <span class="avatar avatar-online">
-                            <x-avatar :user="Auth::getUser()"/><i></i>
-                        </span>
+                <li class="site-menu-item {{request()->routeIs('help') ? 'active open' : ''}}">
+                    <a href="{{route('help')}}">
+                        <i class="site-menu-icon wb-info-circle" aria-hidden="true"></i>
+                        <span class="site-menu-title">{{trans('home.help')}}</span>
                     </a>
-                    <div class="dropdown-menu" role="menu">
-                        @if(Auth::getUser()->is_admin)
-                            <a href="{{route('admin.index')}}" class="dropdown-item" role="menuitem">
-                                <i class="icon wb-user" aria-hidden="true"></i>
-                                {{trans('home.console')}}
-                            </a>
+                </li>
+                @php
+                    $openTicket = App\Models\Ticket::uid()->whereStatus(1)->count()
+                @endphp
+                <li class="site-menu-item {{request()->routeIs('ticket', 'replyTicket') ? 'active open' : ''}}">
+                    <a href="{{route('ticket')}}">
+                        <i class="site-menu-icon wb-chat-working" aria-hidden="true"></i>
+                        <span class="site-menu-title">{{trans('home.ticket_title')}}</span>
+                        @if($openTicket > 0)
+                            <div class="site-menu-badge">
+                                <span class="badge badge-pill badge-success">{{$openTicket}}</span>
+                            </div>
                         @endif
-                        <a href="{{route('profile')}}" class="dropdown-item" role="menuitem">
-                            <i class="icon wb-user" aria-hidden="true"></i>
-                            {{trans('home.profile')}}
-                        </a>
-                        <div class="dropdown-divider" role="presentation"></div>
-                        <a href="{{route('logout')}}" class="dropdown-item" role="menuitem">
-                            <i class="icon wb-power" aria-hidden="true"></i>
-                            {{trans('home.logout')}}
-                        </a>
-                    </div>
+                    </a>
                 </li>
-            </ul>
-        </div>
-    </div>
-</nav>
-<div class="site-menubar site-menubar-light">
-    <div class="site-menubar-body">
-        <ul class="site-menu" data-plugin="menu">
-            <li class="site-menu-item {{request()->routeIs('home', 'profile' ,'article') ? 'active open' : ''}}">
-                <a href="{{route('home')}}">
-                    <i class="site-menu-icon wb-dashboard" aria-hidden="true"></i>
-                    <span class="site-menu-title">{{trans('home.home')}}</span>
-                </a>
-            </li>
-            <li class="site-menu-item {{request()->routeIs('shop', 'buy', 'orderDetail') ? 'active open' : ''}}">
-                <a href="{{route('shop')}}">
-                    <i class="site-menu-icon wb-shopping-cart" aria-hidden="true"></i>
-                    <span class="site-menu-title">{{trans('home.services')}}</span>
-                </a>
-            </li>
-            <li class="site-menu-item {{request()->routeIs('node') ? 'active open' : ''}}">
-                <a href="{{route('node')}}">
-                    <i class="site-menu-icon wb-grid-4" aria-hidden="true"></i>
-                    <span class="site-menu-title">{{trans('home.nodeList')}}</span>
-                </a>
-            </li>
-            <li class="site-menu-item {{request()->routeIs('help') ? 'active open' : ''}}">
-                <a href="{{route('help')}}">
-                    <i class="site-menu-icon wb-info-circle" aria-hidden="true"></i>
-                    <span class="site-menu-title">{{trans('home.help')}}</span>
-                </a>
-            </li>
-            @php
-                $openTicket = App\Models\Ticket::uid()->whereStatus(1)->count()
-            @endphp
-            <li class="site-menu-item {{request()->routeIs('ticket', 'replyTicket') ? 'active open' : ''}}">
-                <a href="{{route('ticket')}}">
-                    <i class="site-menu-icon wb-chat-working" aria-hidden="true"></i>
-                    <span class="site-menu-title">{{trans('home.ticket_title')}}</span>
-                    @if($openTicket > 0)
-                        <div class="site-menu-badge">
-                            <span class="badge badge-pill badge-success">{{$openTicket}}</span>
-                        </div>
+                <li class="site-menu-item {{request()->routeIs('invoice', 'invoiceInfo') ? 'active open' : ''}}">
+                    <a href="{{route('invoice')}}">
+                        <i class="site-menu-icon wb-bookmark" aria-hidden="true"></i>
+                        <span class="site-menu-title">{{trans('home.invoices')}}</span>
+                    </a>
+                </li>
+                @if(\App\Models\ReferralLog::uid()->exists() || \App\Models\Order::uid()->whereStatus(2)->exists())
+                    @if(sysConfig('is_invite_register'))
+                        <li class="site-menu-item {{request()->routeIs('invite') ? 'active open' : ''}}">
+                            <a href="{{route('invite')}}">
+                                <i class="site-menu-icon wb-extension" aria-hidden="true"></i>
+                                <span class="site-menu-title">{{trans('home.invite_code')}}</span>
+                            </a>
+                        </li>
+                    @endif
+                    @if((sysConfig('referral_status')))
+                        <li class="site-menu-item {{request()->routeIs('commission') ? 'active open' : ''}}">
+                            <a href="{{route('commission')}}">
+                                <i class="site-menu-icon wb-star-outline" aria-hidden="true"></i>
+                                <span class="site-menu-title">{{trans('home.referrals')}}</span>
+                            </a>
+                        </li>
                     @endif
-                </a>
-            </li>
-            <li class="site-menu-item {{request()->routeIs('invoice', 'invoiceInfo') ? 'active open' : ''}}">
-                <a href="{{route('invoice')}}">
-                    <i class="site-menu-icon wb-bookmark" aria-hidden="true"></i>
-                    <span class="site-menu-title">{{trans('home.invoices')}}</span>
-                </a>
-            </li>
-            @if(\App\Models\ReferralLog::uid()->exists() || \App\Models\Order::uid()->whereStatus(2)->exists())
-                @if(sysConfig('is_invite_register'))
-                    <li class="site-menu-item {{request()->routeIs('invite') ? 'active open' : ''}}">
-                        <a href="{{route('invite')}}">
-                            <i class="site-menu-icon wb-extension" aria-hidden="true"></i>
-                            <span class="site-menu-title">{{trans('home.invite_code')}}</span>
-                        </a>
-                    </li>
-                @endif
-                @if((sysConfig('referral_status')))
-                    <li class="site-menu-item {{request()->routeIs('commission') ? 'active open' : ''}}">
-                        <a href="{{route('commission')}}">
-                            <i class="site-menu-icon wb-star-outline" aria-hidden="true"></i>
-                            <span class="site-menu-title">{{trans('home.referrals')}}</span>
-                        </a>
-                    </li>
                 @endif
-            @endif
-        </ul>
-    </div>
-</div>
-<div class="page">
-    <!--[if lt IE 8]>
-    <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
-        <a href="http://browsehappy.com/" target="_blank">升级您的浏览器</a> <br/>You are using an
-        <strong>outdated</strong> browser. Please
-        <a href="http://browsehappy.com/" target="_blank">upgrade your browser</a> to improve your experience.</p>
-    <![endif]-->
-    @yield('content')
-</div>
-<footer class="site-footer">
-    <div class="site-footer-legal">
-        Copyright ©️2017 - 2020 <a href="https://github.com/ProxyPanel/ProxyPanel" target="_blank">{{config('version.name')}}</a>
-        🚀 版本: {{config('version.number')}}
+            </ul>
+        </div>
     </div>
-    <div class="site-footer-right">
-        由 <a href="{{sysConfig('website_url')}}" target="_blank">{{sysConfig('website_name')}}</a> 🈺运营
+    <div class="page">
+        <!--[if lt IE 8]>
+        <p class="browserupgrade">您正在使用 <strong>过时/老旧</strong> 的浏览器。 为了您的使用体验,请
+            <a href="http://browsehappy.com/" target="_blank">升级您的浏览器</a> <br/>You are using an
+            <strong>outdated</strong> browser. Please
+            <a href="http://browsehappy.com/" target="_blank">upgrade your browser</a> to improve your experience.</p>
+        <![endif]-->
+        @yield('content')
     </div>
-</footer>
-@if(Session::get("admin"))
-    <div class="panel panel-bordered w-300 bg-grey-200" style="position:fixed;right:20px;bottom:0;">
-        <div class="panel-body text-right">
-            <h5>当前身份:{{Auth::getUser()->email}}</h5>
-            <button type="button" class="btn btn-danger btn-block mt-20" id="return_to_admin">
-                返回管理页面
-            </button>
+    <footer class="site-footer">
+        <div class="site-footer-legal">
+            Copyright ©️2017 - 2020 <a href="https://github.com/ProxyPanel/ProxyPanel" target="_blank">{{config('version.name')}}</a>
+            🚀 版本: {{config('version.number')}}
         </div>
-    </div>
-@endif
-<!-- 核心/Core -->
-<script src="/assets/global/vendor/babel-external-helpers/babel-external-helpers.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/jquery/jquery.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/popper-js/umd/popper.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/bootstrap/bootstrap.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/animsition/animsition.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/mousewheel/jquery.mousewheel.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollbar/jquery-asScrollbar.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/asscrollable/jquery-asScrollable.min.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/ashoverscroll/jquery-asHoverScroll.min.js" type="text/javascript"></script>
-
-<!-- 插件/Plugins -->
-<script src="/assets/global/vendor/screenfull/screenfull.js" type="text/javascript"></script>
-<script src="/assets/global/vendor/slidepanel/jquery-slidePanel.min.js" type="text/javascript"></script>
-<!--[if lt IE 11]>
-<script src="/assets/custom/Plugin/sweetalert2/polyfill.min.js" type="text/javascript"></script>
-<![endif]-->
-<script src="/assets/custom/Plugin/sweetalert2/sweetalert2.all.min.js" type="text/javascript"></script>
-
-<!-- 脚本/Scripts -->
-<script src="/assets/global/js/Component.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin.js" type="text/javascript"></script>
-<script src="/assets/global/js/Base.js" type="text/javascript"></script>
-<script src="/assets/global/js/Config.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Menubar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/Sidebar.js" type="text/javascript"></script>
-<script src="/assets/js/Section/PageAside.js" type="text/javascript"></script>
-<script src="/assets/js/Plugin/menu.js" type="text/javascript"></script>
-
-<!-- 设置/Config -->
-<script src="/assets/global/js/config/colors.js" type="text/javascript"></script>
-<script type="text/javascript">
-  Config.set('assets', '/assets');
-</script>
-<!-- 页面/Page -->
-<script src="/assets/js/Site.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/asscrollable.js" type="text/javascript"></script>
-<script src="/assets/global/js/Plugin/slidepanel.js" type="text/javascript"></script>
-<script src="/assets/custom/Plugin/js-cookie/js.cookie.min.js" type="text/javascript"></script>
-<script type="text/javascript">
-  (function(document, window, $) {
-    'use strict';
-
-    const Site = window.Site;
-    $(document).ready(function() {
-      Site.run();
-    });
-  })(document, window, jQuery);
-</script>
-@yield('javascript')
-
-@if(Session::get("admin"))
-    <script type="text/javascript">
-      $('#return_to_admin').click(function() {
-        $.ajax({
-          method: 'POST',
-          url: '{{route('switch')}}',
-          data: {'_token': '{{csrf_token()}}'},
-          dataType: 'json',
-          success: function(ret) {
-            swal.fire({
-              title: ret.message,
-              icon: 'success',
-              timer: 1000,
-              showConfirmButton: false,
-            }).then(() => window.location.href = '{{route('admin.index')}}');
-          },
-          error: function(ret) {
-            swal.fire({
-              title: ret.message,
-              icon: 'error',
-              timer: 1500,
-              showConfirmButton: false,
-            });
-          },
-        });
-      });
-    </script>
-@endif
-<!-- 统计 -->
-{!! sysConfig('website_analytics') !!}
-<!-- 客服 -->
-{!! sysConfig('website_customer_service') !!}
-</body>
-</html>
+        <div class="site-footer-right">
+            由 <a href="{{sysConfig('website_url')}}" target="_blank">{{sysConfig('website_name')}}</a> 🈺运营
+        </div>
+    </footer>
+    @if(Session::get("admin"))
+        <div class="panel panel-bordered w-300 bg-grey-200" style="position:fixed;right:20px;bottom:0;">
+            <div class="panel-body text-right">
+                <h5>当前身份:{{Auth::getUser()->email}}</h5>
+                <button type="button" class="btn btn-danger btn-block mt-20" id="return_to_admin">
+                    返回管理页面
+                </button>
+            </div>
+        </div>
+        @endif
+@endsection
+@section('layout_javascript')
+    <!--[if lt IE 11]>
+        <script src="/assets/custom/Plugin/sweetalert2/polyfill.min.js" type="text/javascript"></script>
+        <![endif]-->
+        <script src="/assets/custom/Plugin/sweetalert2/sweetalert2.all.min.js" type="text/javascript"></script>
+        <script src="/assets/custom/Plugin/js-cookie/js.cookie.min.js" type="text/javascript"></script>
+        @yield('javascript')
+        @if(Session::get("admin"))
+            <script type="text/javascript">
+              $('#return_to_admin').click(function() {
+                $.ajax({
+                  method: 'POST',
+                  url: '{{route('switch')}}',
+                  data: {'_token': '{{csrf_token()}}'},
+                  dataType: 'json',
+                  success: function(ret) {
+                    swal.fire({
+                      title: ret.message,
+                      icon: 'success',
+                      timer: 1000,
+                      showConfirmButton: false,
+                    }).then(() => window.location.href = '{{route('admin.index')}}');
+                  },
+                  error: function(ret) {
+                    swal.fire({
+                      title: ret.message,
+                      icon: 'error',
+                      timer: 1500,
+                      showConfirmButton: false,
+                    });
+                  },
+                });
+              });
+            </script>
+        @endif
+        <!-- 统计 -->
+        {!! sysConfig('website_analytics') !!}
+        <!-- 客服 -->
+        {!! sysConfig('website_customer_service') !!}
+@endsection

+ 119 - 0
routes/admin.php

@@ -0,0 +1,119 @@
+<?php
+
+Route::prefix('admin')->name('admin.')->group(function () {
+    Route::get('/', 'AdminController@index')->name('index'); // 后台首页
+    Route::get('config', 'AdminController@config')->name('config'); // 系统设置
+    Route::get('invite', 'AdminController@inviteList')->name('invite.index'); // 邀请码列表
+    Route::post('invite', 'AdminController@makeInvite')->name('invite.create'); // 生成邀请码
+    Route::get('Invite/export', 'AdminController@exportInvite')->name('invite.export'); // 导出邀请码
+    Route::get('epayInfo', 'Gateway\EPay@queryInfo')->name('test.epay'); // 易支付信息
+
+    Route::namespace('Admin')->group(function () {
+        Route::resource('user', 'UserController')->except('show');
+        Route::name('user.')->group(function () {
+            Route::post('batchAdd', 'UserController@batchAddUsers')->name('batch'); // 批量生成账号
+            Route::resource('group', 'UserGroupController')->except('show'); // 用户分组管理
+            Route::get('monitor/{id}', 'LogsController@userTrafficMonitor')->name('monitor'); // 用户流量监控
+            Route::get('online/{id}', 'LogsController@onlineIPMonitor')->name('online'); // 在线IP监控
+            Route::post('switch', 'UserController@switchToUser')->name('switch'); // 转换成某个用户的身份
+            Route::post('updateCredit', 'UserController@handleUserCredit')->name('updateCredit'); // 用户余额充值
+            Route::post('reset', 'UserController@resetTraffic')->name('reset'); // 重置用户流量
+            Route::get('export/{user}', 'UserController@export')->name('export'); // 查看配置信息
+            Route::post('export/{user}', 'UserController@exportProxyConfig')->name('exportProxy'); // 读取配置信息
+        });
+
+        Route::prefix('subscribe')->name('subscribe.')->group(function () {
+            Route::get('/', 'SubscribeController@index')->name('index'); // 订阅码列表
+            Route::get('log/{id}', 'SubscribeController@subscribeLog')->name('log'); // 订阅码记录
+            Route::post('set/{id}', 'SubscribeController@setSubscribeStatus')->name('set'); // 启用禁用用户的订阅
+        });
+
+        Route::resource('ticket', 'TicketController')->except('create', 'show');
+        Route::resource('article', 'ArticleController');
+        Route::prefix('marketing')->name('marketing.')->group(function () {
+            Route::get('email', 'MarketingController@emailList')->name('email'); // 邮件消息列表
+            Route::get('push', 'MarketingController@pushList')->name('push'); // 推送消息列表
+            Route::post('add', 'MarketingController@addPushMarketing')->name('add'); // 推送消息
+        });
+
+        Route::resource('node', 'NodeController')->except('show');
+        Route::prefix('node')->name('node.')->group(function () {
+            Route::get('monitor/{id}', 'NodeController@nodeMonitor')->name('monitor'); // 节点流量监控
+            Route::post('check/{id}', 'NodeController@checkNode')->name('check'); // 节点阻断检测
+            Route::post('ping/{id}', 'NodeController@pingNode')->name('ping'); // 节点ping测速
+            Route::get('pingLog', 'NodeController@pingLog')->name('pingLog'); // 节点Ping测速日志
+            Route::get('refreshGeo/{id}', 'NodeController@refreshGeo')->name('geo'); // 更新节点
+            Route::post('reload/{id}', 'NodeController@reload')->name('reload'); // 更新节点
+
+            Route::prefix('auth')->name('auth.')->group(function () {
+                Route::get('/', 'NodeController@authList')->name('index');
+                Route::post('/', 'NodeController@addAuth')->name('store');
+                Route::delete('{id}', 'NodeController@delAuth')->name('destroy');
+                Route::put('{id}', 'NodeController@refreshAuth')->name('update');
+            }); // 节点Api授权相关
+
+            Route::resource('cert', 'CertController')->except('show'); // 节点域名tls相关
+        });
+
+        Route::resource('rule', 'RuleController')->except('create', 'edit', 'show'); // 节点审计规则管理
+        Route::name('rule.')->prefix('rule')->group(function () {
+            Route::resource('group', 'RuleGroupController')->except('show');
+            Route::name('group.')->prefix('group')->group(function () {
+                Route::get('{id}/assign', 'RuleGroupController@assignNode')->name('editNode');
+                Route::put('{id}/assign', 'RuleGroupController@assign')->name('assign'); // 规则分组关联节点
+            });
+            Route::get('log', 'RuleController@ruleLogList')->name('log'); // 用户触发审计规则日志
+            Route::post('clear', 'RuleController@clearLog')->name('clear'); // 清除所有审计触发日志
+        });
+
+        Route::resource('goods', 'ShopController')->except('show'); // 商品管理
+        Route::resource('coupon', 'CouponController')->except('show', 'edit', 'update'); // 优惠券
+        Route::get('coupon/export', 'CouponController@exportCoupon')->name('coupon.export'); // 导出优惠券
+
+        Route::prefix('aff')->name('aff.')->group(function () {
+            Route::get('/', 'AffiliateController@index')->name('index'); // 提现申请列表
+            Route::get('detail/{id}', 'AffiliateController@detail')->name('detail'); // 提现申请详情
+            Route::post('set', 'AffiliateController@setStatus')->name('setStatus'); // 设置提现申请状态
+            Route::get('rebate', 'AffiliateController@rebate')->name('rebate'); // 返利流水记录
+        });
+
+        Route::get('order', 'LogsController@orderList')->name('order'); // 订单列表
+        Route::prefix('log')->name('log.')->group(function () {
+            Route::get('traffic', 'LogsController@trafficLog')->name('traffic'); // 流量日志
+            Route::get('userCredit', 'LogsController@userCreditLogList')->name('credit'); // 余额变动记录
+            Route::get('userTraffic', 'LogsController@userTrafficLogList')->name('flow'); // 流量变动记录
+            Route::get('userBan', 'LogsController@userBanLogList')->name('ban'); // 用户封禁记录
+            Route::get('userOnline', 'LogsController@userOnlineIPList')->name('ip'); // 用户在线IP记录
+            Route::get('onlineIPMonitor', 'LogsController@onlineIPMonitor')->name('online'); // 在线IP监控
+            Route::get('notification', 'LogsController@notificationLog')->name('notify'); // 邮件发送日志
+        });
+        Route::get('payment/callbackList', 'LogsController@callbackList')->name('payment.callback'); // 支付回调日志
+
+        // 工具相关
+        Route::prefix('tools')->name('tools.')->group(function () {
+            Route::match(['get', 'post'], 'decompile', 'ToolsController@decompile')->name('decompile'); // SS(R)链接反解析
+            Route::get('download', 'ToolsController@download')->name('download'); // 下载转换过的JSON配置
+            Route::match(['get', 'post'], 'convert', 'ToolsController@convert')->name('convert'); // 格式转换
+            Route::match(['get', 'post'], 'import', 'ToolsController@import')->name('import'); // 数据导入
+            Route::get('analysis', 'ToolsController@analysis')->name('analysis'); // 日志分析
+        });
+
+        Route::prefix('config')->name('config.')->namespace('Config')->group(function () {
+            Route::resource('country', 'CountryController')->only('store', 'update', 'destroy'); // 等级配置
+            Route::resource('filter', 'EmailFilterController')->only('index', 'store', 'destroy'); // 邮箱过滤
+            Route::resource('label', 'LabelController')->only('store', 'update', 'destroy'); // 标签配置
+            Route::resource('level', 'LevelController')->only('store', 'update', 'destroy'); // 等级配置
+            Route::resource('ss', 'SsConfigController')->only('store', 'update', 'destroy'); // ss配置
+        });
+
+        Route::resource('permission', 'PermissionController')->except('show');
+        Route::resource('role', 'RoleController')->except('show');
+
+        Route::get('system', 'SystemController@index')->name('system.index'); // 系统设置
+        Route::post('setExtend', 'SystemController@setExtend')->name('system.extend'); // 设置客服、统计代码
+        Route::post('setConfig', 'SystemController@setConfig')->name('system.update'); // 设置某个配置项
+        Route::post('sendTestNotification', 'SystemController@sendTestNotification')->name('test.notify'); //推送通知测试
+    });
+
+    Route::get('logs', '\Rap2hpoutre\LaravelLogViewer\LogViewerController@index')->name('log.viewer'); // 系统运行日志
+});

+ 38 - 0
routes/user.php

@@ -0,0 +1,38 @@
+<?php
+
+Route::get('/', 'UserController@index')->name('home'); // 用户首页
+Route::get('article', 'UserController@article')->name('article'); // 文章详情
+Route::post('exchangeSubscribe', 'UserController@exchangeSubscribe')->name('changeSub'); // 更换节点订阅地址
+Route::match(['get', 'post'], 'nodeList', 'UserController@nodeList')->name('node'); // 节点列表
+Route::post('checkIn', 'UserController@checkIn')->name('checkIn'); // 签到
+Route::get('services', 'UserController@services')->name('shop'); // 商品列表
+Route::get('tickets', 'UserController@ticketList')->name('ticket'); // 工单
+Route::post('createTicket', 'UserController@createTicket')->name('openTicket'); // 快速添加工单
+Route::match(['get', 'post'], 'replyTicket', 'UserController@replyTicket')->name('replyTicket'); // 回复工单
+Route::post('closeTicket', 'UserController@closeTicket')->name('closeTicket'); // 关闭工单
+Route::get('invoices', 'UserController@invoices')->name('invoice'); // 订单列表
+Route::post('closePlan', 'UserController@closePlan')->name('cancelPlan'); // 激活预支付套餐
+Route::get('invoice/{sn}', 'UserController@invoiceDetail')->name('invoiceInfo'); // 订单明细
+Route::post('resetUserTraffic', 'UserController@resetUserTraffic')->name('resetTraffic'); // 重置用户流量
+Route::get('buy/{id}', 'UserController@buy')->name('buy'); // 购买商品
+Route::post('redeemCoupon', 'UserController@redeemCoupon')->name('redeemCoupon'); // 使用优惠券
+Route::get('invite', 'UserController@invite')->name('invite'); // 邀请码
+Route::post('makeInvite', 'UserController@makeInvite')->name('createInvite'); // 生成邀请码
+Route::match(['get', 'post'], 'profile', 'UserController@profile')->name('profile'); // 修改个人信息
+Route::post('switchToAdmin', 'UserController@switchToAdmin')->name('switch'); // 转换成管理员的身份
+Route::post('charge', 'UserController@charge')->name('recharge'); // 卡券余额充值
+Route::get('help', 'UserController@help')->name('help'); // 帮助中心
+
+Route::namespace('User')->group(function () {
+    Route::get('referral', 'AffiliateController@referral')->name('commission'); // 推广返利
+    Route::post('extractMoney', 'AffiliateController@extractMoney')->name('applyCommission'); // 申请提现
+});
+
+Route::prefix('payment')->group(function () {
+    Route::post('purchase', 'PaymentController@purchase')->name('purchase'); // 创建支付
+    Route::post('close', 'PaymentController@close')->name('closeOrder'); // 关闭支付单
+    Route::get('getStatus', 'PaymentController@getStatus')->name('orderStatus'); // 获取支付单状态
+    Route::get('{trade_no}', 'PaymentController@detail')->name('orderDetail'); // 支付单详情
+});
+
+Route::get('/stripe-checkout/{session_id}', 'Gateway\Stripe@redirectPage')->name('stripe-checkout'); // Stripe Checkout page

+ 1 - 158
routes/web.php

@@ -24,163 +24,6 @@ Route::middleware(['isForbidden', 'affiliate', 'isMaintenance'])->group(function
     Route::get('free', 'AuthController@free')->name('freeInvitationCode'); // 免费邀请码
     Route::get('create/string', '\Illuminate\Support\Str@random')->name('createStr'); // 生成随机密码
     Route::get('create/uuid', '\Illuminate\Support\Str@uuid')->name('createUUID'); // 生成UUID
+    Route::get('getPort', '\App\Components\Helpers@getPort')->name('getPort'); // 获取端口
 });
 Route::match(['get', 'post'], 'admin/login', 'AuthController@login')->name('admin.login')->middleware('isForbidden', 'isSecurity'); // 管理登录
-
-// 用户相关
-Route::middleware(['isForbidden', 'isMaintenance', 'isLogin'])->group(function () {
-    Route::get('/', 'UserController@index')->name('home'); // 用户首页
-    Route::get('article', 'UserController@article')->name('article'); // 文章详情
-    Route::post('exchangeSubscribe', 'UserController@exchangeSubscribe')->name('changeSub'); // 更换节点订阅地址
-    Route::match(['get', 'post'], 'nodeList', 'UserController@nodeList')->name('node'); // 节点列表
-    Route::post('checkIn', 'UserController@checkIn')->name('checkIn'); // 签到
-    Route::get('services', 'UserController@services')->name('shop'); // 商品列表
-    Route::get('tickets', 'UserController@ticketList')->name('ticket'); // 工单
-    Route::post('createTicket', 'UserController@createTicket')->name('openTicket'); // 快速添加工单
-    Route::match(['get', 'post'], 'replyTicket', 'UserController@replyTicket')->name('replyTicket'); // 回复工单
-    Route::post('closeTicket', 'UserController@closeTicket')->name('closeTicket'); // 关闭工单
-    Route::get('invoices', 'UserController@invoices')->name('invoice'); // 订单列表
-    Route::post('closePlan', 'UserController@closePlan')->name('cancelPlan'); // 激活预支付套餐
-    Route::get('invoice/{sn}', 'UserController@invoiceDetail')->name('invoiceInfo'); // 订单明细
-    Route::post('resetUserTraffic', 'UserController@resetUserTraffic')->name('resetTraffic'); // 重置用户流量
-    Route::get('buy/{id}', 'UserController@buy')->name('buy'); // 购买商品
-    Route::post('redeemCoupon', 'UserController@redeemCoupon')->name('redeemCoupon'); // 使用优惠券
-    Route::get('invite', 'UserController@invite')->name('invite'); // 邀请码
-    Route::post('makeInvite', 'UserController@makeInvite')->name('createInvite'); // 生成邀请码
-    Route::match(['get', 'post'], 'profile', 'UserController@profile')->name('profile'); // 修改个人信息
-    Route::post('switchToAdmin', 'UserController@switchToAdmin')->name('switch'); // 转换成管理员的身份
-    Route::post('charge', 'UserController@charge')->name('recharge'); // 卡券余额充值
-    Route::get('help', 'UserController@help')->name('help'); // 帮助中心
-
-    Route::namespace('User')->group(function () {
-        Route::get('referral', 'AffiliateController@referral')->name('commission'); // 推广返利
-        Route::post('extractMoney', 'AffiliateController@extractMoney')->name('applyCommission'); // 申请提现
-    });
-
-    Route::prefix('payment')->group(function () {
-        Route::post('purchase', 'PaymentController@purchase')->name('purchase'); // 创建支付
-        Route::post('close', 'PaymentController@close')->name('closeOrder'); // 关闭支付单
-        Route::get('getStatus', 'PaymentController@getStatus')->name('orderStatus'); // 获取支付单状态
-        Route::get('{trade_no}', 'PaymentController@detail')->name('orderDetail'); // 支付单详情
-    });
-
-    Route::get('/stripe-checkout/{session_id}', 'Gateway\Stripe@redirectPage')->name('stripe-checkout'); // Stripe Checkout page
-});
-
-// 管理相关
-Route::middleware(['isForbidden', 'isAdminLogin', 'isAdmin'])->prefix('admin')->name('admin.')->group(function () {
-    Route::get('/', 'AdminController@index')->name('index'); // 后台首页
-    Route::match(['get', 'post'], 'profile', 'AdminController@profile')->name('profile'); // 修改个人信息
-    Route::get('config', 'AdminController@config')->name('config'); // 系统设置
-    Route::get('invite', 'AdminController@inviteList')->name('invite'); // 邀请码列表
-    Route::post('invite', 'AdminController@makeInvite')->name('invite.create'); // 生成邀请码
-    Route::get('Invite/export', 'AdminController@exportInvite')->name('invite.export'); // 导出邀请码
-    Route::get('getPort', 'AdminController@getPort')->name('getPort'); // 生成端口
-    Route::get('epayInfo', 'Gateway\EPay@queryInfo')->name('test.epay'); // 易支付信息
-
-    Route::namespace('Admin')->group(function () {
-        Route::resource('user', 'UserController')->except('show');
-        Route::name('user.')->group(function () {
-            Route::post('batchAdd', 'UserController@batchAddUsers')->name('batch'); // 批量生成账号
-            Route::resource('group', 'UserGroupController')->except('show'); // 用户分组管理
-            Route::get('monitor/{id}', 'LogsController@userTrafficMonitor')->name('monitor'); // 用户流量监控
-            Route::get('online/{id}', 'LogsController@onlineIPMonitor')->name('online'); // 在线IP监控
-            Route::post('switch', 'UserController@switchToUser')->name('switch'); // 转换成某个用户的身份
-            Route::post('updateCredit', 'UserController@handleUserCredit')->name('updateCredit'); // 用户余额充值
-            Route::post('reset', 'UserController@resetTraffic')->name('reset'); // 重置用户流量
-            Route::get('export/{id}', 'UserController@export')->name('export'); // 导出(查看)配置信息
-            Route::post('export/{id}', 'UserController@exportProxyConfig')->name('exportProxy'); // 导出(查看)配置信息
-        });
-
-        Route::prefix('subscribe')->name('subscribe.')->group(function () {
-            Route::get('/', 'SubscribeController@index')->name('index'); // 订阅码列表
-            Route::get('log/{id}', 'SubscribeController@subscribeLog')->name('log'); // 订阅码记录
-            Route::post('set/{id}', 'SubscribeController@setSubscribeStatus')->name('set'); // 启用禁用用户的订阅
-        });
-
-        Route::resource('ticket', 'TicketController')->except('create', 'show');
-        Route::resource('article', 'ArticleController');
-        Route::prefix('marketing')->name('marketing.')->group(function () {
-            Route::get('email', 'MarketingController@emailList')->name('email'); // 邮件消息列表
-            Route::get('push', 'MarketingController@pushList')->name('push'); // 推送消息列表
-            Route::post('add', 'MarketingController@addPushMarketing')->name('add'); // 推送消息
-        });
-
-        Route::resource('node', 'NodeController')->except('show');
-        Route::prefix('node')->name('node.')->group(function () {
-            Route::get('monitor/{id}', 'NodeController@nodeMonitor')->name('monitor'); // 节点流量监控
-            Route::post('check/{id}', 'NodeController@checkNode')->name('check'); // 节点阻断检测
-            Route::post('ping/{id}', 'NodeController@pingNode')->name('ping'); // 节点ping测速
-            Route::get('pingLog', 'NodeController@pingLog')->name('pingLog'); // 节点Ping测速日志
-            Route::get('refreshGeo/{id}', 'NodeController@refreshGeo')->name('geo'); // 更新节点
-            Route::post('reload/{id}', 'NodeController@reload')->name('reload'); // 更新节点
-
-            Route::prefix('auth')->name('auth.')->group(function () {
-                Route::get('/', 'NodeController@authList')->name('index');
-                Route::post('/', 'NodeController@addAuth')->name('store');
-                Route::delete('{id}', 'NodeController@delAuth')->name('destroy');
-                Route::put('{id}', 'NodeController@refreshAuth')->name('update');
-            }); // 节点Api授权相关
-
-            Route::resource('cert', 'CertController')->except('show'); // 节点域名tls相关
-        });
-
-        Route::resource('rule', 'RuleController')->except('create', 'edit', 'show'); // 节点审计规则管理
-        Route::name('rule.')->prefix('rule')->group(function () {
-            Route::resource('group', 'RuleGroupController')->except('show');
-            Route::name('group.')->prefix('group')->group(function () {
-                Route::get('{id}/assign', 'RuleGroupController@assignNode')->name('editNode');
-                Route::put('{id}/assign', 'RuleGroupController@assign')->name('assign'); // 规则分组关联节点
-            });
-            Route::get('log', 'RuleController@ruleLogList')->name('log'); // 用户触发审计规则日志
-            Route::post('clear', 'RuleController@clearLog')->name('clear'); // 清除所有审计触发日志
-        });
-
-        Route::resource('goods', 'ShopController')->except('show'); // 商品管理
-        Route::resource('coupon', 'CouponController')->except('show', 'edit', 'update'); // 优惠券
-        Route::get('coupon/export', 'CouponController@exportCoupon')->name('coupon.export'); // 导出优惠券
-
-        Route::prefix('aff')->name('aff.')->group(function () {
-            Route::get('/', 'AffiliateController@index')->name('index'); // 提现申请列表
-            Route::get('detail/{id}', 'AffiliateController@detail')->name('detail'); // 提现申请详情
-            Route::post('set', 'AffiliateController@setStatus')->name('setStatus'); // 设置提现申请状态
-            Route::get('rebate', 'AffiliateController@rebate')->name('rebate'); // 返利流水记录
-        });
-
-        Route::get('order', 'LogsController@orderList')->name('order'); // 订单列表
-        Route::prefix('log')->name('log.')->group(function () {
-            Route::get('traffic', 'LogsController@trafficLog')->name('traffic'); // 流量日志
-            Route::get('userCredit', 'LogsController@userCreditLogList')->name('credit'); // 余额变动记录
-            Route::get('userTraffic', 'LogsController@userTrafficLogList')->name('flow'); // 流量变动记录
-            Route::get('userBan', 'LogsController@userBanLogList')->name('ban'); // 用户封禁记录
-            Route::get('userOnline', 'LogsController@userOnlineIPList')->name('ip'); // 用户在线IP记录
-            Route::get('onlineIPMonitor', 'LogsController@onlineIPMonitor')->name('online'); // 在线IP监控
-            Route::get('notification', 'LogsController@notificationLog')->name('notify'); // 邮件发送日志
-        });
-        Route::get('payment/callbackList', 'LogsController@callbackList')->name('payment.callback'); // 支付回调日志
-
-        // 工具相关
-        Route::prefix('tools')->name('tools.')->group(function () {
-            Route::match(['get', 'post'], 'decompile', 'ToolsController@decompile')->name('decompile'); // SS(R)链接反解析
-            Route::get('download', 'ToolsController@download')->name('download'); // 下载转换过的JSON配置
-            Route::match(['get', 'post'], 'convert', 'ToolsController@convert')->name('convert'); // 格式转换
-            Route::match(['get', 'post'], 'import', 'ToolsController@import')->name('import'); // 数据导入
-            Route::get('analysis', 'ToolsController@analysis')->name('analysis'); // 日志分析
-        });
-
-        Route::prefix('config')->name('config.')->namespace('Config')->group(function () {
-            Route::resource('country', 'CountryController')->only('store', 'update', 'destroy'); // 等级配置
-            Route::resource('filter', 'EmailFilterController')->only('index', 'store', 'destroy'); // 邮箱过滤
-            Route::resource('label', 'LabelController')->only('store', 'update', 'destroy'); // 标签配置
-            Route::resource('level', 'LevelController')->only('store', 'update', 'destroy'); // 等级配置
-            Route::resource('ss', 'SsConfigController')->only('store', 'update', 'destroy'); // ss配置
-        });
-
-        Route::get('system', 'SystemController@index')->name('system'); // 系统设置
-        Route::post('setExtend', 'SystemController@setExtend')->name('system.extend'); // 设置客服、统计代码
-        Route::post('setConfig', 'SystemController@setConfig')->name('system.update'); // 设置某个配置项
-        Route::post('sendTestNotification', 'SystemController@sendTestNotification')->name('test.notify'); //推送通知测试
-    });
-
-    Route::get('logs', '\Rap2hpoutre\LaravelLogViewer\LogViewerController@index')->name('log.viewer'); // 系统运行日志
-});