first commit

This commit is contained in:
yanwenwu
2019-12-22 09:37:52 +08:00
parent cf1dedabd4
commit b27ef2570a
359 changed files with 34726 additions and 758 deletions

120
catch/CatchAdminService.php Normal file
View File

@@ -0,0 +1,120 @@
<?php
namespace catchAdmin;
use catchAdmin\login\LoginLogListener;
use catchAdmin\permissions\OperateLogListener;
use catchAdmin\permissions\PermissionsMiddleware;
use catchAdmin\system\event\LoginLogEvent;
use catchAdmin\system\event\OperateLogEvent;
use catchAdmin\user\Auth;
use catcher\command\BackupCommand;
use catcher\command\CompressPackageCommand;
use catcher\command\CreateModuleCommand;
use catcher\command\InstallCommand;
use catcher\command\MigrateRunCommand;
use catcher\command\ModelGeneratorCommand;
use catcher\command\ModuleCacheCommand;
use catcher\command\SeedRunCommand;
use catcher\event\LoadModuleRoutes;
use catcher\validates\Sometimes;
use think\facade\Validate;
use think\Service;
class CatchAdminService extends Service
{
/**
*
* @time 2019年11月29日
* @return void
*/
public function boot()
{
$this->registerCommands();
$this->registerValidates();
$this->registerMiddleWares();
$this->registerEvents();
$this->registerListeners();
}
/**
*
* @time 2019年12月13日
* @return void
*/
protected function registerCommands(): void
{
$this->commands([
InstallCommand::class,
ModuleCacheCommand::class,
MigrateRunCommand::class,
ModelGeneratorCommand::class,
SeedRunCommand::class,
BackupCommand::class,
CompressPackageCommand::class,
CreateModuleCommand::class,
]);
}
/**
*
* @time 2019年12月07日
* @return void
*/
protected function registerValidates(): void
{
$validates = [
new Sometimes(),
];
Validate::maker(function($validate) use ($validates){
foreach ($validates as $vali) {
$validate->extend($vali->type(), [$vali, 'verify'], $vali->message());
}
});
}
/**
*
* @time 2019年12月12日
* @return void
*/
protected function registerMiddleWares(): void
{
$this->app->middleware->import([
'catch_check_permission' => PermissionsMiddleware::class,
], 'route');
}
/**
*
* @time 2019年12月12日
* @return void
*/
protected function registerEvents(): void
{
$this->app->event->bind([
'loginLog' => LoginLogEvent::class,
'operateLog' => OperateLogEvent::class,
]);
}
/**
* 注册监听者
*
* @time 2019年12月12日
* @return void
*/
protected function registerListeners(): void
{
$this->app->event->listenEvents([
'loginLog' => [
LoginLogListener::class,
],
'operateLog' => [
OperateLogListener::class,
],
'RouteLoaded' => [
LoadModuleRoutes::class
]
]);
}
}

23
catch/helper.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
function editButton($name = '修改', $event = 'edit')
{
return sprintf(
'<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="%s">%s</a>',
$event, $name);
}
function deleteButton($name = '删除', $event = 'del')
{
return sprintf(
'<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="%s">%s</a>',
$event, $name);
}
function addButton($name = '新增', $event = 'add')
{
return sprintf(
'<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="%s">%s</a>',
$event, $name);
}

View File

@@ -0,0 +1,59 @@
<?php
namespace catchAdmin\index\controller;
use catchAdmin\permissions\model\Permissions;
use catchAdmin\user\Auth;
use catcher\base\CatchController;
use catcher\Tree;
use think\facade\Db;
class Index extends CatchController
{
/**
*
* @time 2019年12月11日
* @return string
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\db\exception\DataNotFoundException
*/
public function index(): string
{
$permissionIds = Auth::user()->getPermissionsBy();
$menus = Permissions::whereIn('id', $permissionIds)
->where('type', Permissions::MENU_TYPE)
->field(['id', 'parent_id', 'permission_name', 'route'])
->select()->toArray();
return $this->fetch([
'menus' => Tree::done($menus),
'username' => Auth::user()->username,
]);
}
/**
*
* @time 2019年12月11日
* @throws \Exception
* @return string
*/
public function theme(): string
{
return $this->fetch();
}
/**
*
* @time 2019年12月12日
* @throws \Exception
* @return string
*/
public function dashboard(): string
{
$mysqlVersion = Db::query('select version() as version');
return $this->fetch([
'mysql_version' => $mysqlVersion['0']['version'],
]);
}
}

13
catch/index/module.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "首页管理",
"alias": "index",
"description": "",
"keywords": [],
"order": 0,
"services": [
"catchAdmin\\index\\IndexService"
],
"aliases": {},
"files": [],
"requires": []
}

5
catch/index/route.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
$router->get('/', '\catchAdmin\index\controller\Index@index');
$router->get('theme', '\catchAdmin\index\controller\Index@theme');
$router->get('dashboard', '\catchAdmin\index\controller\Index@dashboard');

View File

@@ -0,0 +1,221 @@
<!DOCTYPE html>
<html>
<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, maximum-scale=1">
<title>CatchAdmin 后台开发框架</title>
<link rel="stylesheet" href="__CATCH_ADMIN_LIBS__/layui/css/layui.css"/>
<link rel="stylesheet" href="__CATCH_ADMIN_MODULE__/admin.css?v=315"/>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<style>
/** 卡片轮播图样式 */
.admin-carousel .layui-carousel-ind {
position: absolute;
top: -41px;
text-align: right;
}
.admin-carousel .layui-carousel-ind ul {
background: 0 0;
}
.admin-carousel .layui-carousel-ind li {
background-color: #e2e2e2;
}
.admin-carousel .layui-carousel-ind li.layui-this {
background-color: #999;
}
/** 广告位轮播图 */
.admin-news .layui-carousel-ind {
height: 45px;
}
.admin-news a {
display: block;
line-height: 60px;
text-align: center;
}
</style>
</head>
<body>
<!-- 加载动画 -->
<div class="page-loading">
<div class="ball-loader">
<span></span><span></span><span></span><span></span>
</div>
</div>
<!-- 正文开始 -->
<div class="layui-fluid">
<div class="layui-row layui-col-space15">
<div class="layui-col-xs12 layui-col-sm6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
访问量<span class="layui-badge layui-bg-blue pull-right"></span>
</div>
<div class="layui-card-body">
<p class="lay-big-font">99,666</p>
<p>总计访问量<span class="pull-right">88万 <i class="layui-icon layui-icon-flag"></i></span></p>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-sm6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
下载<span class="layui-badge layui-bg-black pull-right"></span>
</div>
<div class="layui-card-body">
<p class="lay-big-font">33,555</p>
<p>新下载<span class="pull-right">10% <i class="layui-icon">&#xe601</i></span></p>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-sm6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
Start<span class="layui-badge layui-bg-green pull-right"></span>
</div>
<div class="layui-card-body">
<p class="lay-big-font">99,666</p>
<p>总Start数<span class="pull-right">88万 <i class="layui-icon layui-icon-rate"></i></span></p>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-sm6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
活跃用户<span class="layui-badge layui-bg-orange pull-right"></span>
</div>
<div class="layui-card-body">
<p class="lay-big-font">66,666</p>
<p>最近一个月<span class="pull-right">15% <i class="layui-icon layui-icon-user"></i></span></p>
</div>
</div>
</div>
</div>
<div class="layui-row layui-col-space15">
<div class="layui-col-lg8 layui-col-md7">
<div class="layui-card">
<div class="layui-card-header">更新日志</div>
<div class="layui-card-body">
<ul class="layui-timeline">
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis">&#xe63f;</i>
<div class="layui-timeline-content layui-text">
<h3 class="layui-timeline-title">
V1.0
<small>catchAdmin 后台框架发布</small>&emsp;
<span class="layui-badge-rim">2019-12-15</span>
</h3>
<ul>
<li>基于 Thinkphp6 & layui 开发</li>
<li>完整的权限管理</li>
<li>模块化开发方式</li>
<li>...</li>
</ul>
</div>
</li>
<li class="layui-timeline-item">
<i class="layui-icon layui-timeline-axis">&#xe63f;</i>
<div class="layui-timeline-content layui-text">
<div class="layui-timeline-title">
CatchAdmin 开发中...&emsp;<span class="layui-badge-rim">更早</span>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="layui-col-lg4 layui-col-md5">
<div class="layui-card">
<div class="layui-card-header">后台框架</div>
<div class="layui-card-body">
<table class="layui-table layui-text">
<colgroup>
<col width="100">
<col>
</colgroup>
<tbody>
<tr>
<td>PHP 版本</td>
<td>{$Think.PHP_VERSION}</td>
</tr>
<tr>
<td>MYSQL 版本</td>
<td>{$mysql_version}</td>
</tr>
<tr>
<td>WEB 服务器</td>
<td>{$_SERVER['SERVER_SOFTWARE']}</td>
</tr>
<tr>
<td>操作系统</td>
<td>{$Think.PHP_OS}</td>
</tr>
<tr>
<td>opcache (建议开启)</td>
{if condition="function_exists('opcache_get_configuration')"}
<td>{:opcache_get_configuration()['directives']['opcache.enable'] ? '开启' : '关闭' }</td>
{else/}
<td>未开启</td>
{/if}
</tr>
<tr>
<td>最大执行时间</td>
<td>{:get_cfg_var("max_execution_time")} s</td>
</tr>
<tr>
<td>上传限制大小(M)</td>
<td>{:get_cfg_var ("upload_max_filesize")}</td>
</tr>
<tr>
<td>当前时间</td>
<td>{:date("Y-m-d H:i:s")}</td>
</tr>
<tr>
<td>核心框架</td>
<td>Thinkphp v6</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- js部分 -->
<script type="text/javascript" src="__CATCH_ADMIN_LIBS__/layui/layui.js"></script>
<script type="text/javascript" src="__CATCH_ADMIN_JS__/common.js?v=315"></script>
<script>
layui.use(['layer', 'carousel'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var carousel = layui.carousel;
var device = layui.device;
// 渲染轮播
carousel.render({
elem: '.layui-carousel',
width: '100%',
height: '60px',
arrow: 'none',
autoplay: true,
trigger: device.ios || device.android ? 'click' : 'hover',
anim: 'fade'
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<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, maximum-scale=1">
<title>403</title>
<link rel="stylesheet" href="__CATCH_ADMIN_LIBS__/layui/css/layui.css"/>
<link rel="stylesheet" href="__CATCH_ADMIN_MODULE__/admin.css?v=315"/>
<link rel="stylesheet" href="__CATCH_ADMIN_CSS__/error-page.css"/>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- 正文开始 -->
<div class="error-page">
<img class="error-page-img" src="__CATCH_ADMIN_IMAGES__/ic_403.png">
<div class="error-page-info">
<h1>403</h1>
<div class="error-page-info-desc">{$msg}</div>
<div>
{if ($code == 10006)}
<a href="{:url('login')}" class="layui-btn">返回登录</a>
{else/}
<button onclick="history(-1)" class="layui-btn">返回上一页</button>
{/if}
</div>
</div>
</div>
<!-- js部分 -->
<script type="text/javascript" src="__CATCH_ADMIN_LIBS__/layui/layui.js"></script>
<script type="text/javascript" src="__CATCH_ADMIN_JS__/common.js?v=315"></script>
</body>
</html>

142
catch/index/view/index.html Normal file
View File

@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html>
<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, maximum-scale=1">
<link href="__CATCH_ADMIN_IMAGES__/favicon.ico" rel="icon">
<title>CatchAdmin 后台管理系统</title>
<link rel="stylesheet" href="__CATCH_ADMIN_LIBS__/layui/css/layui.css"/>
<link rel="stylesheet" href="__CATCH_ADMIN_MODULE__/admin.css?v=315"/>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
<!-- 头部 -->
<div class="layui-header">
<div class="layui-logo">
<img src="__CATCH_ADMIN_IMAGES__/logo.png"/>
<cite>CatchAdmin</cite>
</div>
<ul class="layui-nav layui-layout-left">
<li class="layui-nav-item" lay-unselect>
<a ew-event="flexible" title="侧边伸缩"><i class="layui-icon layui-icon-shrink-right"></i></a>
</li>
<li class="layui-nav-item" lay-unselect>
<a ew-event="refresh" title="刷新"><i class="layui-icon layui-icon-refresh-3"></i></a>
</li>
</ul>
<ul class="layui-nav layui-layout-right">
<!-- <li class="layui-nav-item" lay-unselect>
<a ew-event="message" title="消息">
<i class="layui-icon layui-icon-notice"></i>
<span class="layui-badge-dot"></span>
</a>
</li>
<li class="layui-nav-item" lay-unselect>
<a ew-event="note" title="便签"><i class="layui-icon layui-icon-note"></i></a>
</li>-->
<li class="layui-nav-item layui-hide-xs" lay-unselect>
<a ew-event="fullScreen" title="全屏"><i class="layui-icon layui-icon-screen-full"></i></a>
</li>
<li class="layui-nav-item" lay-unselect>
<a>
<img src="__CATCH_ADMIN_IMAGES__/head.png" class="layui-nav-img">
<cite>{$username}</cite>
</a>
<dl class="layui-nav-child">
<!--<dd lay-unselect>
<a ew-href="javascript:;">个人中心</a>
</dd>
<dd lay-unselect>
<a ew-event="psw">修改密码</a>
</dd>
<hr>-->
<dd lay-unselect>
<a id="logout">退出</a>
</dd>
</dl>
</li>
<li class="layui-nav-item" lay-unselect>
<a ew-event="theme" title="主题"><i class="layui-icon layui-icon-more-vertical"></i></a>
</li>
</ul>
</div>
<!-- 侧边栏 -->
<div class="layui-side">
<div class="layui-side-scroll">
<ul class="layui-nav layui-nav-tree arrow2" lay-filter="admin-side-nav" lay-accordion="true"
style="margin-top: 15px;">
{foreach $menus as $key => $menu }
<li class="layui-nav-item">
{if (!empty($menu['children']))}
<a><i class="layui-icon layui-icon-home"></i>&emsp;<cite>{$menu['permission_name']}</cite></a>
<dl class="layui-nav-child">
{foreach $menu['children'] as $m}
{if (!empty($m['children']))}
<a><i class="layui-icon layui-icon-home"></i>&emsp;<cite>{$m['permission_name']}</cite></a>
<dl class="layui-nav-child">
{foreach $m['children'] as $_m}
<dd><a lay-href="{:url($_m['route'])}">{$_m['permission_name']}</a></dd>
{/foreach}
</dl>
{else/}
<dd><a lay-href="{:url($m['route'])}">{$m['permission_name']}</a></dd>
{/if}
{/foreach}
</dl>
{else/}
<a lay-href="{:url($menu['route'])}"><i class="layui-icon layui-icon-unlink"></i>&emsp;<cite>{$menu['permission_name']}</cite></a>
{/if}
</li>
{/foreach}
</ul>
</div>
</div>
<!-- 主体部分 -->
<div class="layui-body"></div>
<!-- 底部 -->
<div class="layui-footer">
copyright 2017 ~ © {:date('Y', time())} <a href="https://catchadmin.com" target="_blank">catchadmin.com</a> all rights reserved.
<span class="pull-right">Version 2.0</span>
</div>
</div>
<!-- 加载动画 -->
<div class="page-loading">
<div class="ball-loader">
<span></span><span></span><span></span><span></span>
</div>
</div>
<!-- js部分 -->
<script type="text/javascript" src="__CATCH_ADMIN_LIBS__/layui/layui.js"></script>
<script type="text/javascript" src="__CATCH_ADMIN_JS__/common.js?v=315"></script>
<script>
layui.use(['index', 'admin'], function () {
var $ = layui.jquery;
var index = layui.index;
var admin = layui.admin;
// 默认加载主页
index.loadHome({
menuPath: '{:url("dashboard")}',
menuName: '<i class="layui-icon layui-icon-home"></i>'
});
$(document).on('click','#logout',function(){
admin.req('{:url("logout")}', {}, function (response) {
if (response.code === 10000) {
window.location.href = "{:url('login')}";
}
}, 'post')
});
});
</script>
</body>
</html>

259
catch/index/view/theme.html Normal file
View File

@@ -0,0 +1,259 @@
<!DOCTYPE html>
<html lang="en" class="bg-white">
<head>
<title>设置</title>
<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, maximum-scale=1">
<link rel="stylesheet" href="__CATCH_ADMIN_LIBS__/layui/css/layui.css"/>
<link rel="stylesheet" href="__CATCH_ADMIN_MODULE__/admin.css?v=315"/>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<style>
body {
overflow-x: hidden;
}
.layui-card-body {
padding: 0;
}
.theme-div {
padding-left: 15px;
padding-top: 20px;
margin-bottom: 10px;
}
.btnTheme {
display: inline-block;
margin: 0 6px 15px 0;
padding: 4px;
border: 1px solid #fff;
}
.btnTheme img {
width: 80px;
height: 50px;
border: 1px solid #f2f2f2;
background: #F2F2F2;
cursor: pointer;
}
.btnTheme:hover, .btnTheme.active {
border-color: #5FB878;
}
.more-menu-item {
display: block;
height: 50px;
line-height: 50px;
font-size: 16px;
border-bottom: 1px solid #e8e8e8;
color: #333;
padding: 0px 25px;
font-style: normal;
}
.more-menu-item:first-child {
border-top: 1px solid #e8e8e8;
}
.more-menu-item:hover {
background: #F2F2F2;
color: #333;
}
.more-menu-item .layui-icon {
padding-right: 10px;
font-size: 18px;
}
.more-menu-item:after {
content: "\e602";
font-family: layui-icon !important;
position: absolute;
right: 16px;
color: #999;
}
.more-menu-item.no-icon:after {
content: "";
}
/** 设置表单样式 */
.set-item-label {
display: inline-block;
height: 38px;
line-height: 38px;
padding-left: 20px;
color: #333333;
}
.set-item-ctrl {
display: inline-block;
height: 38px;
line-height: 38px;
}
.set-item-ctrl > * {
margin: 0;
}
</style>
</head>
<body>
<!-- 加载动画 -->
<div class="page-loading">
<div class="ball-loader">
<span></span><span></span><span></span><span></span>
</div>
</div>
<div class="layui-card-header">设置主题</div>
<div class="layui-card-body">
<!-- 主题列表 -->
<div class="theme-div"></div>
<!-- 导航 -->
<div class="more-menu">
<a class="more-menu-item" href="https://gitee.com/jaguarjack/catchAdmin" target="_blank">
<i class="layui-icon layui-icon-read" style="font-size: 19px;"></i> Git 地址
</a>
<a class="more-menu-item" href="https://catchadmin.com" target="_blank">
<i class="layui-icon layui-icon-tabs" style="font-size: 16px;"></i> &nbsp;官网
</a>
<!--<a class="more-menu-item" href="https://demo.easyweb.vip/theme" target="_blank">
<i class="layui-icon layui-icon-theme"></i> 主题生成器
</a>-->
</div>
<!-- 控制开关 -->
<div class="layui-form" style="margin: 25px 0;">
<div class="layui-form-item">
<label class="set-item-label">&emsp;脚:</label>
<div class="set-item-ctrl">
<input id="setFooter" lay-filter="setFooter" type="checkbox" lay-skin="switch" lay-text="开启|关闭">
</div>
<label class="set-item-label"> Tab&nbsp;记忆:</label>
<div class="set-item-ctrl">
<input id="setTab" lay-filter="setTab" type="checkbox" lay-skin="switch" lay-text="开启|关闭">
</div>
</div>
<div class="layui-form-item">
<label class="set-item-label">多标签:</label>
<div class="set-item-ctrl">
<input id="setMoreTab" lay-filter="setMoreTab" type="checkbox" lay-skin="switch" lay-text="开启|关闭">
</div>
<label class="set-item-label">切换刷新:</label>
<div class="set-item-ctrl">
<input id="setRefresh" lay-filter="setRefresh" type="checkbox" lay-skin="switch" lay-text="开启|关闭">
</div>
</div>
<div class="layui-form-item">
<label class="set-item-label">导航箭头:</label>
<div class="set-item-ctrl">
<input lay-filter="navArrow" type="radio" value="" title="默认" name="navArrow">
<input lay-filter="navArrow" type="radio" value="arrow2" title="箭头" name="navArrow">
<input lay-filter="navArrow" type="radio" value="arrow3" title="加号" name="navArrow">
</div>
</div>
</div>
</div>
<script type="text/javascript" src="__CATCH_ADMIN_LIBS__/layui/layui.js"></script>
<script type="text/javascript" src="__CATCH_ADMIN_JS__/common.js?v=315"></script>
<script>
layui.use(['layer', 'form', 'admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var leftNav = '.layui-layout-admin>.layui-side>.layui-side-scroll>.layui-nav';
var mainTab = '.layui-body>.layui-tab[lay-filter="admin-pagetabs"]';
var themes = [
{title: '黑白主题', theme: 'admin'},
{title: '黑色主题', theme: 'black'},
{title: '蓝色主题', theme: 'blue'},
{title: '藏青主题', theme: 'cyan'},
{title: '黄色主题', theme: 'yellow'},
{title: '绿色主题', theme: 'green'},
{title: '粉红主题', theme: 'pink'},
{title: '紫白主题', theme: 'purple-white'},
{title: '紫色主题', theme: 'purple'},
{title: '白色主题', theme: 'white'},
{title: '红白主题', theme: 'red-white'},
{title: '红色主题', theme: 'red'}
];
for (var i = 0; i < themes.length; i++) {
var str = '<div class="btnTheme" theme="theme-' + themes[i].theme + '" title="' + themes[i].title + '">';
str += ' <img src="__CATCH_ADMIN_MODULE__/theme/img/theme-' + themes[i].theme + '.png">';
str += ' </div>';
$('.theme-div').append(str)
}
// 切换主题
var mTheme = layui.data(admin.tableName).theme;
$('.btnTheme[theme=' + (mTheme ? mTheme : admin.defaultTheme) + ']').addClass('active');
$('.btnTheme').click(function () {
$('.btnTheme').removeClass('active');
$(this).addClass('active');
admin.changeTheme($(this).attr('theme'));
});
// 关闭/开启页脚
var openFooter = layui.data(admin.tableName).openFooter;
$('#setFooter').prop('checked', openFooter == undefined ? true : openFooter);
form.on('switch(setFooter)', function (data) {
var checked = data.elem.checked;
layui.data(admin.tableName, {key: 'openFooter', value: checked});
checked ? top.layui.jquery('body.layui-layout-body').removeClass('close-footer') : top.layui.jquery('body.layui-layout-body').addClass('close-footer');
});
// 关闭/开启Tab记忆功能
$('#setTab').prop('checked', top.layui.index.cacheTab);
form.on('switch(setTab)', function (data) {
top.layui.index.setTabCache(data.elem.checked);
});
// 切换Tab自动刷新
var tabAutoRefresh = layui.data(admin.tableName).tabAutoRefresh;
$('#setRefresh').prop('checked', tabAutoRefresh == undefined ? false : tabAutoRefresh);
form.on('switch(setRefresh)', function (data) {
var checked = data.elem.checked;
layui.data(admin.tableName, {key: 'tabAutoRefresh', value: checked});
checked ? top.layui.jquery(mainTab).attr('lay-autoRefresh', 'true') : top.layui.jquery(mainTab).removeAttr('lay-autoRefresh');
});
// 关闭/开启多标签
var openTab = layui.data(admin.tableName).openTab;
$('#setMoreTab').prop('checked', openTab == undefined ? top.layui.index.pageTabs : openTab);
form.on('switch(setMoreTab)', function (data) {
var checked = data.elem.checked;
layui.data(admin.tableName, {key: 'openTab', value: checked});
admin.putTempData('indexTabs', undefined); // 清除缓存的Tab
top.location.reload();
});
// 导航小三角
var navArrow = layui.data(admin.tableName).navArrow;
if (navArrow == undefined) {
var $sideNav = top.layui.jquery('.layui-side .layui-nav-tree');
navArrow = $sideNav.hasClass('arrow2') ? 'arrow2' : $sideNav.hasClass('arrow3') ? 'arrow3' : '';
}
$('[name="navArrow"][value="' + (navArrow ? navArrow : '') + '"]').prop('checked', 'true');
form.on('radio(navArrow)', function (data) {
layui.data(admin.tableName, {key: 'navArrow', value: data.value});
top.layui.jquery(leftNav).removeClass('arrow2 arrow3');
data.value && top.layui.jquery(leftNav).addClass(data.value);
});
form.render('checkbox');
form.render('radio');
});
</script>
</body>
</html>

View File

@@ -0,0 +1,81 @@
<?php
namespace catchAdmin\login;
use catchAdmin\user\model\Users;
use think\facade\Db;
class LoginLogListener
{
public function handle($params)
{
$agent = request()->header('user-agent');
$username = Users::where('email', $params['email'])->value('username');
Db::name('login_log')->insert([
'login_name' => $username ? : $params['email'],
'login_ip' => request()->ip(),
'browser' => $this->getBrowser($agent),
'os' => $this->getOs($agent),
'login_at' => time(),
'status' => $params['success'] ? 1 : 2,
]);
}
/**
*
* @time 2019年12月12日
* @param $agent
* @return string
*/
private function getOs($agent): string
{
if (false !== stripos($agent, 'win') && preg_match('/nt 6.1/i', $agent)) {
return 'Windows 7';
}
if (false !== stripos($agent, 'win') && preg_match('/nt 6.2/i', $agent)) {
return 'Windows 8';
}
if(false !== stripos($agent, 'win') && preg_match('/nt 10.0/i', $agent)) {
return 'Windows 10';#添加win10判断
}
if (false !== stripos($agent, 'win') && preg_match('/nt 5.1/i', $agent)) {
return 'Windows XP';
}
if (false !== stripos($agent, 'linux')) {
return 'Linux';
}
if (false !== stripos($agent, 'mac')) {
return 'mac';
}
return '未知';
}
/**
*
* @time 2019年12月12日
* @param $agent
* @return string
*/
private function getBrowser($agent): string
{
if (false !== stripos($agent, "MSIE")) {
return 'MSIE';
}
if (false !== stripos($agent, "Firefox")) {
return 'Firefox';
}
if (false !== stripos($agent, "Chrome")) {
return 'Chrome';
}
if (false !== stripos($agent, "Safari")) {
return 'Safari';
}
if (false !== stripos($agent, "Opera")) {
return 'Opera';
}
return '未知';
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace catchAdmin\login\controller;
use app\exceptions\LoginFailedException;
use catchAdmin\user\Auth;
use catchAdmin\login\request\LoginRequest;
use catcher\base\CatchController;
use catcher\CatchResponse;
use think\captcha\Captcha;
class Index extends CatchController
{
/**
* 登录
*
* @time 2019年11月30日
* @throws \Exception
* @return string
*/
public function index(): string
{
return $this->fetch();
}
/**
* 登陆
*
* @time 2019年11月28日
* @param LoginRequest $request
* @return bool|string
* @throws \catcher\exceptions\LoginFailedException
* @throws \cather\exceptions\LoginFailedException
* @throws LoginFailedException
*/
public function login(LoginRequest $request)
{
$params = $request->param();
$token = Auth::login($params);
// 登录事件
$params['success'] = $token;
event('loginLog', $params);
return $token ? CatchResponse::success([
'token' => $token,
], '登录成功') :
CatchResponse::success('', '登录失败');
}
/**
* 登出
*
* @time 2019年11月28日
* @return \think\response\Json
* @throws \Exception
*/
public function logout(): \think\response\Json
{
if (Auth::logout()) {
return CatchResponse::success();
}
return CatchResponse::fail('登出失败');
}
/**
*
* @time 2019年12月12日
* @param Captcha $captcha
* @param null $config
* @return \think\Response
*/
public function captcha(Captcha $captcha, $config = null): \think\Response
{
return $captcha->create($config);
}
}

13
catch/login/module.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "登陆",
"alias": "login",
"description": "",
"keywords": [],
"order": 1,
"services": [
"catchAdmin\\login\\LoginService"
],
"aliases": {},
"files": [],
"requires": []
}

View File

@@ -0,0 +1,23 @@
<?php
namespace catchAdmin\login\request;
use catcher\base\CatchRequest;
class LoginRequest extends CatchRequest
{
protected function rules(): array
{
// TODO: Implement rules() method.
return [
'email|用户名' => 'email',
'password|密码' => 'require',
// 'captcha|验证码' => 'require|captcha'
];
}
protected function message(): array
{
// TODO: Implement message() method.
return [];
}
}

11
catch/login/route.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
# 登陆页面
$router->get('login', '\catchAdmin\login\controller\Index@index');
# 登入
$router->post('login', '\catchAdmin\login\controller\Index@login');
# 登出
$router->post('logout', '\catchAdmin\login\controller\Index@logout');
# 验证码
$router->get('catch/captcha/[:config]','\catchAdmin\login\controller\Index@captcha');

130
catch/login/view/index.html Normal file
View File

@@ -0,0 +1,130 @@
<!DOCTYPE html>
<html>
<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, maximum-scale=1">
<title>登录</title>
<link rel="stylesheet" href="__CATCH_ADMIN_LIBS__/layui/css/layui.css"/>
<link rel="stylesheet" href="__CATCH_ADMIN_CSS__/login.css?v=315">
<link rel="stylesheet" href="__CATCH_ADMIN_MODULE__/admin.css?v=315">
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<script>
if (window != top) {
top.location.replace(location.href);
}
</script>
<style>
.captcha {
width: 114px;
height: 38px;
}
</style>
</head>
<body>
<div class="login-wrapper">
<div class="login-header">
<img src="__CATCH_ADMIN_IMAGES__/logo.png"> CatchAdmin 后台管理系统
</div>
<div class="login-body">
<div class="layui-card">
<div class="layui-card-header">
<i class="layui-icon layui-icon-engine"></i>&nbsp;&nbsp;用户登录
</div>
<form class="layui-card-body layui-form layui-form-pane">
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-username"></i></label>
<div class="layui-input-block">
<input name="email" type="text" placeholder="邮箱" class="layui-input"
lay-verType="tips" lay-verify="required|email" required/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-password"></i></label>
<div class="layui-input-block">
<input name="password" type="password" placeholder="密码" class="layui-input"
lay-verType="tips" lay-verify="required" required/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-vercode"></i></label>
<div class="layui-input-block">
<div class="layui-row inline-block">
<div class="layui-col-xs7">
<input name="captcha" type="text" placeholder="验证码" class="layui-input"
autocomplete="off" lay-verType="tips" lay-verify="required" required/>
</div>
<div class="layui-col-xs5" style="padding-left: 6px;">
<img src="{:url('catch/captcha')}" alt="captcha" class="captcha" onclick="this.src = this.src + '?t=' + (new Date).getTime();"/>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<!--<a href="javascript:;" class="layui-link">帐号注册</a>-->
<!--<a href="javascript:;" class="layui-link pull-right">忘记密码?</a>-->
</div>
<div class="layui-form-item">
<button lay-filter="login-submit" class="layui-btn layui-btn-fluid" lay-submit>登 录</button>
</div>
<!--<div class="layui-form-item login-other">
<label>第三方登录</label>
<a href="javascript:;"><i class="layui-icon layui-icon-login-qq"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-wechat"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-weibo"></i></a>
</div>-->
</form>
</div>
</div>
<div class="login-footer">
<p>© 2015 ~ {:date('Y', time())} @catchAdmin 版权所有</p>
<!--<p>
<span><a href="https://easyweb.vip" target="_blank">获取授权</a></span>
<span><a href="https://easyweb.vip/doc/" target="_blank">开发文档</a></span>
<span><a href="https://demo.easyweb.vip/spa/" target="_blank">单页面版</a></span>
</p>-->
</div>
</div>
<!-- js部分 -->
<script type="text/javascript" src="__CATCH_ADMIN_LIBS__/layui/layui.js"></script>
<script type="text/javascript" src="__CATCH_ADMIN_JS__/common.js?v=315"></script>
<script>
layui.use(['layer', 'form'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
// 表单提交
form.on('submit(login-submit)', function (obj) {
$.ajax({
url: "{:url('login')}",
type: 'post',
data: obj.field,
success: function(response) {
if (response.code === 10000) {
layer.msg(response.msg, {
icon: 1,
time: 2000 //2秒关闭如果不配置默认是3秒
}, function () {
//do something
window.location.href = '/';
})
} else {
layer.msg(response.msg, {
icon: 2,
time: 2000 //2秒关闭如果不配置默认是3秒
})
}
}
});
return false;
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,28 @@
<?php
namespace catchAdmin\permissions;
use catchAdmin\permissions\model\Permissions;
use catcher\CatchAdmin;
use think\facade\Db;
class OperateLogListener
{
public function handle($params)
{
$request = $params['request'];
$permission = $params['permission'];
$parentPermission = Permissions::where('id', $permission->parent_id)->value('permission_name');
Db::name('operate_log')->insert([
'creator_id' => $request->user()->id,
'module' => $parentPermission ? : '',
'method' => $request->method(),
'operate' => $permission->permission_name,
'route' => $permission->route,
'params' => json_encode($request->param()),
'created_at' => time(),
'ip' => $request->ip(),
]);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace catchAdmin\permissions;
use app\Request;
use catchAdmin\permissions\model\Permissions;
use catcher\exceptions\PermissionForbiddenException;
use think\helper\Str;
class PermissionsMiddleware
{
/**
*
* @time 2019年12月12日
* @param Request $request
* @param \Closure $next
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws PermissionForbiddenException
*/
public function handle(Request $request, \Closure $next)
{
$rule = $rule = $request->rule()->getName();
if (!$rule) {
return $next($request);
}
[$module, $controller, $action] = $this->parseRule($rule);
if (in_array($module, $this->ignoreModule())) {
return $next($request);
}
if (!$request->user()) {
throw new PermissionForbiddenException('Login is invalid', 10006);
}
// toad
if (($permission = $this->getPermission($module, $controller, $action, $request))
&& !in_array($permission->id, $request->user()->getPermissionsBy())) {
throw new PermissionForbiddenException();
}
return $next($request);
}
protected function parseRule($rule)
{
[$controller, $action] = explode(Str::contains($rule, '@') ? '@' : '/', $rule);
$controller = explode('\\', $controller);
$controllerName = strtolower(array_pop($controller));
array_pop($controller);
$module = array_pop($controller);
return [$module, $controllerName, $action];
}
/**
*
* @time 2019年12月14日
* @param $module
* @param $controllerName
* @param $action
* @param $request
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @return array|bool|\think\Model|null
*/
protected function getPermission($module, $controllerName, $action, $request)
{
$permissionMark = sprintf('%s:%s', $controllerName, $action);
$permission = Permissions::where('module', $module)->where('permission_mark', $permissionMark)->find();
if (!$permission) {
return false;
}
event('operateLog', [
'request' => $request,
'permission' => $permission,
]);
return $permission;
}
protected function ignoreModule()
{
return ['login'];
}
}

View File

@@ -0,0 +1,166 @@
<?php
namespace catchAdmin\permissions\controller;
use app\Request;
use catcher\base\CatchController;
use catcher\CatchAdmin;
use catcher\CatchForm;
use catcher\CatchResponse;
use catcher\exceptions\FailedException;
use catcher\Tree;
use catchAdmin\permissions\model\Permissions as Permissions;
class Permission extends CatchController
{
protected $permissions;
public function __construct(Permissions $permissions)
{
$this->permissions = $permissions;
}
/**
*
* @time 2019年12月11日
* @throws \Exception
* @return string
*/
public function index()
{
return $this->fetch();
}
/**
*
* @time 2019年12月11日
* @param Request $request
* @return \think\response\Json
*/
public function list(Request $request)
{
return CatchResponse::success(Tree::done($this->permissions->getList($request->param())));
}
/**
*
* @time 2019年12月11日
* @throws \Exception
* @return string
*/
public function create()
{
$form = new CatchForm();
$form->formId('permission');
$form->text('permission_name', '菜单名称', true)->verify('required')->placeholder('请输入菜单名称');
$form->hidden('parent_id')->default(\request()->param('id') ?? 0);
$form->select('module', '模块', true)->verify('required')->options(CatchAdmin::getModulesInfo());
$form->text('route', '路由')->placeholder('请输入路由');
$form->radio('method', '请求方法', true)->default(Permissions::GET)->options([
['value' => Permissions::GET, 'title' => 'get'],
['value' => Permissions::POST, 'title' => 'post'],
['value' => Permissions::PUT, 'title' => 'put'],
['value' => Permissions::DELETE, 'title' => 'delete'],
]);
$form->text('permission_mark', '权限标识', true)->verify('required')->placeholder('请输入权限标识controller:action');
$form->radio('type', '类型', true)->default(Permissions::BTN_TYPE)->options([
['value' => Permissions::MENU_TYPE, 'title' => '菜单'],
['value' => Permissions::BTN_TYPE, 'title' => '按钮'],
]);
$form->text('sort', '排序')->verify('numberX')->default(1)->placeholder('倒叙排序');
$form->formBtn('submitPermission');
return $this->fetch(['form' => $form->render()]);
}
/**
*
* @time 2019年12月11日
* @param Request $request
* @return \think\response\Json
*/
public function save(Request $request)
{
return CatchResponse::success($this->permissions->storeBy($request->param()));
}
public function read()
{}
/**
*
* @time 2019年12月11日
* @param $id
* @throws \Exception
* @return string
*/
public function edit($id)
{
$permission = $this->permissions->findBy($id);
$form = new CatchForm();
$form->formId('permission');
$form->text('permission_name', '菜单名称', true)
->default($permission->permission_name)
->verify('required')
->placeholder('请输入菜单名称');
$form->hidden('parent_id')->default($permission->parent_id);
$form->select('module', '模块', true)->default($permission->module)->options(CatchAdmin::getModulesInfo());
$form->text('route', '路由')->default($permission->route)->placeholder('请输入路由');
$form->radio('method', '请求方法', true)->verify('required')->default($permission->method)->options([
['value' => Permissions::GET, 'title' => 'get'],
['value' => Permissions::POST, 'title' => 'post'],
['value' => Permissions::PUT, 'title' => 'put'],
['value' => Permissions::DELETE, 'title' => 'delete'],
]);
$form->text('permission_mark', '权限标识', true)
->default($permission->permission_mark)
->verify('required')->placeholder('请输入权限标识controller:action');
$form->radio('type', '类型', true)->default($permission->type)->options([
['value' => Permissions::MENU_TYPE, 'title' => '菜单'],
['value' => Permissions::BTN_TYPE, 'title' => '按钮'],
]);
$form->text('sort', '排序')->verify('numberX')->default($permission->sort)->placeholder('倒叙排序');
$form->formBtn('submitPermission');
return $this->fetch([
'form' => $form->render(),
'permission_id' => $permission->id,
]);
}
/**
*
* @time 2019年12月11日
* @param $id
* @param Request $request
* @return \think\response\Json
*/
public function update($id, Request $request)
{
return CatchResponse::success($this->permissions->updateBy($id, $request->param()));
}
/**
*
* @time 2019年12月11日
* @param $id
* @throws FailedException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @return \think\response\Json
*/
public function delete($id)
{
if ($this->permissions->where('parent_id', $id)->find()) {
throw new FailedException('存在子菜单,无法删除');
}
$this->permissions->findBy($id)->roles()->detach();
return CatchResponse::success($this->permissions->deleteBy($id));
}
}

View File

@@ -0,0 +1,197 @@
<?php
namespace catchAdmin\permissions\controller;
use app\Request;
use catcher\base\CatchController;
use catcher\CatchForm;
use catcher\CatchResponse;
use catcher\exceptions\FailedException;
use catcher\Tree;
use think\response\Json;
class Role extends CatchController
{
protected $role;
public function __construct(\catchAdmin\permissions\model\Roles $role)
{
$this->role = $role;
}
/**
*
* @time 2019年12月09日
* @throws \Exception
* @return string
*/
public function index()
{
return $this->fetch();
}
/**
*
* @time 2019年12月11日
* @throws \Exception
* @return string
*/
public function create()
{
$form = new CatchForm();
$form->formId('role');
$form->text('role_name', '角色名称', true)->verify('required')->placeholder('请输入角色名称');
$form->hidden('parent_id')->default(\request()->param('id') ?? 0);
$form->textarea('description', '角色描述')->placeholder('请输入角色描述');
$form->dom('<div id="permissions"></div>', '权限');
$form->formBtn('submitRole');
return $this->fetch([
'form' => $form->render(),
'parent_id' => \request()->param('id') ?? 0,
]);
}
/**
*
* @time 2019年12月11日
* @param Request $request
* @return Json
* @throws \think\db\exception\DbException
*/
public function save(Request $request)
{
$this->role->storeBy($request->param());
if (!empty($request->param('permissionids'))) {
$this->role->attach($request->param('permissionids'));
}
// 添加角色
return CatchResponse::success();
}
public function read($id)
{
}
/**
*
* @time 2019年12月11日
* @param $id
* @throws \Exception
* @return string
*/
public function edit($id)
{
$role = $this->role->findBy($id);
$form = new CatchForm();
$form->formId('role');
$form->hidden('parent_id')->default($role->parent_id);
$form->text('role_name', '角色名称', true)->default($role->name)->verify('required')->placeholder('请输入角色名称');
$form->textarea('description', '角色描述')->default($role->description)->placeholder('请输入角色描述');
$form->dom('<div id="permissions"></div>', '权限');
$form->formBtn('submitRole');
return $this->fetch([
'form' => $form->render(),
'role_id' => $role->id,
'parent_id' => $role->parent_id
]);
}
/**
*
* @time 2019年12月11日
* @param $id
* @param Request $request
* @return Json
* @throws \think\db\exception\DbException
*/
public function update($id, Request $request)
{
$this->role->updateBy($id, $request->param());
$role = $this->role->findBy($id);
$role->detach();
if (!empty($request->param('permissionids'))) {
$role->attach($request->param('permissionids'));
}
return CatchResponse::success();
}
/**
*
* @time 2019年12月11日
* @param $id
* @throws FailedException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @return Json
*/
public function delete($id)
{
if ($this->role->where('parent_id', $id)->find()) {
throw new FailedException('存在子角色,无法删除');
}
$role = $this->role->findBy($id);
// 删除权限
$role->detach();
// 删除用户关联
$role->users()->detach();
// 删除
$this->role->deleteBy($id);
return CatchResponse::success();
}
/**
*
* @time 2019年12月11日
* @param Request $request
* @return Json
*/
public function list(Request $request)
{
return CatchResponse::success(Tree::done($this->role->getList($request->param())));
}
/**
*
* @time 2019年12月11日
* @param Request $request
* @param \catchAdmin\permissions\model\Permissions $permission
* @return Json
*/
public function getPermissions(Request $request, \catchAdmin\permissions\model\Permissions $permission): Json
{
$parentRoleHasPermissionIds = null;
if ($request->param('parent_id')) {
$permissions = $this->role->findBy($request->param('parent_id'))->getPermissions();
foreach ($permissions as $_permission) {
$parentRoleHasPermissionIds[] = $_permission->pivot->permission_id;
}
}
$permissions = Tree::done($permission->getList([
'permission_ids' => $parentRoleHasPermissionIds
]));
$permissionIds = [];
if ($request->param('role_id')) {
$roleHasPermissions = $this->role->findBy($request->param('role_id'))->getPermissions();
foreach ($roleHasPermissions as $_permission) {
$permissionIds[] = $_permission->pivot->permission_id;
}
}
return CatchResponse::success([
'permissions' => $permissions,
'hasPermissions' => $permissionIds,
]);
}
}

View File

@@ -0,0 +1,40 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class Roles extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('roles',['engine'=>'Innodb', 'comment' => '角色表', 'signed' => false]);
$table->addColumn('role_name', 'string',['limit' => 15,'default'=>'','comment'=>'角色名'])
->addColumn('parent_id', 'integer',['default'=>0,'comment'=>'父级ID', 'signed' => false])
->addColumn('description', 'string',['default'=> '','comment'=>'角色备注'])
->addColumn('created_at', 'integer', array('default'=>0,'comment'=>'创建时间', 'signed' => false ))
->addColumn('updated_at', 'integer', array('default'=>0,'comment'=>'更新时间', 'signed' => false))
->addColumn('deleted_at', 'integer', array('default'=>0,'comment'=>'删除状态0未删除 >0 已删除', 'signed' => false))
->create();
}
}

View File

@@ -0,0 +1,46 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class Permissions extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('permissions',['engine'=>'Innodb', 'comment' => '菜单表', 'signed' => false]);
$table->addColumn('permission_name', 'string',['limit' => 15,'default'=>'','comment'=>'菜单名称'])
->addColumn('parent_id', 'integer',['default'=>0,'comment'=>'父级ID', 'signed' => false])
->addColumn('route', 'string', ['default' => '', 'comment' => '路由', 'limit' => 50])
->addColumn('module', 'string', ['default' => '', 'comment' => '模块', 'limit' => 20])
->addColumn('method', 'string', ['default' => 'get', 'comment' => '路由请求方法', 'limit' => 15])
->addColumn('permission_mark', 'string', ['null' => false, 'comment' => '权限标识', 'limit' => 50])
->addColumn('type', 'integer',['limit' => \Phinx\Db\Adapter\MysqlAdapter::INT_TINY,'default'=> 1,'comment'=>'1 菜单 2 按钮'])
->addColumn('sort', 'integer',['default'=> 0,'comment'=>'排序字段'])
->addColumn('created_at', 'integer', array('default'=>0,'comment'=>'创建时间', 'signed' => false ))
->addColumn('updated_at', 'integer', array('default'=>0,'comment'=>'更新时间', 'signed' => false))
->addColumn('deleted_at', 'integer', array('default'=>0,'comment'=>'删除状态null 未删除 timestamp 已删除', 'signed' => false))
->create();
}
}

View File

@@ -0,0 +1,36 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class UserHasRoles extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('user_has_roles',['engine'=>'Innodb', 'comment' => '用户角色表', 'signed' => false]);
$table->addColumn('uid', 'integer',['comment'=>'用户ID', 'signed' => false])
->addColumn('role_id', 'integer', ['comment'=>'角色ID', 'signed' => false])
->create();
}
}

View File

@@ -0,0 +1,36 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class RoleHasPermissions extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('role_has_permissions',['engine'=>'Innodb', 'comment' => '角色权限表', 'signed' => false]);
$table->addColumn('role_id', 'integer',['comment'=>'角色ID', 'signed' => false])
->addColumn('permission_id', 'integer', ['comment'=>'权限ID', 'signed' => false])
->create();
}
}

View File

@@ -0,0 +1,603 @@
<?php
use think\migration\Seeder;
class PermissionSeed extends Seeder
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* http://docs.phinx.org/en/latest/seeding.html
*/
public function run()
{
$this->roles();
$this->createPermissions();
}
protected function roles()
{
\catchAdmin\permissions\model\Roles::create([
'role_name' => '超级管理员',
'description' => 'super user',
]);
\think\facade\Db::name('user_has_roles')->insert([
'role_id' => 1,
'uid' => 1,
]);
\think\facade\Db::name('role_has_permissions')->insertAll(array (
0 =>
array (
'role_id' => 1,
'permission_id' => 4,
),
1 =>
array (
'role_id' => 1,
'permission_id' => 6,
),
2 =>
array (
'role_id' => 1,
'permission_id' => 7,
),
3 =>
array (
'role_id' => 1,
'permission_id' => 8,
),
4 =>
array (
'role_id' => 1,
'permission_id' => 9,
),
5 =>
array (
'role_id' => 1,
'permission_id' => 10,
),
6 =>
array (
'role_id' => 1,
'permission_id' => 11,
),
7 =>
array (
'role_id' => 1,
'permission_id' => 12,
),
8 =>
array (
'role_id' => 1,
'permission_id' => 13,
),
9 =>
array (
'role_id' => 1,
'permission_id' => 14,
),
10 =>
array (
'role_id' => 1,
'permission_id' => 15,
),
11 =>
array (
'role_id' => 1,
'permission_id' => 16,
),
12 =>
array (
'role_id' => 1,
'permission_id' => 17,
),
13 =>
array (
'role_id' => 1,
'permission_id' => 18,
),
14 =>
array (
'role_id' => 1,
'permission_id' => 19,
),
15 =>
array (
'role_id' => 1,
'permission_id' => 20,
),
16 =>
array (
'role_id' => 1,
'permission_id' => 21,
),
17 =>
array (
'role_id' => 1,
'permission_id' => 22,
),
18 =>
array (
'role_id' => 1,
'permission_id' => 23,
),
19 =>
array (
'role_id' => 1,
'permission_id' => 24,
),
20 =>
array (
'role_id' => 1,
'permission_id' => 25,
),
21 =>
array (
'role_id' => 1,
'permission_id' => 26,
),
22 =>
array (
'role_id' => 1,
'permission_id' => 27,
),
23 =>
array (
'role_id' => 1,
'permission_id' => 28,
),
24 =>
array (
'role_id' => 1,
'permission_id' => 29,
),
25 =>
array (
'role_id' => 1,
'permission_id' => 30,
),
26 =>
array (
'role_id' => 1,
'permission_id' => 31,
),
27 =>
array (
'role_id' => 1,
'permission_id' => 32,
),
28 =>
array (
'role_id' => 1,
'permission_id' => 33,
),
29 =>
array (
'role_id' => 1,
'permission_id' => 34,
),
30 =>
array (
'role_id' => 1,
'permission_id' => 35,
),
31 =>
array (
'role_id' => 1,
'permission_id' => 36,
),
));
}
protected function createPermissions()
{
foreach (array (
0 =>
array (
'id' => 4,
'permission_name' => 'Dashboard',
'parent_id' => 0,
'module' => 'index',
'route' => 'dashboard',
'method' => 'get',
'permission_mark' => 'index:dashboard',
'type' => 1,
'sort' => 10000,
),
1 =>
array (
'id' => 5,
'permission_name' => '主题',
'parent_id' => 4,
'module' => '',
'route' => 'themes',
'method' => 'get',
'permission_mark' => 'index:theme',
'type' => 2,
'sort' => 0,
),
2 =>
array (
'id' => 6,
'permission_name' => '用户管理',
'parent_id' => 16,
'module' => 'user',
'route' => 'user',
'method' => 'get',
'permission_mark' => 'user:index',
'type' => 1,
'sort' => 9999,
),
3 =>
array (
'id' => 7,
'permission_name' => '创建',
'parent_id' => 6,
'module' => 'user',
'route' => 'user',
'method' => 'get',
'permission_mark' => 'user:create',
'type' => 2,
'sort' => 0,
),
4 =>
array (
'id' => 8,
'permission_name' => '保存',
'parent_id' => 6,
'module' => 'user',
'route' => 'user',
'method' => 'post',
'permission_mark' => 'user:save',
'type' => 2,
'sort' => 0,
),
5 =>
array (
'id' => 9,
'permission_name' => '查看',
'parent_id' => 6,
'module' => 'user',
'route' => 'user/<id>/edit',
'method' => 'get',
'permission_mark' => 'user:edit',
'type' => 2,
'sort' => 0,
),
6 =>
array (
'id' => 10,
'permission_name' => '编辑',
'parent_id' => 6,
'module' => 'user',
'route' => 'user/<id>',
'method' => 'put',
'permission_mark' => 'user:update',
'type' => 2,
'sort' => 0,
),
7 =>
array (
'id' => 11,
'permission_name' => '删除',
'parent_id' => 6,
'module' => 'user',
'route' => 'user/<id>',
'method' => 'delete',
'permission_mark' => 'user:delete',
'type' => 2,
'sort' => 0,
),
8 =>
array (
'id' => 12,
'permission_name' => '列表',
'parent_id' => 6,
'module' => 'user',
'route' => 'users',
'method' => 'get',
'permission_mark' => 'user:list',
'type' => 2,
'sort' => 0,
),
9 =>
array (
'id' => 13,
'permission_name' => '禁用/启用',
'parent_id' => 6,
'module' => 'user',
'route' => 'user/switch/status/<id>',
'method' => 'put',
'permission_mark' => 'user:switchStatus',
'type' => 2,
'sort' => 0,
),
10 =>
array (
'id' => 14,
'permission_name' => '恢复',
'parent_id' => 6,
'module' => 'user',
'route' => 'user/recover/<id>',
'method' => 'put',
'permission_mark' => 'user:recover',
'type' => 2,
'sort' => 0,
),
11 =>
array (
'id' => 15,
'permission_name' => '角色管理',
'parent_id' => 16,
'module' => 'permissions',
'route' => 'role',
'method' => 'get',
'permission_mark' => 'role:index',
'type' => 1,
'sort' => 1000,
),
12 =>
array (
'id' => 16,
'permission_name' => '权限管理',
'parent_id' => 0,
'module' => 'permissions',
'route' => '',
'method' => 'get',
'permission_mark' => '@:@',
'type' => 1,
'sort' => 1,
),
13 =>
array (
'id' => 17,
'permission_name' => '菜单管理',
'parent_id' => 16,
'module' => 'permissions',
'route' => 'permission',
'method' => 'get',
'permission_mark' => 'permission:index',
'type' => 1,
'sort' => 1,
),
14 =>
array (
'id' => 18,
'permission_name' => '创建',
'parent_id' => 15,
'module' => 'permissions',
'route' => 'role/create',
'method' => 'get',
'permission_mark' => 'role:create',
'type' => 2,
'sort' => 1,
),
15 =>
array (
'id' => 19,
'permission_name' => '保存',
'parent_id' => 15,
'module' => 'permissions',
'route' => 'role',
'method' => 'post',
'permission_mark' => 'role:save',
'type' => 2,
'sort' => 1,
),
16 =>
array (
'id' => 20,
'permission_name' => '查看',
'parent_id' => 15,
'module' => 'permissions',
'route' => 'role/<id>/edit',
'method' => 'get',
'permission_mark' => 'role:edit',
'type' => 2,
'sort' => 1,
),
17 =>
array (
'id' => 21,
'permission_name' => '更新',
'parent_id' => 15,
'module' => 'permissions',
'route' => 'role/<id>',
'method' => 'put',
'permission_mark' => 'role:update',
'type' => 2,
'sort' => 1,
),
18 =>
array (
'id' => 22,
'permission_name' => '删除',
'parent_id' => 15,
'module' => 'permissions',
'route' => 'role/<id>',
'method' => 'delete',
'permission_mark' => 'role:delete',
'type' => 2,
'sort' => 1,
),
19 =>
array (
'id' => 23,
'permission_name' => '列表',
'parent_id' => 15,
'module' => 'permissions',
'route' => 'roles',
'method' => 'get',
'permission_mark' => 'role:list',
'type' => 2,
'sort' => 1,
),
20 =>
array (
'id' => 24,
'permission_name' => '获取权限',
'parent_id' => 15,
'module' => 'permissions',
'route' => 'role/get/permissions',
'method' => 'get',
'permission_mark' => 'role:getPermissions',
'type' => 2,
'sort' => 1,
),
21 =>
array (
'id' => 25,
'permission_name' => '删除',
'parent_id' => 17,
'module' => 'permissions',
'route' => 'permission/<id>',
'method' => 'delete',
'permission_mark' => 'permissions:delete',
'type' => 2,
'sort' => 1,
),
22 =>
array (
'id' => 26,
'permission_name' => '更新',
'parent_id' => 17,
'module' => 'permissions',
'route' => 'permission/<id>',
'method' => 'put',
'permission_mark' => 'permission:update',
'type' => 2,
'sort' => 1,
),
23 =>
array (
'id' => 27,
'permission_name' => '查看',
'parent_id' => 17,
'module' => 'permissions',
'route' => 'permissions/<id>/edit',
'method' => 'get',
'permission_mark' => 'permission:edit',
'type' => 2,
'sort' => 1,
),
24 =>
array (
'id' => 28,
'permission_name' => '创建',
'parent_id' => 17,
'module' => 'permissions',
'route' => 'permission/create',
'method' => 'get',
'permission_mark' => 'permission:create',
'type' => 2,
'sort' => 1,
),
25 =>
array (
'id' => 29,
'permission_name' => '保存',
'parent_id' => 17,
'module' => 'permissions',
'route' => 'permission',
'method' => 'post',
'permission_mark' => 'permission',
'type' => 2,
'sort' => 1,
),
26 =>
array (
'id' => 30,
'permission_name' => '列表',
'parent_id' => 17,
'module' => 'permissions',
'route' => 'permissions',
'method' => 'get',
'permission_mark' => 'permission:list',
'type' => 2,
'sort' => 1,
),
27 =>
array (
'id' => 31,
'permission_name' => '系统管理',
'parent_id' => 0,
'module' => 'system',
'route' => '',
'method' => 'get',
'permission_mark' => 's:s',
'type' => 1,
'sort' => 1,
),
28 =>
array (
'id' => 32,
'permission_name' => '日志管理',
'parent_id' => 31,
'module' => 'system',
'route' => '',
'method' => 'get',
'permission_mark' => 'log:log',
'type' => 1,
'sort' => 1,
),
29 =>
array (
'id' => 33,
'permission_name' => '登录日志',
'parent_id' => 32,
'module' => 'system',
'route' => 'loginLog/index',
'method' => 'get',
'permission_mark' => 'operateLog:index',
'type' => 1,
'sort' => 1,
),
30 =>
array (
'id' => 34,
'permission_name' => '操作日志',
'parent_id' => 32,
'module' => 'index',
'route' => 'operateLog/index',
'method' => 'get',
'permission_mark' => 'loginLog:index',
'type' => 1,
'sort' => 1,
),
31 =>
array (
'id' => 35,
'permission_name' => '数据字典',
'parent_id' => 31,
'module' => 'system',
'route' => 'data/dictionary',
'method' => 'get',
'permission_mark' => 'datadictory:index',
'type' => 1,
'sort' => 1,
),
32 =>
array (
'id' => 36,
'permission_name' => '查看表',
'parent_id' => 35,
'module' => 'system',
'route' => 'table/view/<table>',
'method' => 'get',
'permission_mark' => 'datadictionary:view',
'type' => 2,
'sort' => 1,
),
) as $permission) {
\catchAdmin\permissions\model\Permissions::create($permission);
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace catchAdmin\permissions\model;
trait HasRolesTrait
{
/**
*
* @time 2019年12月08日
* @return mixed
*/
public function roles()
{
return $this->belongsToMany(Roles::class, 'user_has_roles', 'role_id', 'uid');
}
/**
*
* @time 2019年12月08日
* @return mixed
*/
public function getRoles()
{
return $this->roles()->select();
}
/**
*
* @time 2019年12月08日
* @param array $roles
* @return mixed
*/
public function attach(array $roles)
{
if (empty($roles)) {
return true;
}
sort($roles);
return $this->roles()->attach($roles);
}
/**
*
* @time 2019年12月08日
* @param array $roles
* @return mixed
*/
public function detach(array $roles = [])
{
if (empty($roles)) {
return $this->roles()->detach();
}
return $this->roles()->detach($roles);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace catchAdmin\permissions\model;
use catcher\base\CatchModel;
class Permissions extends CatchModel
{
protected $name = 'permissions';
protected $field = [
'id', //
'permission_name', // 菜单名称
'parent_id', // 父级ID
'module', // 模块
'route', // 路由
'method', // 请求方法
'permission_mark', // 权限标识
'type', // 1 菜单 2 按钮
'sort', // 排序字段
'created_at', // 创建时间
'updated_at', // 更新时间
'deleted_at', // 删除状态null 未删除 timestamp 已删除
];
public const MENU_TYPE = 1;
public const BTN_TYPE = 2;
public const GET = 'get';
public const POST = 'post';
public const PUT = 'put';
public const DELETE = 'delete';
public function getList($search = [])
{
return $this->when($search['name'] ?? false, function ($query) use ($search){
$query->whereLike('name', $search['name']);
})
->when($search['id'] ?? false, function ($query) use ($search){
$query->where('parent_id', $search['id'])
->whereOr('id', $search['id']);
})
->when($search['permission_ids'] ?? false, function ($query) use ($search){
$query->whereIn('id', $search['permission_ids']);
})
->order('sort', 'desc')
->order('id', 'desc')
->select()
->toArray();
}
public function roles(): \think\model\relation\BelongsToMany
{
return $this->belongsToMany(Roles::class, 'role_has_permissions', 'role_id', 'permission_id');
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace catchAdmin\permissions\model;
use catchAdmin\user\model\Users;
use catcher\base\CatchModel;
class Roles extends CatchModel
{
protected $name = 'roles';
protected $field = [
'id', //
'role_name', // 角色名
'parent_id', // 父级ID
'description', // 角色备注
'created_at', // 创建时间
'updated_at', // 更新时间
'deleted_at', // 删除状态0未删除 >0 已删除
];
public function getList($search = [])
{
return $this->when($search['name'] ?? false, function ($query) use ($search){
$query->whereLike('name', $search['name']);
})
->when($search['id'] ?? false, function ($query) use ($search){
$query->where('parent_id', $search['id'])
->whereOr('id', $search['id']);
})
->order('id', 'desc')
->select()
->toArray();
}
/**
*
* @time 2019年12月08日
* @return \think\model\relation\BelongsToMany
*/
public function users(): \think\model\relation\BelongsToMany
{
return $this->belongsToMany(Users::class, 'user_has_roles', 'uid', 'role_id');
}
/**
*
* @time 2019年12月09日
* @return \think\model\relation\BelongsToMany
*/
public function permissions(): \think\model\relation\BelongsToMany
{
return $this->belongsToMany(Permissions::class, 'role_has_permissions', 'permission_id', 'role_id');
}
/**
*
* @time 2019年12月08日
* @param array $condition
* @param array $field
* @return mixed
*/
public function getPermissions($condition = [], $field = [])
{
return $this->permissions()
->when(!empty($field), function ($query) use ($field){
$query->field($field);
})
->when(!empty($condition), function ($query) use ($condition){
$query->where($condition);
})
->select();
}
/**
*
* @time 2019年12月08日
* @param array $permissions
* @return mixed
* @throws \think\db\exception\DbException
*/
public function attach(array $permissions)
{
if (empty($permissions)) {
return true;
}
sort($permissions);
return $this->permissions()->attach($permissions);
}
/**
*
* @time 2019年12月08日
* @param array $roles
* @return mixed
*/
public function detach(array $roles = [])
{
if (empty($roles)) {
return $this->permissions()->detach();
}
return $this->permissions()->detach($roles);
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "权限管理",
"alias": "permissions",
"description": "",
"keywords": [],
"order": 2,
"services": [],
"aliases": {},
"files": [],
"requires": []
}

View File

@@ -0,0 +1,11 @@
<?php
// 角色
$router->resource('role', '\catchAdmin\permissions\controller\Role');
// 角色列表
$router->get('roles', '\catchAdmin\permissions\controller\Role@list');
$router->get('/role/get/permissions', '\catchAdmin\permissions\controller\Role@getPermissions');
// 权限
$router->resource('permission', '\catchAdmin\permissions\controller\Permission');
// 权限列表
$router->get('permissions', '\catchAdmin\permissions\controller\Permission@list');

View File

@@ -0,0 +1,22 @@
{$form|raw}
<script>
layui.use(['layer', 'form', 'admin', 'formX'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var mUser = admin.getLayerData('#permission'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
form.render();
// 回显数据
form.val('role', mUser);
// 表单提交事件
form.on('submit(submitPermission)', function (data) {
admin.req('{:url("permission")}', data.field, function (response) {
layer.msg(response.msg, {icon: 1});
admin.putLayerData('formOk', true, '#permission'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
admin.closeDialog('#permission'); // 关闭页面层弹窗
}, 'post');
return false;
});
});
</script>

View File

@@ -0,0 +1,22 @@
{$form|raw}
<script>
layui.use(['layer', 'form', 'admin', 'formX'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var mUser = admin.getLayerData('#permission'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
form.render();
// 回显数据
form.val('permission', mUser);
// 表单提交事件
form.on('submit(submitPermission)', function (data) {
admin.req('permission/'+ "{$permission_id}", data.field, function (response) {
layer.msg(response.msg, {icon: 1});
admin.putLayerData('formOk', true, '#permission'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
admin.closeDialog('#permission'); // 关闭页面层弹窗
}, 'put');
return false;
});
});
</script>

View File

@@ -0,0 +1,200 @@
{extend name="$layout"}
{block name="title"}角色管理{/block}
{block name="search"}
<div class="layui-form toolbar">
<div class="layui-form-item">
<!--<div class="layui-inline">
<div class="layui-input-inline mr0">
<input id="edtSearchAuth" class="layui-input" type="text" placeholder="输入角色名称"/>
</div>
</div>
<div class="layui-inline">
<button id="btnSearchAuth" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>-->
<button id="btnAddAuth" class="layui-btn icon-btn"><i class="layui-icon">&#xe654;</i>添加</button>
<!--</div>-->
</div>
</div>
{/block}
{block name="table"}
<table class="layui-table" id="tablePermission" lay-filter="tablePermission"></table>
<!-- 表格操作列 -->
<script type="text/html" id="tableBarAuth">
{:editButton()}
{:addButton('新增子菜单')}
{:deleteButton()}
</script>
{/block}
{block name="script"}
<script>
layui.use(['layer', 'form', 'table', 'admin', 'util', 'treeTable'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var util = layui.util;
var admin = layui.admin;
var treeTable = layui.treeTable;
var treeTb = treeTable.render({
tree: {
arrowType: 'arrow2',
iconIndex: 1, // 折叠图标显示在第几列
idName: 'id', // 自定义id字段的名称
childName: 'children', // 自定义标识是否还有子节点的字段名称
pidName: 'parent_id',
isPidData: true,
getIcon: function(d) { // 自定义图标
// d是当前行的数据
if (d.children.length) { // 判断是否有子集
return '<i class="ew-tree-icon ew-tree-icon-folder"></i>';
} else {
return '<i class="ew-tree-icon ew-tree-icon-file"></i>';
}
}
},
elem: '#tablePermission',
cellMinWidth: 100,
cols: [
{type: 'numbers', title: '#'},
{field: 'permission_name', title: '菜单名称', minWidth: 200},
{field: 'route', title: '菜单路由', templet: function (d) {
return escapeChars(d.route);
}
},
{field: 'method', title: '请求方法', width: 90},
{field: 'permission_mark', title: '权限标识'},
{field: 'type', title: '菜单类型', templet: function (d) {
return d.type == 1 ? '<button class="layui-btn layui-btn-primary layui-btn-xs">菜单</button>' :
'<button class="layui-btn layui-btn-primary layui-btn-xs">按钮</button>'
}, width: 90, align: 'center'
},
{field: 'sort', title: '排序', width:60, align: 'center'},
{
field: 'created_at', sort: true, templet: function (d) {
return util.toDateString(d.created_at);
}, title: '创建时间', maxWidth: 100
},
{templet: '#tableBarAuth', title: '操作', align: 'center', minWidth: 150}
],
reqData: function(data, callback) {
// 在这里写ajax请求通过callback方法回调数据
$.get('{:url("permissions")}', function (res) {
callback(res.data); // 参数是数组类型
});
}
});
// 添加按钮点击事件
$('#btnAddAuth').click(function () {
showEditModel();
});
function escapeChars(str) {
str = str.replace(/&/g, '&amp;');
str = str.replace(/</g, '&lt;');
str = str.replace(/>/g, '&gt;');
str = str.replace(/'/g, '&acute;');
str = str.replace(/"/g, '&quot;');
str = str.replace(/\|/g, '&brvbar;');
return str;
}
// 工具条点击事件
treeTable.on('tool(tablePermission)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') { // 删除
doDel(obj);
} else {
showEditModel(data, true);
}
});
treeTable.on('row(tablePermission)', function(obj){
console.log(obj.value); //得到修改后的值
console.log(obj.field); //当前编辑的字段名
console.log(obj.data); //所在行的所有相关数据
});
// 删除
function doDel(obj) {
layer.confirm('确定要删除“' + obj.data.permission_name + '”吗?', {
skin: 'layui-layer-admin',
shade: .1
}, function (index) {
layer.close(index);
admin.req('/permission/'+ obj.data.id, {}, function (response) {
if (response.code === 10000) {
layer.msg(response.msg, {icon: 1});
obj.del()
} else {
layer.msg(response.msg, {icon: 2});
}
}, 'delete');
});
}
// 显示表单弹窗
// 显示表单弹窗
function showEditModel(permission, addPermission= false) {
var layIndex = admin.open({
title: addPermission ? '新增子菜单' : ((permission ? '修改' : '添加') + '菜单'),
url: addPermission ? '/permission/create' + '?id='+permission.id : (permission ? '/permission/'+permission.id + '/edit': '/permission/create'),
data: addPermission ? '' : permission, // 传递数据到表单页面
end: function () {
if (admin.getLayerData(layIndex, 'formOk')) { // 判断表单操作成功标识
if (addPermission) {
treeTb.reload();
setTimeout(function () {
treeTb.expand(permission.id)
}, 200)
} else {
if (permission) {
treeTb.reload();
setTimeout(function () {
treeTb.expand(permission.id)
}, 200)
} else {
treeTb.reload(); // 成功刷新表格
}
}
}
},
success: function (layero, dIndex) {
// 弹窗超出范围不出现滚动条
$(layero).children('.layui-layer-content').css('overflow', 'visible');
}
});
}
// 搜索按钮点击事件
$('#btnSearchAuth').click(function () {
$('#edtSearchAuth').removeClass('layui-form-danger');
var keyword = $('#edtSearchAuth').val();
var $tds = $('#tableAuth').next('.treeTable').find('.layui-table-body tbody tr td');
$tds.css('background-color', 'transparent');
if (!keyword) {
layer.tips('请输入关键字', '#edtSearchAuth', {tips: [1, '#ff4c4c']});
$('#edtSearchAuth').addClass('layui-form-danger');
$('#edtSearchAuth').focus();
return;
}
var searchCount = 0;
$tds.each(function () {
if ($(this).text().indexOf(keyword) >= 0) {
$(this).css('background-color', '#FAE6A0');
if (searchCount == 0) {
$('body,html').stop(true);
$('body,html').animate({scrollTop: $(this).offset().top - 150}, 500);
}
searchCount++;
}
});
if (searchCount == 0) {
layer.msg("没有匹配结果", {icon: 5, anim: 6});
} else {
treetable.expandAll('#tableAuth');
}
});
});
</script>
{/block}

View File

@@ -0,0 +1,35 @@
{$form|raw}
<script>
layui.use(['layer', 'form', 'admin', 'formX', 'authtree'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var authtree = layui.authtree;
var mUser = admin.getLayerData('#role'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
// 回显数据
form.val('role', mUser);
// 表单提交事件
form.on('submit(submitRole)', function (data) {
admin.req('{:url("role")}', data.field, function (response) {
layer.msg(response.msg, {icon: 1});
admin.putLayerData('formOk', true, '#role'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
admin.closeDialog('#role'); // 关闭页面层弹窗
}, 'post');
return false;
});
admin.req('{:url("/role/get/permissions")}',{parent_id:"{$parent_id}"}, function (response) {
authtree.render('#permissions', response.data.permissions,{
inputname: 'permissionids[]',
layfilter: 'lay-check-auth',
autowidth: true,
nameKey: 'permission_name',
valueKey: 'id',
childKey: 'children',
collapseLeafNode: true,
theme: 'auth-skin-default',
});
})
});
</script>

View File

@@ -0,0 +1,36 @@
{$form|raw}
<script>
layui.use(['layer', 'form', 'admin', 'formX', 'authtree'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var mUser = admin.getLayerData('#role'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
var authtree = layui.authtree;
// 回显数据
form.val('role', mUser);
admin.req('{:url("/role/get/permissions")}',{role_id:"{$role_id}", parent_id:"{$parent_id}"}, function (response) {
authtree.render('#permissions', response.data.permissions,{
inputname: 'permissionids[]',
layfilter: 'lay-check-auth',
autowidth: true,
nameKey: 'permission_name',
valueKey: 'id',
childKey: 'children',
collapseLeafNode: true,
theme: 'auth-skin-default',
checkedKey: response.data.hasPermissions
});
})
// 表单提交事件
form.on('submit(submitRole)', function (data) {
admin.req('role/'+ "{$role_id}", data.field, function (response) {
layer.msg(response.msg, {icon: 1});
admin.putLayerData('formOk', true, '#role'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
admin.closeDialog('#role'); // 关闭页面层弹窗
}, 'put');
return false;
});
});
</script>

View File

@@ -0,0 +1,178 @@
{extend name="$layout"}
{block name="title"}角色管理{/block}
{block name="search"}
<div class="layui-form toolbar">
<div class="layui-form-item">
<!--<div class="layui-inline">
<div class="layui-input-inline mr0">
<input id="edtSearchAuth" class="layui-input" type="text" placeholder="输入角色名称"/>
</div>
</div>
<div class="layui-inline">
<button id="btnSearchAuth" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>-->
<button id="btnAddAuth" class="layui-btn icon-btn"><i class="layui-icon">&#xe654;</i>添加</button>
<!--</div>-->
</div>
</div>
{/block}
{block name="table"}
<table class="layui-table" id="tableRole"></table>
<!-- 表格操作列 -->
<script type="text/html" id="tableBarAuth">
{:editButton()}
{:addButton('新增子角色')}
{:deleteButton()}
</script>
{/block}
{block name="script"}
<script>
layui.use(['layer', 'form', 'table', 'admin', 'util', 'treeTable'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var util = layui.util;
var admin = layui.admin;
var treeTable = layui.treeTable;
var treeTb = treeTable.render({
tree: {
arrowType: 'arrow2',
iconIndex: 1, // 折叠图标显示在第几列
idName: 'id', // 自定义id字段的名称
childName: 'children', // 自定义标识是否还有子节点的字段名称
pidName: 'parent_id',
isPidData: true,
getIcon: function(d) { // 自定义图标
// d是当前行的数据
if (d.children.length) { // 判断是否有子集
return '<i class="ew-tree-icon ew-tree-icon-folder"></i>';
} else {
return '<i class="ew-tree-icon ew-tree-icon-file"></i>';
}
}
},
elem: '#tableRole',
cellMinWidth: 100,
cols: [
{type: 'numbers', title: '#'},
{field: 'role_name', title: '角色名称', minWidth: 100},
{field: 'description', title: '角色描述'},
{
field: 'created_at', sort: true, templet: function (d) {
return util.toDateString(d.created_at);
}, title: '创建时间', maxWidth: 100
},
{templet: '#tableBarAuth', title: '操作', align: 'center', minWidth: 120}
],
reqData: function(data, callback) {
// 在这里写ajax请求通过callback方法回调数据
$.get('{:url("roles")}', function (res) {
callback(res.data); // 参数是数组类型
});
}
});
// 添加按钮点击事件
$('#btnAddAuth').click(function () {
showEditModel();
});
// 工具条点击事件
treeTable.on('tool(tableRole)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') { // 删除
doDel(obj);
} else {
showEditModel(data, true);
}
});
// 删除
function doDel(obj) {
layer.confirm('确定要删除“' + obj.data.role_name + '”吗?', {
skin: 'layui-layer-admin',
shade: .1
}, function (index) {
layer.close(index);
admin.req('/role/'+ obj.data.id, {}, function (response) {
if (response.code === 10000) {
layer.msg(response.msg, {icon: 1});
obj.del()
} else {
layer.msg(response.msg, {icon: 2});
}
}, 'delete');
});
}
// 显示表单弹窗
// 显示表单弹窗
function showEditModel(mRole, addRole = false) {
var layIndex = admin.open({
title: addRole ? '新增子角色' : ((mRole ? '修改' : '添加') + '角色'),
url: addRole ? '/role/create' + '?id='+mRole.id : (mRole ? '/role/'+mRole.id + '/edit': '/role/create'),
data: addRole ? '' : mRole, // 传递数据到表单页面
area: '700px',
end: function () {
if (admin.getLayerData(layIndex, 'formOk')) { // 判断表单操作成功标识
if (addRole) {
treeTb.reload();
setTimeout(function () {
treeTb.expand(mRole.id)
}, 200)
} else {
if (mRole) {
treeTb.reload();
setTimeout(function () {
treeTb.expand(mRole.id)
}, 200)
} else {
treeTb.reload(); // 成功刷新表格
}
}
}
},
success: function (layero, dIndex) {
// 弹窗超出范围不出现滚动条
$(layero).children('.layui-layer-content').css('overflow', 'visible');
}
});
}
// 搜索按钮点击事件
$('#btnSearchAuth').click(function () {
$('#edtSearchAuth').removeClass('layui-form-danger');
var keyword = $('#edtSearchAuth').val();
var $tds = $('#tableAuth').next('.treeTable').find('.layui-table-body tbody tr td');
$tds.css('background-color', 'transparent');
if (!keyword) {
layer.tips('请输入关键字', '#edtSearchAuth', {tips: [1, '#ff4c4c']});
$('#edtSearchAuth').addClass('layui-form-danger');
$('#edtSearchAuth').focus();
return;
}
var searchCount = 0;
$tds.each(function () {
if ($(this).text().indexOf(keyword) >= 0) {
$(this).css('background-color', '#FAE6A0');
if (searchCount == 0) {
$('body,html').stop(true);
$('body,html').animate({scrollTop: $(this).offset().top - 150}, 500);
}
searchCount++;
}
});
if (searchCount == 0) {
layer.msg("没有匹配结果", {icon: 5, anim: 6});
} else {
treetable.expandAll('#tableAuth');
}
});
});
</script>
{/block}

View File

@@ -0,0 +1,84 @@
<?php
namespace catchAdmin\system\controller;
use app\Request;
use catcher\base\CatchController;
use catcher\CatchResponse;
use catcher\exceptions\FailedException;
use think\facade\Console;
use think\facade\Db;
use think\Paginator;
class DataDictionary extends CatchController
{
/**
*
* @time 2019年12月13日
* @throws \Exception
* @return string
*/
public function index(): string
{
return $this->fetch();
}
/**
*
* @time 2019年12月13日
* @param Request $request
* @return \think\response\Json
*/
public function tables(Request $request): \think\response\Json
{
$tables = Db::query('show table status');
return CatchResponse::paginate(Paginator::make($tables, $request->get('limit') ?? 10, $request->get('page'), count($tables), false, []));
}
/**
*
* @time 2019年12月13日
* @param $table
* @throws \Exception
* @return string
*/
public function view($table): string
{
$this->table = Db::query('show full columns from ' . $table);
return $this->fetch();
}
/**
*
* @time 2019年12月13日
* @return \think\response\Json
*/
public function optimize(): \think\response\Json
{
$tables = \request()->post('data');
foreach ($tables as $table) {
Db::query(sprintf('optimize table %s', $table));
}
return CatchResponse::success([], '优化成功');
}
/**
*
* @time 2019年12月13日
* @throws FailedException
* @return \think\response\Json
*/
public function backup(): \think\response\Json
{
try {
Console::call('backup:data', [trim(implode(',', \request()->post('data')), ',')]);
}catch (\Exception $e) {
throw new FailedException($e->getMessage());
}
return CatchResponse::success([], '备份成功');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace catchAdmin\system\controller;
use catcher\base\CatchController;
use catcher\CatchResponse;
use think\facade\Db;
class LoginLog extends CatchController
{
public function index()
{
return $this->fetch();
}
public function list()
{
return CatchResponse::paginate(Db::name('login_log')->paginate(10));
}
public function empty()
{
return CatchResponse::success(Db::name('login_log')->delete(true), '清空成功');
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace catchAdmin\system\controller;
use catcher\base\CatchController;
use catcher\CatchResponse;
use think\facade\Db;
class OperateLog extends CatchController
{
public function index()
{
return $this->fetch();
}
public function list()
{
return CatchResponse::paginate(
Db::name('operate_log')
->field(['operate_log.*', 'users.username as creator'])
->join('users','users.id = operate_log.creator_id')
->order('id', 'desc')
->paginate(10)
);
}
public function empty()
{
return CatchResponse::success(Db::name('operate_log')->delete(true), '清空成功');
}
}

View File

@@ -0,0 +1,40 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class LoginLog extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('login_log',['engine'=>'Myisam', 'comment' => '登录日志', 'signed' => false]);
$table->addColumn('login_name', 'string',['limit' => 50,'default'=>'','comment'=>'用户名'])
->addColumn('login_ip', 'string',['default'=>0, 'limit' => 20, 'comment'=>'登录地点ip', 'signed' => false])
->addColumn('browser', 'string',['default'=> '','comment'=>'浏览器'])
->addColumn('os', 'string',['default'=> '','comment'=>'操作系统'])
->addColumn('login_at', 'integer', array('default'=>0,'comment'=>'登录时间', 'signed' => false ))
->addColumn('status', 'integer',['limit' => \Phinx\Db\Adapter\MysqlAdapter::INT_TINY,'default'=> 1,'comment'=>'1 成功 2 失败'])
->create();
}
}

View File

@@ -0,0 +1,42 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class OperateLog extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('operate_log',['engine'=>'Myisam', 'comment' => '操作日志', 'signed' => false]);
$table->addColumn('module', 'string',['limit' => 50,'default'=>'','comment'=>'模块名称'])
->addColumn('operate', 'string',['default'=> '', 'limit' => 20, 'comment'=>'操作模块'])
->addColumn('route', 'string',['default'=> '','limit' => 20, 'comment'=>'路由'])
->addColumn('params', 'string',['default'=> '','limit' => 1000, 'comment'=>'参数'])
->addColumn('ip', 'string',['default'=>'', 'limit' => 20,'comment'=>'ip', 'signed' => false])
->addColumn('creator_id', 'integer',['default'=> 0,'comment'=>'创建人ID', 'signed' => false])
->addColumn('method', 'string',['default'=> '','comment'=>'请求方法'])
->addColumn('created_at', 'integer', array('default'=>0,'comment'=>'登录时间', 'signed' => false ))
->create();
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace catchAdmin\system\event;
class LoginLogEvent
{
protected $params;
public function __construct(array $params)
{
$this->params = $params;
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace catchAdmin\system\event;
class OperateLogEvent
{
protected $params;
public function __construct(array $params)
{
$this->params = $params;
}
}

11
catch/system/module.json Normal file
View File

@@ -0,0 +1,11 @@
{
"name": "系统管理",
"alias": "system",
"description": "",
"keywords": [],
"order": 2,
"services": [],
"aliases": {},
"files": [],
"requires": []
}

18
catch/system/route.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
// 登录日志
$router->get('log/login', '\catchAdmin\system\controller\LoginLog@list');
$router->get('loginLog/index', '\catchAdmin\system\controller\LoginLog@index');
$router->delete('loginLog/empty', '\catchAdmin\system\controller\LoginLog@empty');
// 操作日志
$router->get('log/operate', '\catchAdmin\system\controller\OperateLog@list');
$router->get('operateLog/index', '\catchAdmin\system\controller\OperateLog@index');
$router->delete('operateLog/empty', '\catchAdmin\system\controller\OperateLog@empty');
// 数据字典
$router->get('data/dictionary', '\catchAdmin\system\controller\DataDictionary@index');
$router->get('tables', '\catchAdmin\system\controller\DataDictionary@tables');
$router->get('table/view/<table>', '\catchAdmin\system\controller\DataDictionary@view');
$router->post('table/optimize', '\catchAdmin\system\controller\DataDictionary@optimize');
$router->post('table/backup', '\catchAdmin\system\controller\DataDictionary@backup');

View File

@@ -0,0 +1,113 @@
{extend name="$layout"}
{block name="title"}登录日志{/block}
{block name="search"}
<div class="layui-form toolbar">
<div class="layui-form-item">
<!--<div class="layui-inline">
<div class="layui-input-inline mr0">
<input id="edtSearchAuth" class="layui-input" type="text" placeholder="输入角色名称"/>
</div>
</div>
<div class="layui-inline">
<button id="btnSearchAuth" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>-->
<button id="optimize" class="layui-btn icon-btn"><i class="layui-icon">&#xe631;</i>优化</button>
<button id="backup" class="layui-btn layui-btn-normal icon-btn"><i class="layui-icon">&#xe620;</i>备份</button>
<!--</div>-->
</div>
</div>
{/block}
{block name="table"}
<table class="layui-table" id="database" lay-filter="database"></table>
<!-- 表格操作列 -->cl
<script type="text/html" id="operate">
{:editButton('查看', 'view')}
</script>
{/block}
{block name="script"}
<script>
layui.use(['layer', 'table', 'util', 'admin'], function () {
var $ = layui.jquery;
var table = layui.table;
var admin = layui.admin;
table.render({
elem: '#database',
url: '{:url("tables")}',
page: true,
response: {
statusCode: 10000,
},
// toolbar: true,
cellMinWidth: 100,
cols: [[
{type: 'checkbox'},
{field: 'Name', title: '表名'},
{field: 'Engine', title: '表引擎'},
{field: 'Collation', title: '数据集'},
{field: 'Rows',title: '数据行数'},
{field: 'Index_length', title: '索引大小', templet: function (d) {
if (d.Index_length < (1024 * 1024)) {
return Math.round(d.Index_length / 1024) + 'KB'
} else {
return Math.round(d.Index_length/(1024 * 1024)) + 'MB'
}
}
},
{field: 'Data_length', title: '数据大小', templet: function (d) {
if (d.Data_length < (1024 * 1024)) {
return Math.round(d.Data_length / 1024) + 'KB'
} else {
return Math.round(d.Data_length/(1024 * 1024)) + 'MB'
}
}
},
{field: 'Create_time', title: '创建时间'},
{field: 'Comment', title: '表注释'},
{align: 'center', toolbar: '#operate', title: '操作'}
]],
});
// 工具条点击事件
table.on('tool(database)', function (obj) {
if (obj.event === 'view') { // 查看
admin.open({
title: '查看表结构',
url: '/table/view/'+obj.data.Name,
area: '900px',
});
}
});
$('#optimize').click(function () {
var checkRows = table.checkStatus('database');
if (checkRows.data.length == 0) {
layer.msg('选择需要优化的表', {icon: 2});
} else {
var name = [];
checkRows.data.map(function(item,index){
name.push(item.Name)
});
admin.req("{:url('table/optimize')}", {data:name},function (response) {
layer.msg(response.msg, {icon: 1})
}, 'post')
}
})
$('#backup').click(function () {
var checkRows = table.checkStatus('database');
if (checkRows.data.length == 0) {
layer.msg('选择需要备份的表', {icon: 2});
} else {
var name = [];
checkRows.data.map(function(item,index){
name.push(item.Name)
});
admin.req("{:url('table/backup')}", {data:name},function (response) {
layer.msg(response.msg, {icon: response.code === 10000 ? 1 : 2})
}, 'post')
}
})
});
</script>
{/block}

View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div style="padding: 10px">
<table class="layui-table">
<thead>
<tr>
<th>字段</th>
<th>类型</th>
<th>字符集</th>
<th>Null</th>
<th>索引</th>
<th>默认值</th>
<th>权限</th>
<th>注释</th>
</tr>
</thead>
<tbody>
{foreach $table as $field}
<tr>
<td>{$field['Field']}</td>
<td>{$field['Type']}</td>
<td>{$field['Collation']}</td>
<td>{$field['Null']}</td>
<td>{$field['Key']}</td>
<td>{$field['Default']}</td>
<td>{$field['Privileges']}</td>
<td>{$field['Comment']}</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -0,0 +1,68 @@
{extend name="$layout"}
{block name="title"}登录日志{/block}
{block name="search"}
<div class="layui-form toolbar">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline mr0">
<input id="edtSearchAuth" class="layui-input" type="text" placeholder="输入角色名称"/>
</div>
</div>
<div class="layui-inline">
<button id="btnSearchAuth" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>
<button id="empty" class="layui-btn icon-btn"><i class="layui-icon">&#xe640;</i>清空</button>
</div>
</div>
</div>
{/block}
{block name="table"}
<table class="layui-table" id="log"></table>
{/block}
{block name="script"}
<script>
layui.use(['layer', 'form', 'table', 'util', 'admin'], function () {
var $ = layui.jquery;
var table = layui.table;
var util = layui.util;
var admin = layui.admin;
var t = table.render({
elem: '#log',
url: '{:url("log/login")}',
page: true,
response: {
statusCode: 10000,
},
// toolbar: true,
cellMinWidth: 100,
cols: [[
{type: 'id', title: '序号', field: 'id'},
{field: 'login_name', title: '登录名'},
{field: 'login_ip', title: '登录IP'},
{field: 'browser', title: '浏览器'},
{field: 'os', title: '操作系统'},
{field: 'status', title: '状态', templet: function (d) {
return d.status === 1 ?
'<button class="layui-btn layui-btn-xs">成功</button>' :
'<button class="layui-btn layui-btn-xs layui-btn-danger">失败</button>'
}
},
{
field: 'login_at', templet: function (d) {
return util.toDateString(d.login_at);
}, title: '登录时间'
},
]],
});
$('#empty').click(function () {
layer.confirm('确定清空日志吗?', function () {
admin.req("{:url('loginLog/empty')}", {}, function (response) {
layer.msg(response.msg, {icon:1});
t.reload();
}, 'delete')
})
})
});
</script>
{/block}

View File

@@ -0,0 +1,63 @@
{extend name="$layout"}
{block name="title"}操作日志{/block}
{block name="search"}
<div class="layui-form toolbar">
<div class="layui-form-item">
<!--<div class="layui-inline">
<div class="layui-input-inline mr0">
<input id="edtSearchAuth" class="layui-input" type="text" placeholder="输入角色名称"/>
</div>
</div>
<div class="layui-inline">
<button id="btnSearchAuth" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>-->
<button id="empty" class="layui-btn icon-btn"><i class="layui-icon">&#xe640;</i>清空</button>
<!--</div>-->
</div>
</div>
{/block}
{block name="table"}
<table class="layui-table" id="log"></table>
{/block}
{block name="script"}
<script>
layui.use(['layer', 'form', 'table', 'util', 'admin'], function () {
var $ = layui.jquery;
var table = layui.table;
var util = layui.util;
var admin = layui.admin;
var t = table.render({
elem: '#log',
url: '{:url("log/operate")}',
page: true,
response: {
statusCode: 10000,
},
// toolbar: true,
cellMinWidth: 100,
cols: [[
{type: 'id', title: '序号', field: 'id'},
{field: 'module', title: '模块'},
{field: 'ip', title: 'IP'},
{field: 'operate', title: 'operate'},
{field: 'creator', title: '操作者'},
{field: 'method', title: '请求方法'},
{
field: 'created_at', sort: true, templet: function (d) {
return util.toDateString(d.created_at);
}, title: '创建时间'
},
]],
});
$('#empty').click(function () {
layer.confirm('确定清空日志吗?', function () {
admin.req("{:url('operateLog/empty')}", {}, function (response) {
layer.msg(response.msg, {icon:1});
t.reload();
}, 'delete')
})
})
});
</script>
{/block}

121
catch/user/Auth.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
namespace catchAdmin\user;
use catchAdmin\permissions\model\Permissions;
use catchAdmin\permissions\model\Roles;
use catchAdmin\user\model\Users;
use catcher\exceptions\LoginFailedException;
use catcher\Tree;
use thans\jwt\facade\JWTAuth;
use think\facade\Session;
class Auth
{
protected const USER_ID = 'catch_uid';
/**
* 登陆
*
* @time 2019年11月28日
* @param $params
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws LoginFailedException
*/
public static function login($params)
{
$user = Users::where('email', $params['email'])->find();
if (!$user) {
throw new LoginFailedException('登陆失败, 请检查用户名和密码');
}
if (!password_verify($params['password'], $user->password)) {
throw new LoginFailedException('登陆失败, 请检查用户名和密码');
}
if ($user->status == Users::DISABLE) {
throw new LoginFailedException('该用户已被禁用');
}
// 记录用户登录
$user->last_login_ip = request()->ip();
$user->last_login_time = time();
$user->save();
// Session::set(self::getLoginUserKey(), $user);
return JWTAuth::builder([self::USER_ID => $user->id]);
}
/**
* 退出登陆
*
* @time 2019年11月28日
* @return bool
*/
public static function logout(): bool
{
Session::delete(self::getLoginUserKey());
return true;
}
/**
*
* @time 2019年12月15日
* @return mixed
*/
public static function user()
{
$user = Users::where('id', JWTAuth::auth()[self::USER_ID])
->field(['id', 'username', 'status'])->find();
return $user;
}
public static function getUserInfo()
{
$user = self::user();
$roles = $user->getRoles();
foreach ($roles as &$role) {
$role['permissions'] = Roles::where('id', $role['id'])->find()->getPermissions([
'type' => Permissions::MENU_TYPE
], ['permission_name', 'route']);
}
$user->roles = $roles;
return $user;
}
/**
*
* @time 2019年12月15日
* @return string
*/
protected static function getLoginUserKey(): string
{
// return md5(self::USER_KEY);
}
/**
*
* @time 2019年12月15日
* @param $mark
* @param $module
* @return bool
*/
public static function hasPermissions($mark, $module): bool
{
$permissionIds = self::user()->get->getPermissionsBy();
$permissionId = Permissions::where('module', $module)
->where('permission_mark', $mark)->value('id');
return in_array($permissionId, $permissionIds);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace catchAdmin\user;
use catcher\Code;
use catcher\exceptions\FailedException;
use thans\jwt\exception\TokenBlacklistException;
use thans\jwt\exception\TokenExpiredException;
use thans\jwt\exception\TokenInvalidException;
use thans\jwt\facade\JWTAuth;
use think\Middleware;
class AuthTokenMiddleware extends Middleware
{
public function handle($request, \Closure $next)
{
try {
JWTAuth::auth();
} catch (\Exception $e) {
if ($e instanceof TokenExpiredException) {
throw new FailedException('token 过期', Code::LOST_LOGIN);
}
if ($e instanceof TokenBlacklistException) {
throw new FailedException('token 被加入黑名单', Code::LOST_LOGIN);
}
if ($e instanceof TokenInvalidException) {
throw new FailedException('token 不合法', Code::LOST_LOGIN);
}
throw new FailedException('登录用户不合法', Code::LOST_LOGIN);
}
return $next($request);
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace catchAdmin\user\controller;
use app\Request;
use catchAdmin\permissions\model\Roles;
use catchAdmin\user\Auth;
use catchAdmin\user\model\Users;
use catchAdmin\user\request\CreateRequest;
use catchAdmin\user\request\UpdateRequest;
use catcher\base\CatchController;
use catcher\CatchForm;
use catcher\CatchResponse;
use catcher\Tree;
class User extends CatchController
{
protected $user;
public function __construct(Users $user)
{
$this->user = $user;
}
/**
*
* @time 2019年12月04日
* @param Request $request
* @return string
* @throws \think\db\exception\DbException
*/
public function index(Request $request)
{
return CatchResponse::paginate($this->user->getList($request->param()));
}
public function info()
{
return CatchResponse::success(Auth::getUserInfo());
}
/**
*
* @time 2019年12月06日
* @throws \Exception
* @return string
*/
public function create()
{
$form = new CatchForm();
$form->formId('userForm');
$form->text('username', '用户名', true)->verify('required')->placeholder('请输入用户名');
$form->text('email', '邮箱', true)->verify('email')->placeholder('请输入邮箱');
$form->password('password', '密码', true)->id('pwd')->verify('required|psw')->placeholder('请输入密码');
$form->password('passwordConfirm', '确认密码', true)->verify('required|equalTo', ['pwd', '两次密码输入不一致'])->placeholder('请再次输入密码');
$form->dom('<div id="roles"></div>', '角色');
$form->formBtn('submitUser');
return $this->fetch([
'form' => $form->render(),
]);
}
/**
*
* @param CreateRequest $request
* @time 2019年12月06日
* @return Json
*/
public function save(CreateRequest $request)
{
$this->user->storeBy($request->post());
if (!empty($request->param('roleids'))) {
$this->user->attach($request->param('roleids'));
}
return CatchResponse::success();
}
/**
*
* @time 2019年12月04日
* @param $id
* @return Json
*/
public function read($id)
{
return CatchResponse::success($this->user->findBy($id));
}
/**
* @param $id
* @return string
* @throws \Exception
*/
public function edit($id)
{
$user = $this->user->findBy($id, ['id','username', 'email']);
$form = new CatchForm();
$form->formId('userForm');
$form->text('username', '用户名', true)->verify('required')->default($user->username)->placeholder('请输入用户名');
$form->text('email', '邮箱', true)->verify('email')->default($user->email)->placeholder('请输入邮箱');
$form->password('password', '密码')->id('pwd')->placeholder('请输入密码');
$form->password('passwordConfirm', '确认密码')->verify('equalTo', ['pwd', '两次密码输入不一致'])->placeholder('请再次输入密码');
$form->dom('<div id="roles"></div>', '角色');
$form->formBtn('submitUser');
return $this->fetch([
'form' => $form->render(),
'uid' => $user->id,
]);
}
/**
*
* @time 2019年12月04日
* @param $id
* @param UpdateRequest $request
* @return Json
*/
public function update($id, UpdateRequest $request)
{
$this->user->updateBy($id, $request->post());
$user = $this->user->findBy($id);
$user->detach();
if (!empty($request->param('roleids'))) {
$user->attach($request->param('roleids'));
}
return CatchResponse::success();
}
/**
*
* @time 2019年12月04日
* @param $id
* @return Json
*/
public function delete($id)
{
// 删除角色
$this->user->findBy($id)->detach();
$this->user->deleteBy($id);
return CatchResponse::success();
}
/**
*
* @time 2019年12月07日
* @param $id
* @return Json
*/
public function switchStatus($id): Json
{
$user = $this->user->findBy($id);
return CatchResponse::success($this->user->updateBy($id, [
'status' => $user->status == Users::ENABLE ? Users::DISABLE : Users::ENABLE,
]));
}
/**
*
* @time 2019年12月07日
* @param $id
* @return Json
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\db\exception\DataNotFoundException
*/
public function recover($id): Json
{
$trashedUser = $this->user->findBy($id, ['*'], true);
if ($this->user->where('email', $trashedUser->email)->find()) {
return CatchResponse::fail(sprintf('该恢复用户的邮箱 [%s] 已被占用', $trashedUser->email));
}
return CatchResponse::success($this->user->recover($id));
}
/**
*
* @time 2019年12月11日
* @param Request $request
* @param Roles $roles
* @return \think\response\Json
*/
public function getRoles(Request $request, Roles $roles): \think\response\Json
{
$roles = Tree::done($roles->getList());
$roleIds = [];
if ($request->param('uid')) {
$userHasRoles = $this->user->findBy($request->param('uid'))->getRoles();
foreach ($userHasRoles as $role) {
$roleIds[] = $role->pivot->role_id;
}
}
return CatchResponse::success([
'roles' => $roles,
'hasRoles' => $roleIds,
]);
}
}

View File

@@ -0,0 +1,43 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class Users extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('users',array('engine'=>'Innodb', 'comment' => '用户表', 'signed' => false));
$table->addColumn('username', 'string',array('limit' => 15,'default'=>'','comment'=>'用户名'))
->addColumn('password', 'string',array('limit' => 255,'comment'=>'用户密码'))
->addColumn('email', 'string',array('limit' => 100, 'comment'=>'邮箱 登录'))
->addColumn('status', 'boolean',array('limit' => 1,'default'=> 1,'comment'=>'用户状态 1 正常 2 禁用'))
->addColumn('last_login_ip', 'string',array('limit' => 30,'default'=>0,'comment'=>'最后登录IP'))
->addColumn('last_login_time', 'integer',array('default'=>0,'comment'=>'最后登录时间', 'signed' => false))
->addColumn('created_at', 'integer', array('default'=>0,'comment'=>'创建时间', 'signed' => false ))
->addColumn('updated_at', 'integer', array('default'=>0,'comment'=>'更新时间', 'signed' => false))
->addColumn('deleted_at', 'integer', array('default'=>0,'comment'=>'删除状态0未删除 >0 已删除', 'signed' => false))
->create();
}
}

View File

@@ -0,0 +1,23 @@
<?php
use think\migration\Seeder;
class UsersSeed extends Seeder
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* http://docs.phinx.org/en/latest/seeding.html
*/
public function run()
{
return \catchAdmin\user\model\Users::create([
'username' => 'admin',
'password' => 'admin',
'email' => 'admin@gmail.com',
]);
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace catchAdmin\user\model;
use catchAdmin\permissions\model\HasRolesTrait;
use catcher\base\CatchModel;
class Users extends CatchModel
{
use HasRolesTrait;
protected $name = 'users';
protected $field = [
'id', //
'username', // 用户名
'password', // 用户密码
'email', // 邮箱 登录
'status', // 用户状态 1 正常 2 禁用
'last_login_ip', // 最后登录IP
'last_login_time', // 最后登录时间
'created_at', // 创建时间
'updated_at', // 更新时间
'deleted_at', // 删除状态0未删除 >0 已删除
];
/**
* set password
*
* @time 2019年12月07日
* @param $value
* @return false|string
*/
public function setPasswordAttr($value)
{
return password_hash($value, PASSWORD_DEFAULT);
}
/**
* 用户列表
*
* @time 2019年12月08日
* @param $search
* @throws \think\db\exception\DbException
* @return \think\Paginator
*/
public function getList($search): \think\Paginator
{
return (($search['trash'] ?? false) ? static::onlyTrashed() : $this)
->field(['id', 'username', 'email', 'status','last_login_time','last_login_ip', 'created_at', 'updated_at'])
->when($search['username'] ?? false, function ($query) use ($search){
return $query->whereLike('username', $search['username']);
})
->when($search['email'] ?? false, function ($query) use ($search){
return $query->whereLike('email', $search['email']);
})
->when($search['status'] ?? false, function ($query) use ($search){
return $query->where('status', $search['status']);
})->paginate($search['limit'] ?? $this->limit);
}
/**
* 获取权限
*
* @time 2019年12月12日
* @param $uid
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @return array
*/
public function getPermissionsBy($uid = 0): array
{
$roles = $uid ? $this->findBy($uid)->getRoles() : $this->getRoles();
$permissionIds = [];
foreach ($roles as $role) {
$permissionIds = array_merge($permissionIds, $role->getPermissions()->column('id'));
}
return array_unique($permissionIds);
}
}

13
catch/user/module.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "用户管理",
"alias": "user",
"description": "",
"keywords": [],
"order": 2,
"services": [
"catchAdmin\\user\\UserService"
],
"aliases": {},
"files": [],
"requires": []
}

View File

@@ -0,0 +1,25 @@
<?php
namespace catchAdmin\user\request;
use catchAdmin\user\model\Users;
use catcher\base\CatchRequest;
class CreateRequest extends CatchRequest
{
protected function rules(): array
{
// TODO: Implement rules() method.
return [
'username|用户名' => 'require|max:20',
'password|密码' => 'require|min:5|max:12',
'passwordConfirm|密码' => 'confirm:password',
'email|邮箱' => 'require|email|unique:'.Users::class,
];
}
protected function message(): array
{
// TODO: Implement message() method.
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace catchAdmin\user\request;
use catchAdmin\user\model\Users;
use catcher\base\CatchRequest;
class UpdateRequest extends CatchRequest
{
protected function rules(): array
{
// TODO: Implement rules() method.
return [
'username|用户名' => 'require|max:20',
'password|密码' => 'sometimes|min:5|max:12',
'passwordConfirm|密码' => 'sometimes|confirm:password',
'email|邮箱' => 'require|email|unique:'.Users::class,
];
}
protected function message(): array
{
// TODO: Implement message() method.
}
}

7
catch/user/route.php Normal file
View File

@@ -0,0 +1,7 @@
<?php
$router->resource('user', '\catchAdmin\user\controller\User');
// 切换状态
$router->put('user/switch/status/<id>', '\catchAdmin\user\controller\User@switchStatus');
$router->put('user/recover/<id>', '\catchAdmin\user\controller\User@recover');
$router->get('user/get/roles', '\catchAdmin\user\controller\User@getRoles');

View File

@@ -0,0 +1,38 @@
{$form|raw}
<script>
layui.use(['layer', 'form', 'admin', 'formX','authtree'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var authtree = layui.authtree;
var mUser = admin.getLayerData('#userForm'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
// 回显数据
form.val('userForm', mUser);
// 表单提交事件
form.on('submit(submitUser)', function (data) {
var url = mUser ? '{:url("user")}' : '{:url("user")}';
admin.req(url, data.field, function (response) {
layer.msg(response.msg, {icon: 1});
admin.putLayerData('formOk', true, '#userForm'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
admin.closeDialog('#userForm'); // 关闭页面层弹窗
}, 'post');
return false;
});
admin.req('{:url("/user/get/roles")}',{}, function (response) {
authtree.render('#roles', response.data.roles,{
inputname: 'roleids[]',
layfilter: 'lay-check-auth',
autowidth: true,
nameKey: 'role_name',
valueKey: 'id',
childKey: 'children',
collapseLeafNode: true,
theme: 'auth-skin-default',
autochecked: false,
autoclose: false,
});
});
});
</script>

40
catch/user/view/edit.html Normal file
View File

@@ -0,0 +1,40 @@
{$form|raw}
<script>
layui.use(['layer', 'form', 'admin', 'formX', 'authtree'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var authtree = layui.authtree;
var mUser = admin.getLayerData('#userForm'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
// 回显数据
form.val('userForm', mUser);
var uid = "{$uid}";
admin.req('{:url("/user/get/roles")}',{uid:uid}, function (response) {
authtree.render('#roles', response.data.roles, {
inputname: 'roleids[]',
primaryKey: 'id',
parentKey: 'parent_id',
layfilter: 'lay-check-auth',
autowidth: true,
nameKey: 'role_name',
valueKey: 'id',
childKey: 'children',
collapseLeafNode: true,
theme: 'auth-skin-default',
checkedKey: response.data.hasRoles,
autochecked: false,
autoclose: false,
});
});
// 表单提交事件
form.on('submit(submitUser)', function (data) {
admin.req('/user/' + uid, data.field, function (response) {
layer.msg(response.msg, {icon: 1});
admin.putLayerData('formOk', true, '#userForm'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
admin.closeDialog('#userForm'); // 关闭页面层弹窗
}, 'put');
return false;
});
});
</script>

194
catch/user/view/index.html Normal file
View File

@@ -0,0 +1,194 @@
{extend name="$layout"}
{block name="title"}用户管理{/block}
{block name="search"}
<div class="layui-form toolbar">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label w-auto">用户名:</label>
<div class="layui-input-inline">
<input name="username" class="layui-input" type="text" placeholder="输入用户名"/>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label w-auto">邮箱:</label>
<div class="layui-input-inline mr0">
<input name="email" class="layui-input" type="text" placeholder="输入邮箱"/>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label w-auto">状态:</label>
<div class="layui-input-inline mr0">
<select name="status">
<option value="">选择状态</option>
<option value="1">正常</option>
<option value="2">禁用</option>
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label w-auto">回收站:</label>
<div class="layui-input-inline mr0">
<select name="trash">
<option value="">选择</option>
<option value="1">恢复数据</option>
</select>
</div>
</div>
<div class="layui-inline" style="padding-right: 110px;">
<button class="layui-btn icon-btn" lay-filter="formSubSearchUser" lay-submit>
<i class="layui-icon">&#xe615;</i>搜索
</button>
<button id="btnAddUser" class="layui-btn icon-btn"><i class="layui-icon">&#xe654;</i>添加</button>
</div>
</div>
</div>
{/block}
{block name="table"}
<table class="layui-table" id="tableUser" lay-filter="tableUser"></table>
<!-- 表格操作列 -->
<script type="text/html" id="tableBarUser">
{:editButton()}
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="{{d.deleted_at ? 'recover' : 'del'}}">{{d.deleted_at ? '恢复' : '删除'}}</a>
</script>
<!-- 表格状态列 -->
<script type="text/html" id="tableStateUser">
<input type="checkbox" lay-filter="ckStateUser" value="{{d.id}}" lay-skin="switch"
lay-text="正常|禁用" {{d.status==1?'checked':''}}/>
</script>
{/block}
{block name="script"}
<script>
layui.use(['layer', 'form', 'table', 'util', 'admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var util = layui.util;
var admin = layui.admin;
// 渲染表格
var insTb = table.render({
elem: '#tableUser',
url: '{:url("users")}',
page: true,
response: {
statusCode: 10000,
},
toolbar: true,
cellMinWidth: 100,
cols: [[
{type: 'id', title: '#', field: 'id'},
{field: 'username', sort: true, title: '用户名'},
{field: 'email', sort: true, title: '邮箱'},
{field: 'status', sort: true, title: '状态', templet: '#tableStateUser'},
{
field: 'created_at', sort: true, templet: function (d) {
return util.toDateString(d.created_at);
}, title: '创建时间'
},
{
field: 'updated_at', sort: true, templet: function (d) {
return util.toDateString(d.updated_at);
}, title: '更新时间'
},
{align: 'center', toolbar: '#tableBarUser', title: '操作', minWidth: 200}
]],
});
// 添加
$('#btnAddUser').click(function () {
showEditModel();
});
// 搜索
form.on('submit(formSubSearchUser)', function (data) {
insTb.reload({where: data.field}, 'data');
});
// 工具条点击事件
table.on('tool(tableUser)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') { // 删除
doDel(data.id, data.username);
} else if (layEvent === 'reset') { // 重置密码
resetPsw(data.id, data.username);
} else if (layEvent === 'recover') {
recover(data.id, data.username);
}
});
function recover(uid, username) {
layer.confirm('确定要恢复“' + username + '”吗?', {
skin: 'layui-layer-admin',
shade: .1
}, function (i) {
layer.close(i);
admin.req('/user/recover/'+ uid,{}, function(res){
layer.closeAll('loading');
if (res.code == 10000) {
layer.msg(res.msg, {icon: 1});
insTb.reload({}, 'data');
} else {
layer.msg(res.msg, {icon: 2});
}
}, 'put');
});
}
// 显示表单弹窗
function showEditModel(mUser) {
var layIndex = admin.open({
title: (mUser ? '修改' : '添加') + '用户',
url: mUser ? '/user/'+mUser.id + '/edit':'/user/create',
data: mUser, // 传递数据到表单页面
area: '500px',
end: function () {
if (admin.getLayerData(layIndex, 'formOk')) { // 判断表单操作成功标识
insTb.reload(); // 成功刷新表格
}
},
success: function (layero, dIndex) {
// 弹窗超出范围不出现滚动条
$(layero).children('.layui-layer-content').css('overflow', 'visible');
}
});
}
// 删除
function doDel(userId, nickName) {
layer.confirm('确定要删除“' + nickName + '”吗?', {
skin: 'layui-layer-admin',
shade: .1
}, function (i) {
layer.close(i);
admin.req('/user/'+ userId,{}, function(res){
layer.closeAll('loading');
if (res.code == 10000) {
layer.msg(res.msg, {icon: 1});
insTb.reload({}, 'data');
} else {
layer.msg(res.msg, {icon: 2});
}
}, 'delete');
});
}
// 修改用户状态
form.on('switch(ckStateUser)', function (obj) {
admin.req('/user/switch/status/'+obj.value,{}, function(res){
layer.closeAll('loading');
if (res.code == 10000) {
layer.msg(res.msg, {icon: 1});
} else {
layer.msg(res.msg, {icon: 2});
$(obj.elem).prop('checked', !obj.elem.checked);
form.render('checkbox');
}
}, 'put');
});
});
</script>
{/block}