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

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}