用户管理
This commit is contained in:
parent
ae7fd47a5d
commit
22064c6178
1
catchAdmin/permissions/PermissionsMiddleware.php
Normal file
1
catchAdmin/permissions/PermissionsMiddleware.php
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?php
|
26
catchAdmin/permissions/view/permissions/create.html
Normal file
26
catchAdmin/permissions/view/permissions/create.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{$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('#role'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
||||||
|
// 回显数据
|
||||||
|
form.val('role', mUser);
|
||||||
|
// 表单提交事件
|
||||||
|
form.on('submit(submitRole)', function (data) {
|
||||||
|
admin.req('{:url("role")}', data.field, function (response) {
|
||||||
|
layer.closeAll('loading');
|
||||||
|
if (response.code === 10000) {
|
||||||
|
layer.msg(response.msg, {icon: 1});
|
||||||
|
admin.putLayerData('formOk', true, '#role'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
||||||
|
admin.closeDialog('#role'); // 关闭页面层弹窗
|
||||||
|
} else {
|
||||||
|
layer.msg(response.msg, {icon: 2});
|
||||||
|
}
|
||||||
|
}, 'post');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
25
catchAdmin/permissions/view/permissions/edit.html
Normal file
25
catchAdmin/permissions/view/permissions/edit.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{$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('#role'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
||||||
|
// 回显数据
|
||||||
|
form.val('role', mUser);
|
||||||
|
// 表单提交事件
|
||||||
|
form.on('submit(submitRole)', function (data) {
|
||||||
|
admin.req('role/'+ "{$role_id}", data.field, function (response) {
|
||||||
|
if (response.code === 10000) {
|
||||||
|
layer.msg(response.msg, {icon: 1});
|
||||||
|
admin.putLayerData('formOk', true, '#role'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
||||||
|
admin.closeDialog('#role'); // 关闭页面层弹窗
|
||||||
|
} else {
|
||||||
|
layer.msg(response.msg, {icon: 2});
|
||||||
|
}
|
||||||
|
}, 'put');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
178
catchAdmin/permissions/view/permissions/index.html
Normal file
178
catchAdmin/permissions/view/permissions/index.html
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
{extend name="../../../view/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"></i>搜索
|
||||||
|
</button>-->
|
||||||
|
<button id="btnAddAuth" class="layui-btn icon-btn"><i class="layui-icon"></i>添加</button>
|
||||||
|
<!--</div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
||||||
|
{block name="table"}
|
||||||
|
<table class="layui-table" id="tableRole"></table>
|
||||||
|
<!-- 表格操作列 -->
|
||||||
|
<script type="text/html" id="tableBarAuth">
|
||||||
|
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a>
|
||||||
|
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="add">新增子角色</a>
|
||||||
|
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||||
|
</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, // 传递数据到表单页面
|
||||||
|
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}
|
10
catchAdmin/permissions/view/roles/edit.html
Normal file
10
catchAdmin/permissions/view/roles/edit.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -2,14 +2,16 @@
|
|||||||
namespace catchAdmin\user\controller;
|
namespace catchAdmin\user\controller;
|
||||||
|
|
||||||
use app\Request;
|
use app\Request;
|
||||||
|
use catchAdmin\permissions\model\Roles;
|
||||||
use catchAdmin\user\model\Users;
|
use catchAdmin\user\model\Users;
|
||||||
use catchAdmin\user\request\CreateRequest;
|
use catchAdmin\user\request\CreateRequest;
|
||||||
use catchAdmin\user\request\UpdateRequest;
|
use catchAdmin\user\request\UpdateRequest;
|
||||||
use catcher\base\BaseController;
|
use catcher\base\CatchController;
|
||||||
use catcher\CatchForm;
|
use catcher\CatchForm;
|
||||||
use catcher\CatchResponse;
|
use catcher\CatchResponse;
|
||||||
|
use catcher\Tree;
|
||||||
|
|
||||||
class User extends BaseController
|
class User extends CatchController
|
||||||
{
|
{
|
||||||
protected $user;
|
protected $user;
|
||||||
|
|
||||||
@ -49,6 +51,7 @@ class User extends BaseController
|
|||||||
$form->text('email', '邮箱')->verify('email')->placeholder('请输入邮箱');
|
$form->text('email', '邮箱')->verify('email')->placeholder('请输入邮箱');
|
||||||
$form->password('password', '密码')->id('pwd')->verify('required|psw')->placeholder('请输入密码');
|
$form->password('password', '密码')->id('pwd')->verify('required|psw')->placeholder('请输入密码');
|
||||||
$form->password('passwordConfirm', '确认密码')->verify('required|equalTo', ['pwd', '两次密码输入不一致'])->placeholder('请再次输入密码');
|
$form->password('passwordConfirm', '确认密码')->verify('required|equalTo', ['pwd', '两次密码输入不一致'])->placeholder('请再次输入密码');
|
||||||
|
$form->dom('<div id="roles"></div>', '角色');
|
||||||
$form->formBtn('submitUser');
|
$form->formBtn('submitUser');
|
||||||
|
|
||||||
return $this->fetch([
|
return $this->fetch([
|
||||||
@ -64,7 +67,12 @@ class User extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function save(CreateRequest $request)
|
public function save(CreateRequest $request)
|
||||||
{
|
{
|
||||||
return CatchResponse::success($this->user->storeBy($request->post()));
|
$uid = $this->user->storeBy($request->post());
|
||||||
|
|
||||||
|
if (!empty($request->param('roleids'))) {
|
||||||
|
$this->user->attach($request->param('roleids'));
|
||||||
|
}
|
||||||
|
return CatchResponse::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,6 +96,7 @@ class User extends BaseController
|
|||||||
$form->text('email', '邮箱')->verify('email')->default($user->email)->placeholder('请输入邮箱');
|
$form->text('email', '邮箱')->verify('email')->default($user->email)->placeholder('请输入邮箱');
|
||||||
$form->password('password', '密码')->id('pwd')->placeholder('请输入密码');
|
$form->password('password', '密码')->id('pwd')->placeholder('请输入密码');
|
||||||
$form->password('passwordConfirm', '确认密码')->verify('equalTo', ['pwd', '两次密码输入不一致'])->placeholder('请再次输入密码');
|
$form->password('passwordConfirm', '确认密码')->verify('equalTo', ['pwd', '两次密码输入不一致'])->placeholder('请再次输入密码');
|
||||||
|
$form->dom('<div id="roles"></div>', '角色');
|
||||||
$form->formBtn('submitUser');
|
$form->formBtn('submitUser');
|
||||||
|
|
||||||
return $this->fetch([
|
return $this->fetch([
|
||||||
@ -152,4 +161,29 @@ class User extends BaseController
|
|||||||
|
|
||||||
return CatchResponse::success($this->user->recover($id));
|
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,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,9 +2,9 @@
|
|||||||
namespace catchAdmin\user\model;
|
namespace catchAdmin\user\model;
|
||||||
|
|
||||||
use catchAdmin\permissions\model\HasRolesTrait;
|
use catchAdmin\permissions\model\HasRolesTrait;
|
||||||
use catcher\base\BaseModel;
|
use catcher\base\CatchModel;
|
||||||
|
|
||||||
class Users extends BaseModel
|
class Users extends CatchModel
|
||||||
{
|
{
|
||||||
use HasRolesTrait;
|
use HasRolesTrait;
|
||||||
|
|
||||||
@ -46,7 +46,9 @@ class Users extends BaseModel
|
|||||||
*/
|
*/
|
||||||
public function getList($search): \think\Paginator
|
public function getList($search): \think\Paginator
|
||||||
{
|
{
|
||||||
return (($search['trash'] ?? false) ? static::onlyTrashed() : $this)->when($search['username'] ?? false, function ($query) use ($search){
|
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']);
|
return $query->whereLike('username', $search['username']);
|
||||||
})
|
})
|
||||||
->when($search['email'] ?? false, function ($query) use ($search){
|
->when($search['email'] ?? false, function ($query) use ($search){
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
namespace catchAdmin\user\request;
|
namespace catchAdmin\user\request;
|
||||||
|
|
||||||
use catchAdmin\user\model\Users;
|
use catchAdmin\user\model\Users;
|
||||||
use catcher\base\BaseRequest;
|
use catcher\base\CatchRequest;
|
||||||
|
|
||||||
class CreateRequest extends BaseRequest
|
class CreateRequest extends CatchRequest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function rules(): array
|
protected function rules(): array
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
namespace catchAdmin\user\request;
|
namespace catchAdmin\user\request;
|
||||||
|
|
||||||
use catchAdmin\user\model\Users;
|
use catchAdmin\user\model\Users;
|
||||||
use catcher\base\BaseRequest;
|
use catcher\base\CatchRequest;
|
||||||
|
|
||||||
class UpdateRequest extends BaseRequest
|
class UpdateRequest extends CatchRequest
|
||||||
{
|
{
|
||||||
protected function rules(): array
|
protected function rules(): array
|
||||||
{
|
{
|
||||||
|
@ -6,3 +6,4 @@ $router->get('users', '\catchAdmin\user\controller\User/list');
|
|||||||
// 切换状态
|
// 切换状态
|
||||||
$router->put('user/switch/status/<id>', '\catchAdmin\user\controller\User/switchStatus');
|
$router->put('user/switch/status/<id>', '\catchAdmin\user\controller\User/switchStatus');
|
||||||
$router->put('user/recover/<id>', '\catchAdmin\user\controller\User/recover');
|
$router->put('user/recover/<id>', '\catchAdmin\user\controller\User/recover');
|
||||||
|
$router->get('user/get/roles', '\catchAdmin\user\controller\User/getRoles');
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace catchAdmin\user\validate;
|
|
||||||
|
|
||||||
use catcher\base\BaseValidate;
|
|
||||||
|
|
||||||
class CreateValidate extends BaseValidate
|
|
||||||
{
|
|
||||||
protected function getRules(): array
|
|
||||||
{
|
|
||||||
// TODO: Implement getRules() method.
|
|
||||||
return [
|
|
||||||
'username|用户名' => 'require|max:20',
|
|
||||||
'password|密码' => 'require|max:20',
|
|
||||||
'email|邮箱' => 'require|email'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace catchAdmin\user\validate;
|
|
||||||
|
|
||||||
use catchAdmin\user\model\Users;
|
|
||||||
use catcher\base\BaseValidate;
|
|
||||||
|
|
||||||
class UpdateValidate extends BaseValidate
|
|
||||||
{
|
|
||||||
protected function getRules(): array
|
|
||||||
{
|
|
||||||
// TODO: Implement getRules() method.
|
|
||||||
return [
|
|
||||||
'username|用户名' => 'require|max:20',
|
|
||||||
'password|密码' => 'require|max:20',
|
|
||||||
'email|邮箱' => 'require|email|unique:'.Users::class.',email,'.request()->route('user').',id',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +1,42 @@
|
|||||||
{$form|raw}
|
{$form|raw}
|
||||||
<script>
|
<script>
|
||||||
layui.use(['layer', 'form', 'admin', 'formX'], function () {
|
layui.use(['layer', 'form', 'admin', 'formX','authtree'], function () {
|
||||||
var $ = layui.jquery;
|
var $ = layui.jquery;
|
||||||
var layer = layui.layer;
|
var layer = layui.layer;
|
||||||
var form = layui.form;
|
var form = layui.form;
|
||||||
var admin = layui.admin;
|
var admin = layui.admin;
|
||||||
|
var authtree = layui.authtree;
|
||||||
var mUser = admin.getLayerData('#userForm'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
var mUser = admin.getLayerData('#userForm'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
||||||
// 回显数据
|
// 回显数据
|
||||||
form.val('userForm', mUser);
|
form.val('userForm', mUser);
|
||||||
|
|
||||||
// 表单提交事件
|
// 表单提交事件
|
||||||
form.on('submit(submitUser)', function (data) {
|
form.on('submit(submitUser)', function (data) {
|
||||||
layer.load(2);
|
layer.load(2);
|
||||||
var url = mUser ? '{:url("user")}' : '{:url("user")}';
|
var url = mUser ? '{:url("user")}' : '{:url("user")}';
|
||||||
$.post(url, data.field, function (response) {
|
admin.req(url, data.field, function (response) {
|
||||||
layer.closeAll('loading');
|
layer.closeAll('loading');
|
||||||
if (response.code == 10000) {
|
if (response.code === 10000) {
|
||||||
layer.msg(response.msg, {icon: 1});
|
layer.msg(response.msg, {icon: 1});
|
||||||
admin.putLayerData('formOk', true, '#userForm'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
admin.putLayerData('formOk', true, '#userForm'); // 设置操作成功的标识,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
||||||
admin.closeDialog('#userForm'); // 关闭页面层弹窗
|
admin.closeDialog('#userForm'); // 关闭页面层弹窗
|
||||||
} else {
|
} else {
|
||||||
layer.msg(response.msg, {icon: 2});
|
layer.msg(response.msg, {icon: 2});
|
||||||
}
|
}
|
||||||
}, 'json');
|
}, 'post');
|
||||||
return false;
|
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',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
@ -1,14 +1,30 @@
|
|||||||
{$form|raw}
|
{$form|raw}
|
||||||
<script>
|
<script>
|
||||||
layui.use(['layer', 'form', 'admin', 'formX'], function () {
|
layui.use(['layer', 'form', 'admin', 'formX', 'authtree'], function () {
|
||||||
var $ = layui.jquery;
|
var $ = layui.jquery;
|
||||||
var layer = layui.layer;
|
var layer = layui.layer;
|
||||||
var form = layui.form;
|
var form = layui.form;
|
||||||
var admin = layui.admin;
|
var admin = layui.admin;
|
||||||
|
var authtree = layui.authtree;
|
||||||
var mUser = admin.getLayerData('#userForm'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
var mUser = admin.getLayerData('#userForm'); // 列表页面传递的数据,#modelUserForm这个只要写弹窗内任意一个元素的id即可
|
||||||
// 回显数据
|
// 回显数据
|
||||||
form.val('userForm', mUser);
|
form.val('userForm', mUser);
|
||||||
var uid = "{$uid}";
|
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
|
||||||
|
});
|
||||||
|
});
|
||||||
// 表单提交事件
|
// 表单提交事件
|
||||||
form.on('submit(submitUser)', function (data) {
|
form.on('submit(submitUser)', function (data) {
|
||||||
admin.req('/user/' + uid, data.field, function (response) {
|
admin.req('/user/' + uid, data.field, function (response) {
|
||||||
|
1
extend/catcher/Tree.php
Normal file
1
extend/catcher/Tree.php
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?php
|
1
extend/catcher/exceptions/FailedException.php
Normal file
1
extend/catcher/exceptions/FailedException.php
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?php
|
731
public/catch-admin/assets/module/authtree/authtree.js
Executable file
731
public/catch-admin/assets/module/authtree/authtree.js
Executable file
@ -0,0 +1,731 @@
|
|||||||
|
/*
|
||||||
|
* @Author: Jeffrey Wang
|
||||||
|
* @Date: 2018-03-16 18:24:47
|
||||||
|
* @Version: v1.2.4
|
||||||
|
* @Last Modified by: Jeffrey Wang
|
||||||
|
* @Last Modified time: 2019-04-29 14:33:00
|
||||||
|
*/
|
||||||
|
// 节点树
|
||||||
|
layui.define(['jquery', 'form'], function (exports) {
|
||||||
|
var $ = layui.jquery;
|
||||||
|
var form = layui.form;
|
||||||
|
var MOD_NAME = 'authtree';
|
||||||
|
|
||||||
|
var obj = {
|
||||||
|
// 渲染 + 绑定事件
|
||||||
|
openIconContent: '',
|
||||||
|
closeIconContent: '',
|
||||||
|
// 表单类型 checkbox: 多选,radio:单选
|
||||||
|
checkType: 'checkbox',
|
||||||
|
// 选中、半选中、未选中
|
||||||
|
checkedIconContent: '',
|
||||||
|
halfCheckedIconContent: '',
|
||||||
|
notCheckedIconContent: '',
|
||||||
|
// 保存节点数据
|
||||||
|
checkedNode: {},
|
||||||
|
notCheckedNode: {},
|
||||||
|
// 临时保存最新操作影响的节点
|
||||||
|
lastCheckedNode: {},
|
||||||
|
lastNotCheckedNode: {},
|
||||||
|
// 已经渲染过的树,可用来获取配置,{ dst: {trees: '树的节点数据', opt: '配置'} }
|
||||||
|
renderedTrees: {},
|
||||||
|
// 使用 layui 的监听事件
|
||||||
|
on: function (events, callback) {
|
||||||
|
return layui.onevent.call(this, MOD_NAME, events, callback);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 渲染DOM并绑定事件
|
||||||
|
* @param {[type]} dst [目标ID,如:#test1]
|
||||||
|
* @param {[type]} trees [数据,格式:{}]
|
||||||
|
* @param {[type]} inputname [上传表单名]
|
||||||
|
* @param {[type]} layfilter [lay-filter的值]
|
||||||
|
* @param {[type]} openall [是否展开全部]
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
render: function (dst, trees, opt) {
|
||||||
|
// 表单名称配置
|
||||||
|
var inputname = opt.inputname ? opt.inputname : 'menuids[]';
|
||||||
|
opt.inputname = inputname;
|
||||||
|
// lay-filter 配置
|
||||||
|
var layfilter = opt.layfilter ? opt.layfilter : 'checkauth';
|
||||||
|
opt.layfilter = layfilter;
|
||||||
|
// 默认展开全部 配置
|
||||||
|
var openall = opt.openall ? opt.openall : false;
|
||||||
|
opt.openall = openall;
|
||||||
|
// 双击展开此层配置
|
||||||
|
var dblshow = opt.dblshow ? opt.dblshow : false;
|
||||||
|
opt.dblshow = dblshow;
|
||||||
|
// 双击时间差 - 不能设置过长,否则单击延迟很感人
|
||||||
|
var dbltimeout = opt.dbltimeout ? opt.dbltimeout : 120;
|
||||||
|
opt.dbltimeout = dbltimeout;
|
||||||
|
// 默认展开有选中数据的层
|
||||||
|
var openchecked = typeof opt.openchecked !== 'undefined' ? opt.openchecked : true;
|
||||||
|
opt.openchecked = openchecked;
|
||||||
|
// 自动取消选中
|
||||||
|
var autoclose = typeof opt.autoclose !== 'undefined' ? opt.autoclose : true;
|
||||||
|
opt.autoclose = autoclose;
|
||||||
|
// 自动选择直属父级节点
|
||||||
|
var autochecked = typeof opt.autochecked !== 'undefined' ? opt.autochecked : true;
|
||||||
|
opt.autochecked = autochecked;
|
||||||
|
// 是否隐藏左侧 单选/多选的选框 -- 特殊需求,一般用于单选树并且不用
|
||||||
|
var hidechoose = typeof opt.hidechoose !== 'undefined' ? opt.hidechoose : false;
|
||||||
|
opt.hidechoose = hidechoose;
|
||||||
|
// 是否开启半选
|
||||||
|
var halfchoose = typeof opt.halfchoose !== 'undefined' ? opt.halfchoose : false;
|
||||||
|
opt.halfchoose = halfchoose;
|
||||||
|
// 收起叶子节点(排列于一行)
|
||||||
|
var collapseLeafNode = typeof opt.collapseLeafNode !== 'undefined' ? opt.collapseLeafNode : false;
|
||||||
|
opt.collapseLeafNode = collapseLeafNode;
|
||||||
|
// 有子节点的前显字符配置
|
||||||
|
opt.prefixChildStr = opt.prefixChildStr ? opt.prefixChildStr : '├─';
|
||||||
|
// 单选、多选配置
|
||||||
|
opt.checkType = opt.checkType ? opt.checkType : 'checkbox';
|
||||||
|
this.checkType = opt.checkType;
|
||||||
|
// 皮肤可选择
|
||||||
|
opt.checkSkin = opt.checkSkin ? opt.checkSkin : 'primary';
|
||||||
|
// 主题定制
|
||||||
|
opt.theme = opt.theme ? opt.theme : '';
|
||||||
|
opt.themePath = opt.themePath ? opt.themePath : 'layui_exts/tree_themes/';
|
||||||
|
// 展开、折叠节点的前显字符配置
|
||||||
|
opt.openIconContent = opt.openIconContent ? opt.openIconContent : '';
|
||||||
|
this.openIconContent = opt.openIconContent;
|
||||||
|
opt.closeIconContent = opt.closeIconContent ? opt.closeIconContent : '';
|
||||||
|
this.closeIconContent = opt.closeIconContent;
|
||||||
|
// 选中、半选中、未选中节点的图标配置
|
||||||
|
opt.checkedIconContent = opt.checkedIconContent ? opt.checkedIconContent : '\e605';
|
||||||
|
this.checkedIconContent = opt.checkedIconContent;
|
||||||
|
opt.halfCheckedIconContent = opt.halfCheckedIconContent ? opt.halfCheckedIconContent : '\e605';
|
||||||
|
this.halfCheckedIconContent = opt.halfCheckedIconContent;
|
||||||
|
opt.notCheckedIconContent = opt.notCheckedIconContent ? opt.notCheckedIconContent : '';
|
||||||
|
this.notCheckedIconContent = opt.notCheckedIconContent;
|
||||||
|
// 渲染配置参数
|
||||||
|
opt.checkedKey = opt.checkedKey ? opt.checkedKey : 'checked';
|
||||||
|
opt.childKey = opt.childKey ? opt.childKey : 'list';
|
||||||
|
opt.disabledKey = opt.disabledKey ? opt.disabledKey : 'disabled';
|
||||||
|
opt.nameKey = opt.nameKey ? opt.nameKey : 'name';
|
||||||
|
opt.valueKey = opt.valueKey ? opt.valueKey : 'value';
|
||||||
|
|
||||||
|
// 不启用双击展开,单击不用延迟
|
||||||
|
var dblisten = true;
|
||||||
|
if (dblshow) {
|
||||||
|
// 开启双击展开,双击事件默认为120s
|
||||||
|
} else {
|
||||||
|
// 未开启双击展开且 dbltimeout <= 0,则说明不用监听双击事件
|
||||||
|
if (opt.dbltimeout <= 0) {
|
||||||
|
dblisten = false;
|
||||||
|
}
|
||||||
|
dbltimeout = 0;
|
||||||
|
// opt.dbltimeout = dbltimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录渲染过的树
|
||||||
|
obj.renderedTrees[dst] = {trees: trees, opt: opt};
|
||||||
|
|
||||||
|
// 主题定制
|
||||||
|
if (typeof opt.theme === 'string' && opt.theme !== '') {
|
||||||
|
$(dst).addClass(opt.theme)
|
||||||
|
layui.link(opt.themePath + opt.theme + '.css')
|
||||||
|
}
|
||||||
|
if (opt.hidechoose) {
|
||||||
|
$(dst).addClass('auth-tree-hidechoose');
|
||||||
|
}
|
||||||
|
$(dst).html(obj.renderAuth(trees, 0, {
|
||||||
|
inputname: inputname,
|
||||||
|
layfilter: layfilter,
|
||||||
|
openall: openall,
|
||||||
|
openchecked: openchecked,
|
||||||
|
checkType: this.checkType,
|
||||||
|
prefixChildStr: opt.prefixChildStr,
|
||||||
|
// 配置参数
|
||||||
|
checkedKey: opt.checkedKey,
|
||||||
|
childKey: opt.childKey,
|
||||||
|
disabledKey: opt.disabledKey,
|
||||||
|
nameKey: opt.nameKey,
|
||||||
|
valueKey: opt.valueKey,
|
||||||
|
collapseLeafNode: opt.collapseLeafNode,
|
||||||
|
}));
|
||||||
|
if (openchecked) {
|
||||||
|
obj.showChecked(dst);
|
||||||
|
}
|
||||||
|
form.render();
|
||||||
|
// 变动则存一下临时状态
|
||||||
|
obj._saveNodeStatus(dst);
|
||||||
|
|
||||||
|
// 开启自动宽度优化
|
||||||
|
obj.autoWidthAll();
|
||||||
|
// 备注:如果使用form.on('checkbox()'),外部就无法使用form.on()监听同样的元素了(LAYUI不支持重复监听了)。
|
||||||
|
// form.on('checkbox('+layfilter+')', function(data){
|
||||||
|
// /*属下所有权限状态跟随,如果选中,往上走全部选中*/
|
||||||
|
// var childs = $(data.elem).parent().next().find('input[type="checkbox"]').prop('checked', data.elem.checked);
|
||||||
|
// if(data.elem.checked){
|
||||||
|
// /*查找child的前边一个元素,并将里边的checkbox选中状态改为true。*/
|
||||||
|
// $(data.elem).parents('.auth-child').prev().find('input[type="checkbox"]').prop('checked', true);
|
||||||
|
// }
|
||||||
|
// /*console.log(childs);*/
|
||||||
|
// form.render('checkbox');
|
||||||
|
// });
|
||||||
|
|
||||||
|
// 解决单击和双击冲突问题的 timer 变量
|
||||||
|
var timer = 0;
|
||||||
|
$(dst).find('.auth-single:first').unbind('click').on('click', '.layui-form-checkbox,.layui-form-radio', function (event) {
|
||||||
|
// window.event? window.event.cancelBubble = true : event.stopPropagation();
|
||||||
|
var that = this;
|
||||||
|
clearTimeout(timer);
|
||||||
|
// 双击判断需要的延迟处理
|
||||||
|
timer = setTimeout(function () {
|
||||||
|
var elem = $(that).prev();
|
||||||
|
var checked = elem.is(':checked');
|
||||||
|
|
||||||
|
if (autochecked) {
|
||||||
|
if (checked) {
|
||||||
|
/*查找child的前边一个元素,并将里边的checkbox选中状态改为true。*/
|
||||||
|
elem.parents('.auth-child').prev().find('.authtree-checkitem:not(:disabled)[type="checkbox"]').prop('checked', true);
|
||||||
|
}
|
||||||
|
elem.parent().next().find('.authtree-checkitem:not(:disabled)[type="checkbox"]').prop('checked', checked);
|
||||||
|
}
|
||||||
|
if (autoclose) {
|
||||||
|
if (checked) {
|
||||||
|
// pass
|
||||||
|
} else {
|
||||||
|
// 自动关闭父级选中节点
|
||||||
|
obj._autoclose($(that).parent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form.render('checkbox');
|
||||||
|
form.render('radio');
|
||||||
|
// 变动则存一下临时状态
|
||||||
|
obj._saveNodeStatus(dst);
|
||||||
|
// 触发 change 事件
|
||||||
|
obj._triggerEvent(dst, 'change', {
|
||||||
|
othis: $(that),
|
||||||
|
oinput: elem,
|
||||||
|
value: elem.val(),
|
||||||
|
});
|
||||||
|
obj.autoWidthAll();
|
||||||
|
}, dbltimeout);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
/*动态绑定展开事件*/
|
||||||
|
$(dst).unbind('click').on('click', '.auth-icon', function () {
|
||||||
|
obj.iconToggle(dst, this);
|
||||||
|
});
|
||||||
|
/*双击展开*/
|
||||||
|
$(dst).find('.auth-single:first').unbind('dblclick').on('dblclick', '.layui-form-checkbox,.layui-form-radio', function (e) {
|
||||||
|
// 触发时间 > 0,才触发双击事件
|
||||||
|
// opt.dbltimeout 是用户真实设定的超时时间,与 dbltimeout 不一样
|
||||||
|
// if (opt.dbltimeout > 0) {
|
||||||
|
obj._triggerEvent(dst, 'dblclick', {
|
||||||
|
othis: $(this),
|
||||||
|
elem: $(this).prev(),
|
||||||
|
value: $(this).prev().val(),
|
||||||
|
});
|
||||||
|
// }
|
||||||
|
if (dblshow) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
obj.iconToggle(dst, $(this).prevAll('.auth-icon:first'));
|
||||||
|
}
|
||||||
|
}).on('selectstart', function () {
|
||||||
|
// 屏蔽双击选中文字
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 自动关闭 - 如果兄弟节点均没选中,递归取消上级元素选中状态,传入的是 .auth-status 节点,递归 .auth-status 上级节点
|
||||||
|
_autoclose: function (obj) {
|
||||||
|
var single = $(obj).parent().parent();
|
||||||
|
var authStatus = single.parent().prev();
|
||||||
|
|
||||||
|
if (!authStatus.hasClass('auth-status')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 仅一层
|
||||||
|
if (single.find('div>.auth-status>input.authtree-checkitem:not(:disabled)[type="checkbox"]:checked').length === 0) {
|
||||||
|
authStatus.find('.authtree-checkitem:not(:disabled)[type="checkbox"]').prop('checked', false);
|
||||||
|
this._autoclose(authStatus);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 以 icon 的维度,切换显示下级空间
|
||||||
|
iconToggle: function (dst, iconobj) {
|
||||||
|
var origin = $(iconobj);
|
||||||
|
var child = origin.parent().parent().find('.auth-child:first');
|
||||||
|
if (origin.is('.active')) {
|
||||||
|
/*收起*/
|
||||||
|
origin.removeClass('active').html(obj.closeIconContent);
|
||||||
|
child.slideUp('fast');
|
||||||
|
} else {
|
||||||
|
/*展开*/
|
||||||
|
origin.addClass('active').html(obj.openIconContent);
|
||||||
|
child.slideDown('fast');
|
||||||
|
}
|
||||||
|
obj._triggerEvent(dst, 'deptChange');
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
// 递归创建格式
|
||||||
|
renderAuth: function (tree, dept, opt) {
|
||||||
|
var inputname = opt.inputname;
|
||||||
|
var layfilter = opt.layfilter;
|
||||||
|
var openall = opt.openall;
|
||||||
|
var str = '<div class="auth-single">';
|
||||||
|
|
||||||
|
// 参数配置
|
||||||
|
var childKey = opt.childKey;
|
||||||
|
var nameKey = opt.nameKey;
|
||||||
|
var valueKey = opt.valueKey;
|
||||||
|
|
||||||
|
var _this = this;
|
||||||
|
layui.each(tree, function (index, item) {
|
||||||
|
var hasChild = (item[childKey] && (item[childKey].length || !$.isEmptyObject(item[childKey].length))) ? 1 : 0;
|
||||||
|
// 注意:递归调用时,this的环境会改变!
|
||||||
|
var append = hasChild ? obj.renderAuth(item[childKey], dept + 1, opt) : '';
|
||||||
|
var openstatus = openall || (opt.openchecked && item.checked);
|
||||||
|
var isChecked = _this._getStatusByDynamicKey(item, opt.checkedKey, opt.valueKey);
|
||||||
|
var isDisabled = _this._getStatusByDynamicKey(item, opt.disabledKey, opt.valueKey);
|
||||||
|
|
||||||
|
var rowFlag = !hasChild && opt.collapseLeafNode;
|
||||||
|
if (rowFlag) {
|
||||||
|
str += '<div class="auth-row auth-skin"><div class="auth-row-item auth-status" style="display: flex;flex-direction: row;align-items: flex-end;">' +
|
||||||
|
(hasChild ? '' : '<i class="layui-icon auth-leaf" style="opacity:0;color: transparent;"></i>');
|
||||||
|
} else {
|
||||||
|
// '+new Array(dept * 4).join(' ')+'
|
||||||
|
str += '<div class="auth-skin"><div class="auth-status" style="display: flex;flex-direction: row;align-items: flex-end;"> ' +
|
||||||
|
(hasChild ? '<i class="layui-icon auth-icon ' + (openstatus ? 'active' : '') + '" style="cursor:pointer;">' + (openstatus ? obj.openIconContent : obj.closeIconContent) + '</i>' : '<i class="layui-icon auth-leaf" style="opacity:0;color: transparent;"></i>') +
|
||||||
|
(dept > 0 ? ('<span class="auth-prefix">' + opt.prefixChildStr + ' </span>') : '');
|
||||||
|
}
|
||||||
|
str +=
|
||||||
|
'<input class="authtree-checkitem" type="' + opt.checkType + '" name="' + inputname + '" title="' + item[nameKey] + '" value="' + item[valueKey] + '" lay-skin="primary" lay-filter="' + layfilter + '" ' +
|
||||||
|
(isChecked ? ' checked="checked"' : '') +
|
||||||
|
(isDisabled ? ' disabled' : '') +
|
||||||
|
'> </div>' +
|
||||||
|
' <div class="auth-child" style="' + (openstatus ? '' : 'display:none;') + '"> ' + append + '</div></div>'
|
||||||
|
});
|
||||||
|
str += '</div>';
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
// 通过动态key,获取状态信息,dynamicKey支持:数字/字符时直接取属性,对象时查看是否在数组中
|
||||||
|
_getStatusByDynamicKey: function (item, dynamicKey, valueKey) {
|
||||||
|
var isChecked = false;
|
||||||
|
if (typeof dynamicKey === "string" || typeof dynamicKey === 'number') {
|
||||||
|
isChecked = item[dynamicKey];
|
||||||
|
} else if (typeof dynamicKey === 'object') {
|
||||||
|
isChecked = $.inArray(item[valueKey], dynamicKey) !== -1;
|
||||||
|
} else {
|
||||||
|
isChecked = false;
|
||||||
|
}
|
||||||
|
return isChecked;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 显示到已选中的最高层级
|
||||||
|
* @param {[type]} dst [description]
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
showChecked: function (dst) {
|
||||||
|
$(dst).find('.authtree-checkitem:checked').parents('.auth-child').show();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 将普通列表无限递归转换为树
|
||||||
|
* @param {[type]} list [普通的列表,必须包括 opt.primaryKey 指定的键和 opt.parentKey 指定的键]
|
||||||
|
* @param {[type]} opt [配置参数,支持 primaryKey(主键 默认id) parentKey(父级id对应键 默认pid) nameKey(节点标题对应的key 默认name) valueKey(节点值对应的key 默认id) checkedKey、disabledKey(节点是否选中的字段 默认checked,传入数组则判断主键是否在此数组中) startPid(第一层扫描的PID 默认0) currentDept(当前层 默认0) maxDept(最大递归层 默认100) childKey(递归完成后子节点对应键 默认list) deptPrefix(根据层级重复的前缀 默认'')]
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
listConvert: function (list, opt) {
|
||||||
|
opt.primaryKey = opt.primaryKey ? opt.primaryKey : 'id';
|
||||||
|
opt.parentKey = opt.parentKey ? opt.parentKey : 'pid';
|
||||||
|
opt.startPid = opt.startPid ? opt.startPid : 0;
|
||||||
|
opt.authType = opt.authType ? opt.authType : 'type';
|
||||||
|
opt.currentDept = opt.currentDept ? parseInt(opt.currentDept) : 0;
|
||||||
|
opt.maxDept = opt.maxDept ? opt.maxDept : 100;
|
||||||
|
opt.childKey = opt.childKey ? opt.childKey : 'list';
|
||||||
|
opt.checkedKey = opt.checkedKey ? opt.checkedKey : 'checked';
|
||||||
|
opt.disabledKey = opt.disabledKey ? opt.disabledKey : 'disabled';
|
||||||
|
opt.nameKey = opt.nameKey ? opt.nameKey : 'name';
|
||||||
|
opt.valueKey = opt.valueKey ? opt.valueKey : 'id';
|
||||||
|
return this._listToTree(list, opt.startPid, opt.currentDept, opt);
|
||||||
|
},
|
||||||
|
// 实际的递归函数,将会变化的参数抽取出来
|
||||||
|
_listToTree: function (list, startPid, currentDept, opt) {
|
||||||
|
if (opt.maxDept < currentDept) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var child = [];
|
||||||
|
for (var index in list) {
|
||||||
|
if (list.hasOwnProperty(index)) {
|
||||||
|
// 筛查符合条件的数据(主键 = startPid)
|
||||||
|
var item = list[index];
|
||||||
|
if (typeof item[opt.parentKey] !== 'undefined' && item[opt.parentKey] === startPid) {
|
||||||
|
// 满足条件则递归
|
||||||
|
var nextChild = this._listToTree(list, item[opt.primaryKey], currentDept + 1, opt);
|
||||||
|
// 节点信息保存
|
||||||
|
var node = {};
|
||||||
|
if (nextChild.length > 0) {
|
||||||
|
node[opt.childKey] = nextChild;
|
||||||
|
}
|
||||||
|
node['name'] = item[opt.nameKey];
|
||||||
|
node['value'] = item[opt.valueKey];
|
||||||
|
node['type'] = item[opt.authType] || 0;
|
||||||
|
// 禁用/选中节点的两种渲染方式
|
||||||
|
node['checked'] = this._getStatusByDynamicKey(item, opt.checkedKey, opt.valueKey);
|
||||||
|
node['disabled'] = this._getStatusByDynamicKey(item, opt.disabledKey, opt.valueKey);
|
||||||
|
child.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 将树转为单选可用的 select,如果后台返回列表数据,可以先转换为 tree
|
||||||
|
* @param {[type]} tree [description]
|
||||||
|
* @param {[type]} opt [description]
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
treeConvertSelect: function (tree, opt) {
|
||||||
|
if (typeof tree.length !== 'number' || tree.length <= 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// 初始化层级
|
||||||
|
opt.currentDept = opt.currentDept ? parseInt(opt.currentDept) : 0;
|
||||||
|
// 子节点列表的Key
|
||||||
|
opt.childKey = opt.childKey ? opt.childKey : 'list';
|
||||||
|
// 名称的key
|
||||||
|
opt.nameKey = opt.valueKey ? opt.valueKey : 'name';
|
||||||
|
// 值的key
|
||||||
|
opt.valueKey = opt.valueKey ? opt.valueKey : 'value';
|
||||||
|
// 选中的key - 仅支持字符串
|
||||||
|
opt.checkedKey = opt.checkedKey ? opt.checkedKey : 'checked';
|
||||||
|
// 禁用的key
|
||||||
|
opt.disabledKey = opt.disabledKey ? opt.disabledKey : 'disabled';
|
||||||
|
// 有子节点的前缀
|
||||||
|
opt.prefixChildStr = opt.prefixChildStr ? opt.prefixChildStr : '├─ ';
|
||||||
|
// 没有子节点的前缀
|
||||||
|
opt.prefixNoChildStr = opt.prefixNoChildStr ? opt.prefixNoChildStr : '● ';
|
||||||
|
// 树的深度影响的子节点数据
|
||||||
|
opt.prefixDeptStr = opt.prefixDeptStr ? opt.prefixDeptStr : ' ';
|
||||||
|
// 如果第一列就存在没有子节点的情况,加的特殊前缀
|
||||||
|
opt.prefixFirstEmpty = opt.prefixFirstEmpty ? opt.prefixFirstEmpty : ' '
|
||||||
|
|
||||||
|
return this._treeToSelect(tree, opt.currentDept, opt);
|
||||||
|
},
|
||||||
|
// 实际处理递归的函数
|
||||||
|
_treeToSelect: function (tree, currentDept, opt) {
|
||||||
|
var ansList = [];
|
||||||
|
var prefix = '';
|
||||||
|
|
||||||
|
for (var i = 0; i < currentDept; i++) {
|
||||||
|
prefix += opt.prefixDeptStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var index in tree) {
|
||||||
|
if (!tree.hasOwnProperty(index)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var child_flag = 0;
|
||||||
|
var item = tree[index];
|
||||||
|
if (opt.childKey in item && item[opt.childKey] && item[opt.childKey].length > 0) {
|
||||||
|
child_flag = 1;
|
||||||
|
}
|
||||||
|
var name = item[opt.nameKey];
|
||||||
|
if (child_flag) {
|
||||||
|
name = opt.prefixChildStr + name;
|
||||||
|
} else {
|
||||||
|
if (currentDept > 1) {
|
||||||
|
name = opt.prefixNoChildStr + name;
|
||||||
|
} else {
|
||||||
|
name = opt.prefixFirstEmpty + name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ansList.push({
|
||||||
|
name: prefix + name,
|
||||||
|
value: item[opt.valueKey],
|
||||||
|
checked: this._getStatusByDynamicKey(item, opt.checkedKey, opt.valueKey),
|
||||||
|
disabled: this._getStatusByDynamicKey(item, opt.disabledKey, opt.valueKey),
|
||||||
|
});
|
||||||
|
// 添加子节点
|
||||||
|
if (child_flag) {
|
||||||
|
var child = this._treeToSelect(item[opt.childKey], currentDept + 1, opt);
|
||||||
|
// apply 的骚操作,使用第二个参数可以用于合并两个数组
|
||||||
|
ansList.push.apply(ansList, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ansList;
|
||||||
|
},
|
||||||
|
autoWidthAll: function () {
|
||||||
|
for (var dst in this.renderedTrees) {
|
||||||
|
if (this.renderedTrees.hasOwnProperty(dst)) {
|
||||||
|
this.autoWidth(dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 自动调整宽度以解决 form.render()生成元素兼容性问题,如果用户手动调用 form.render() 之后也需要调用此方法
|
||||||
|
autoWidth: function (dst) {
|
||||||
|
var tree = this.getRenderedInfo(dst);
|
||||||
|
var opt = tree.opt;
|
||||||
|
$(dst).css({
|
||||||
|
'whiteSpace': 'nowrap',
|
||||||
|
'maxWidth': '100%',
|
||||||
|
});
|
||||||
|
// 自动刷新多选框半选状态
|
||||||
|
// this.autoNodeRender(dst)
|
||||||
|
// 自动宽度调整的逻辑
|
||||||
|
$(dst).find('.layui-form-checkbox,.layui-form-radio,.layui-form-audio').each(function (index, item) {
|
||||||
|
var width = $(this).find('span').width() + $(this).find('i').width() + 25;
|
||||||
|
if ($(this).is(':hidden')) {
|
||||||
|
// 比较奇葩的获取隐藏元素宽度的手法,请见谅
|
||||||
|
$('body').append('<div id="layui-authtree-get-width">' + $(this).html() + '</div>');
|
||||||
|
width = $('#layui-authtree-get-width').find('span').width() + $('#layui-authtree-get-width').find('i').width() + 29;
|
||||||
|
$('#layui-authtree-get-width').remove();
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
//$(this).width(width);
|
||||||
|
// 隐藏 单选/多选的左侧选框隐藏
|
||||||
|
if (opt.hidechoose) {
|
||||||
|
$(this).prevAll('i').css({
|
||||||
|
zIndex: 2,
|
||||||
|
});
|
||||||
|
$(this).css({
|
||||||
|
position: 'relative'
|
||||||
|
, left: function () {
|
||||||
|
return '-' + $(this).css('padding-left');// 避免点击抖动的骚操作
|
||||||
|
}
|
||||||
|
}).find('i').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 自动刷新多选框半选状态
|
||||||
|
autoNodeRender: function (dst) {
|
||||||
|
var tree = this.getRenderedInfo(dst);
|
||||||
|
var opt = tree.opt;
|
||||||
|
if (opt.halfchoose) {
|
||||||
|
this._nodeRenderByParent($(dst).find('.auth-single'))
|
||||||
|
}
|
||||||
|
document.styleSheets[0].addRule(dst + ' .layui-icon-ok:before', 'content: ' + this.checkedIconContent)
|
||||||
|
},
|
||||||
|
_nodeRenderByParent: function (leaf) {
|
||||||
|
},
|
||||||
|
// 触发自定义事件
|
||||||
|
_triggerEvent: function (dst, events, other) {
|
||||||
|
var tree = this.getRenderedInfo(dst);
|
||||||
|
var origin = $(dst);
|
||||||
|
if (tree) {
|
||||||
|
var opt = tree.opt;
|
||||||
|
var data = {
|
||||||
|
opt: opt,
|
||||||
|
tree: tree.trees,
|
||||||
|
dst: dst,
|
||||||
|
othis: origin,
|
||||||
|
};
|
||||||
|
if (other && typeof other === 'object') {
|
||||||
|
data = $.extend(data, other);
|
||||||
|
}
|
||||||
|
// 支持 dst 和 用户的配置的 layfilter 监听
|
||||||
|
layui.event.call(origin, MOD_NAME, events + '(' + dst + ')', data);
|
||||||
|
layui.event.call(origin, MOD_NAME, events + '(' + opt.layfilter + ')', data);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取渲染过的信息
|
||||||
|
getRenderedInfo: function (dst) {
|
||||||
|
return this.renderedTrees[dst];
|
||||||
|
},
|
||||||
|
// 动态获取最大深度
|
||||||
|
getMaxDept: function (dst) {
|
||||||
|
var next = $(dst);
|
||||||
|
var dept = 0;
|
||||||
|
while (next.length && dept < 100000) {
|
||||||
|
next = this._getNext(next);
|
||||||
|
if (next.length) {
|
||||||
|
dept++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dept;
|
||||||
|
},
|
||||||
|
// 全选
|
||||||
|
checkAll: function (dst) {
|
||||||
|
var origin = $(dst);
|
||||||
|
|
||||||
|
origin.find('.authtree-checkitem:not(:disabled):not(:checked)').prop('checked', true);
|
||||||
|
form.render('checkbox');
|
||||||
|
form.render('radio');
|
||||||
|
obj.autoWidthAll();
|
||||||
|
// 变动则存一下临时状态
|
||||||
|
obj._saveNodeStatus(dst);
|
||||||
|
obj._triggerEvent(dst, 'change');
|
||||||
|
obj._triggerEvent(dst, 'checkAll');
|
||||||
|
},
|
||||||
|
// 全不选
|
||||||
|
uncheckAll: function (dst) {
|
||||||
|
var origin = $(dst);
|
||||||
|
origin.find('.authtree-checkitem:not(:disabled):checked').prop('checked', false);
|
||||||
|
form.render('checkbox');
|
||||||
|
form.render('radio');
|
||||||
|
obj.autoWidthAll();
|
||||||
|
// 变动则存一下临时状态
|
||||||
|
obj._saveNodeStatus(dst);
|
||||||
|
obj._triggerEvent(dst, 'change');
|
||||||
|
obj._triggerEvent(dst, 'uncheckAll');
|
||||||
|
},
|
||||||
|
// 显示整个树
|
||||||
|
showAll: function (dst) {
|
||||||
|
this.showDept(dst, this.getMaxDept(dst));
|
||||||
|
},
|
||||||
|
// 关闭整颗树
|
||||||
|
closeAll: function (dst) {
|
||||||
|
this.closeDept(dst, 1);
|
||||||
|
},
|
||||||
|
// 切换整颗树的显示/关闭
|
||||||
|
toggleAll: function (dst) {
|
||||||
|
if (this._shownDept(2)) {
|
||||||
|
this.closeDept(dst);
|
||||||
|
} else {
|
||||||
|
this.showAll(dst);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 显示到第 dept 层
|
||||||
|
showDept: function (dst, dept) {
|
||||||
|
var next = $(dst);
|
||||||
|
for (var i = 1; i < dept; i++) {
|
||||||
|
next = this._getNext(next);
|
||||||
|
if (next.length) {
|
||||||
|
this._showSingle(next);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj._triggerEvent(dst, 'deptChange', {dept: dept});
|
||||||
|
},
|
||||||
|
// 第 dept 层之后全部关闭
|
||||||
|
closeDept: function (dst, dept) {
|
||||||
|
var next = $(dst);
|
||||||
|
for (var i = 0; i < dept; i++) {
|
||||||
|
next = this._getNext(next);
|
||||||
|
}
|
||||||
|
while (next.length) {
|
||||||
|
this._closeSingle(next);
|
||||||
|
next = this._getNext(next);
|
||||||
|
}
|
||||||
|
obj._triggerEvent(dst, 'deptChange', {dept: dept});
|
||||||
|
},
|
||||||
|
// 临时保存所有节点信息状态
|
||||||
|
_saveNodeStatus: function (dst) {
|
||||||
|
var currentChecked = this.getChecked(dst);
|
||||||
|
var currentNotChecked = this.getNotChecked(dst);
|
||||||
|
// 保存新信息前,最新选择的信息
|
||||||
|
this.lastCheckedNode[dst] = this._getLastChecked(dst, currentChecked, currentNotChecked);
|
||||||
|
this.lastNotCheckedNode[dst] = this._getLastNotChecked(dst, currentChecked, currentNotChecked);
|
||||||
|
this.checkedNode[dst] = currentChecked;
|
||||||
|
this.notCheckedNode[dst] = currentNotChecked;
|
||||||
|
|
||||||
|
// console.log('保存节点信息', this.checkedNode[dst], this.notCheckedNode[dst], this.lastCheckedNode[dst], this.lastNotCheckedNode[dst]);
|
||||||
|
},
|
||||||
|
// 判断某一层是否显示
|
||||||
|
_shownDept: function (dst, dept) {
|
||||||
|
var next = $(dst);
|
||||||
|
for (var i = 0; i < dept; i++) {
|
||||||
|
next = this._getNext(next);
|
||||||
|
}
|
||||||
|
return !next.is(':hidden');
|
||||||
|
},
|
||||||
|
// 获取
|
||||||
|
_getNext: function (dst) {
|
||||||
|
return $(dst).find('.auth-single:first>div>.auth-child');
|
||||||
|
},
|
||||||
|
// 显示某层 single
|
||||||
|
_showSingle: function (dst) {
|
||||||
|
layui.each(dst, function (index, item) {
|
||||||
|
var origin = $(item).find('.auth-single:first');
|
||||||
|
var parentChild = origin.parent();
|
||||||
|
var parentStatus = parentChild.prev();
|
||||||
|
if (!parentStatus.find('.auth-icon').hasClass('active')) {
|
||||||
|
parentChild.show();
|
||||||
|
// 显示上级的 .auth-child节点,并修改.auth-status的折叠状态
|
||||||
|
parentStatus.find('.auth-icon').addClass('active').html(obj.openIconContent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 关闭某层 single
|
||||||
|
_closeSingle: function (dst) {
|
||||||
|
var origin = $(dst).find('.auth-single:first');
|
||||||
|
var parentChild = origin.parent();
|
||||||
|
var parentStatus = parentChild.prev();
|
||||||
|
if (parentStatus.find('.auth-icon').hasClass('active')) {
|
||||||
|
parentChild.hide();
|
||||||
|
// 显示上级的 .auth-child节点,并修改.auth-status的折叠状态
|
||||||
|
parentStatus.find('.auth-icon').removeClass('active').html(obj.closeIconContent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取选中叶子结点
|
||||||
|
getLeaf: function (dst) {
|
||||||
|
var leafs = $(dst).find('.auth-leaf').parent().find('.authtree-checkitem:checked');
|
||||||
|
var data = [];
|
||||||
|
leafs.each(function (index, item) {
|
||||||
|
// console.log(item);
|
||||||
|
data.push(item.value);
|
||||||
|
});
|
||||||
|
// console.log(data);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
// 获取所有节点数据
|
||||||
|
getAll: function (dst) {
|
||||||
|
var inputs = $(dst).find('.authtree-checkitem');
|
||||||
|
var data = [];
|
||||||
|
inputs.each(function (index, item) {
|
||||||
|
data.push(item.value);
|
||||||
|
});
|
||||||
|
// console.log(data);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
// 获取最新选中(之前取消-现在选中)
|
||||||
|
getLastChecked: function (dst) {
|
||||||
|
return this.lastCheckedNode[dst] || [];
|
||||||
|
},
|
||||||
|
// (逻辑)最新选中(之前取消-现在选中)
|
||||||
|
_getLastChecked: function (dst, currentChecked, currentNotChecked) {
|
||||||
|
var lastCheckedNode = currentChecked;
|
||||||
|
|
||||||
|
var data = [];
|
||||||
|
for (var i in lastCheckedNode) {
|
||||||
|
if ($.inArray(lastCheckedNode[i], this.notCheckedNode[dst]) !== -1) {
|
||||||
|
data.push(lastCheckedNode[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
// 获取所有选中的数据
|
||||||
|
getChecked: function (dst) {
|
||||||
|
var inputs = $(dst).find('.authtree-checkitem:checked');
|
||||||
|
var data = [];
|
||||||
|
inputs.each(function (index, item) {
|
||||||
|
data.push(item.value);
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
// 获取最新取消(之前取消-现在选中)
|
||||||
|
getLastNotChecked: function (dst) {
|
||||||
|
return this.lastNotCheckedNode[dst] || [];
|
||||||
|
},
|
||||||
|
// (逻辑)最新取消(之前选中-现在取消)
|
||||||
|
_getLastNotChecked: function (dst, currentChecked, currentNotChecked) {
|
||||||
|
var lastNotCheckedNode = currentNotChecked;
|
||||||
|
|
||||||
|
var data = [];
|
||||||
|
for (var i in lastNotCheckedNode) {
|
||||||
|
if ($.inArray(lastNotCheckedNode[i], this.checkedNode[dst]) !== -1) {
|
||||||
|
data.push(lastNotCheckedNode[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
// 获取未选中数据
|
||||||
|
getNotChecked: function (dst) {
|
||||||
|
var inputs = $(dst).find('.authtree-checkitem:not(:checked)');
|
||||||
|
var data = [];
|
||||||
|
inputs.each(function (index, item) {
|
||||||
|
data.push(item.value);
|
||||||
|
});
|
||||||
|
// console.log(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports('authtree', obj);
|
||||||
|
});
|
108
public/catch-admin/assets/module/authtree/tree_themes/auth-skin-default.css
Executable file
108
public/catch-admin/assets/module/authtree/tree_themes/auth-skin-default.css
Executable file
@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
Author: Jeffrey Wang
|
||||||
|
Email: admin@wj2015.com
|
||||||
|
Name: auth-skin-default
|
||||||
|
Comment: 默认主题,感谢lipengfei886@qq.com提供的基础样式
|
||||||
|
Version: v1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*请在js中的.auth-status前的div加一个class属性auth-skin*/
|
||||||
|
.auth-skin-default .auth-status {
|
||||||
|
margin-top: -12px;
|
||||||
|
}
|
||||||
|
.auth-skin-default .auth-status > i.layui-icon {
|
||||||
|
width: 18px;
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.auth-skin-default .auth-status > div {
|
||||||
|
position: relative;
|
||||||
|
top: 6px;
|
||||||
|
padding-left: 17px;
|
||||||
|
}
|
||||||
|
.auth-skin-default .auth-status > div.layui-form-radio {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
/*美化的多选框/单选框DOM*/
|
||||||
|
.auth-skin-default .auth-status > div > span {
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
top: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-default .auth-skin {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 13px 5px 0 5px;
|
||||||
|
position: relative;
|
||||||
|
color: #009688;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-default .auth-skin::before, .auth-skin-default .auth-skin::after {
|
||||||
|
content: '';
|
||||||
|
left: -10px;
|
||||||
|
position: absolute;
|
||||||
|
right: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-default .auth-skin::before {
|
||||||
|
border-left: 1px dashed #009688;
|
||||||
|
bottom: 50px;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
width: 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-default .auth-skin::after {
|
||||||
|
border-top: 1px dashed #009688;
|
||||||
|
height: 20px;
|
||||||
|
top: 25px;
|
||||||
|
width: 25px
|
||||||
|
}
|
||||||
|
/*解决多层数据中,auth-child缩进长度的问题*/
|
||||||
|
.auth-skin-default > .auth-single .auth-single {
|
||||||
|
margin-left: 36px;
|
||||||
|
}
|
||||||
|
.auth-skin-default > .auth-single > .auth-skin > .auth-child .auth-child {
|
||||||
|
margin-left: 18px;
|
||||||
|
}
|
||||||
|
.auth-skin-default > .auth-single > .auth-skin {
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-default > .auth-single > .auth-skin::before, .auth-skin-default > .auth-single > .auth-skin::after {
|
||||||
|
border: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-default > .auth-single > .auth-skin:last-child::before {
|
||||||
|
height: 30px
|
||||||
|
}
|
||||||
|
/*隐藏左侧操作的特殊样式*/
|
||||||
|
.auth-skin-default.auth-tree-hidechoose .auth-status > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.auth-skin-default.auth-tree-hidechoose .auth-status > div > span {
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
.auth-skin-default.auth-tree-hidechoose .auth-status > i {
|
||||||
|
position: relative;
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*放于同一行的样式*/
|
||||||
|
.auth-skin-default .auth-row {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.auth-skin-default > .auth-single .auth-child .auth-row {
|
||||||
|
margin-left: -22px;
|
||||||
|
}
|
||||||
|
.auth-skin-default .auth-row.auth-skin::before, .auth-skin-default .auth-row.auth-skin::after {
|
||||||
|
content: '';
|
||||||
|
border: 0;
|
||||||
|
}
|
127
public/catch-admin/assets/module/authtree/tree_themes/auth-skin-universal.css
Executable file
127
public/catch-admin/assets/module/authtree/tree_themes/auth-skin-universal.css
Executable file
@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
Author: lipengfei886
|
||||||
|
Email: lipengfei886@qq.com
|
||||||
|
Name: auth-skin-universal
|
||||||
|
Comment: lipengfei886@qq.com提供的绿色背景样式
|
||||||
|
Version: v1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*请在js中的.auth-status前的div加一个class属性auth-skin*/
|
||||||
|
.auth-skin-universal .auth-status {
|
||||||
|
margin-top: -12px;
|
||||||
|
}
|
||||||
|
.auth-skin-universal .auth-status > div {
|
||||||
|
position: relative;
|
||||||
|
top: 6px;
|
||||||
|
padding-left: 17px;
|
||||||
|
}
|
||||||
|
.auth-skin-universal .auth-status > div.layui-form-radio {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
/*美化的多选框/单选框DOM*/
|
||||||
|
.auth-skin-universal .auth-status > div > span {
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
top: -3px;
|
||||||
|
left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-universal .auth-status >.layui-form-checkbox > span{
|
||||||
|
display:inline-block;
|
||||||
|
padding:3px 8px;
|
||||||
|
text-decoration:none;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background-color: #009688 !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
.auth-skin-universal .auth-status >.layui-form-radio > div{
|
||||||
|
display:inline-block;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding:3px 8px;
|
||||||
|
text-decoration:none;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background-color: #009688 !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
.auth-skin-universal .auth-status >.layui-form-radio{
|
||||||
|
line-height: normal!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.auth-skin-universal .auth-skin {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 13px 5px 0 5px;
|
||||||
|
position: relative;
|
||||||
|
color: #009688;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-universal .auth-skin::before, .auth-skin-universal .auth-skin::after {
|
||||||
|
content: '';
|
||||||
|
left: -32.5px;
|
||||||
|
position: absolute;
|
||||||
|
right: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-universal .auth-skin::before {
|
||||||
|
border-left: 1px dashed #009688;
|
||||||
|
bottom: 50px;
|
||||||
|
height: 100%;
|
||||||
|
top: -4px;
|
||||||
|
width: 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-universal .auth-skin::after {
|
||||||
|
border-top: 1px dashed #009688;
|
||||||
|
height: 20px;
|
||||||
|
top: 24px; /*radio时 改为24px checkbox时改为27px*/
|
||||||
|
width: 40px
|
||||||
|
}
|
||||||
|
|
||||||
|
/*解决多层数据中,auth-child缩进长度的问题*/
|
||||||
|
.auth-skin-universal > .auth-single .auth-single {
|
||||||
|
margin-left: 58px;
|
||||||
|
}
|
||||||
|
.auth-skin-universal > .auth-single > .auth-skin > .auth-child .auth-child {
|
||||||
|
margin-left: 18px;
|
||||||
|
}
|
||||||
|
.auth-skin-universal > .auth-single > .auth-skin {
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-universal > .auth-single > .auth-skin::before, .auth-skin-universal > .auth-single > .auth-skin::after {
|
||||||
|
border: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-skin-universal > .auth-single > .auth-skin:last-child::before {
|
||||||
|
height: 30px
|
||||||
|
}
|
||||||
|
/*隐藏左侧操作的特殊样式*/
|
||||||
|
.auth-skin-universal.auth-tree-hidechoose .auth-status > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.auth-skin-universal.auth-tree-hidechoose .auth-status > div > span {
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
.auth-skin-universal.auth-tree-hidechoose .auth-status > i {
|
||||||
|
position: relative;
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*放于同一行的样式*/
|
||||||
|
.auth-skin-universal .auth-row {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.auth-skin-universal > .auth-single .auth-child .auth-row {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
.auth-skin-universal .auth-row.auth-skin::before, .auth-skin-universal .auth-row.auth-skin::after {
|
||||||
|
content: '';
|
||||||
|
border: 0;
|
||||||
|
}
|
16
public/catch-admin/assets/module/authtree/tree_themes/template.css
Executable file
16
public/catch-admin/assets/module/authtree/tree_themes/template.css
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
Author: Jeffrey Wang
|
||||||
|
Email: admin@wj2015.com
|
||||||
|
Name: authtree主题模板
|
||||||
|
Comment: 备注,用于该主题的描述
|
||||||
|
Version: v1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
主题开发规范:
|
||||||
|
- 主题的名称均按照 auth-skin-自定义名称 的规则进行,禁止中文,因为会造成部分服务器加载失败,比如 auth-skin-bob、auth-skin-shark、auth-skin-deepblue
|
||||||
|
- 主题的文件均放在 layui_exts/tree_themes/ 目录下,主题的名称与CSS的名称需要完全一致,否则主题文件自动加载会失效,比如名称叫做 auth-skin-bob,样式文件路径为 layui_exts/tree_themes/auth-skin-bob
|
||||||
|
- 每个主题样式必须指定作用域,作用域均为 .auth-skin-自定义名称,比如 auth-skin-bob主题需要改造字体颜色为 #666 即可写为 .auth-skin-bob .auth-status > div > span {color: #666;}
|
||||||
|
- 每个主题需要适配左侧有单选/多选框以及隐藏的情况,两者的区别在于顶级作用域多了一个 .auth-tree-hidechoose 类,比如 .auth-skin-bob.auth-tree-hidechoose .auth-status {color: #666}
|
||||||
|
- 主题开发完毕后可以在GitHub上提 Pull Request,或者私聊 admin@wj2015.com,我会更新到文档中以供用户选择
|
||||||
|
*/
|
@ -1,18 +0,0 @@
|
|||||||
.treeTable-empty {
|
|
||||||
width: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.treeTable-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.treeTable-icon .layui-icon-triangle-d:before {
|
|
||||||
content: "\e623";
|
|
||||||
}
|
|
||||||
|
|
||||||
.treeTable-icon.open .layui-icon-triangle-d:before {
|
|
||||||
content: "\e625";
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
@ -1,206 +0,0 @@
|
|||||||
layui.define(['layer', 'table'], function (exports) {
|
|
||||||
var $ = layui.jquery;
|
|
||||||
var layer = layui.layer;
|
|
||||||
var table = layui.table;
|
|
||||||
|
|
||||||
var treetable = {
|
|
||||||
// 渲染树形表格
|
|
||||||
render: function (param) {
|
|
||||||
// 检查参数
|
|
||||||
if (!treetable.checkParam(param)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 获取数据
|
|
||||||
if (param.data) {
|
|
||||||
treetable.init(param, param.data);
|
|
||||||
} else {
|
|
||||||
$.getJSON(param.url, param.where, function (res) {
|
|
||||||
treetable.init(param, res.data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 渲染表格
|
|
||||||
init: function (param, data) {
|
|
||||||
var mData = [];
|
|
||||||
var doneCallback = param.done;
|
|
||||||
var tNodes = data;
|
|
||||||
// 补上id和pid字段
|
|
||||||
for (var i = 0; i < tNodes.length; i++) {
|
|
||||||
var tt = tNodes[i];
|
|
||||||
if (!tt.id) {
|
|
||||||
if (!param.treeIdName) {
|
|
||||||
layer.msg('参数treeIdName不能为空', {icon: 5});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tt.id = tt[param.treeIdName];
|
|
||||||
}
|
|
||||||
if (!tt.pid) {
|
|
||||||
if (!param.treePidName) {
|
|
||||||
layer.msg('参数treePidName不能为空', {icon: 5});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tt.pid = tt[param.treePidName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对数据进行排序
|
|
||||||
var sort = function (s_pid, data) {
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
if (data[i].pid == s_pid) {
|
|
||||||
var len = mData.length;
|
|
||||||
if (len > 0 && mData[len - 1].id == s_pid) {
|
|
||||||
mData[len - 1].isParent = true;
|
|
||||||
}
|
|
||||||
mData.push(data[i]);
|
|
||||||
sort(data[i].id, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sort(param.treeSpid, tNodes);
|
|
||||||
|
|
||||||
// 重写参数
|
|
||||||
param.url = undefined;
|
|
||||||
param.data = mData;
|
|
||||||
param.page = {
|
|
||||||
count: param.data.length,
|
|
||||||
limit: param.data.length
|
|
||||||
};
|
|
||||||
param.cols[0][param.treeColIndex].templet = function (d) {
|
|
||||||
var mId = d.id;
|
|
||||||
var mPid = d.pid;
|
|
||||||
var isDir = d.isParent;
|
|
||||||
var emptyNum = treetable.getEmptyNum(mPid, mData);
|
|
||||||
var iconHtml = '';
|
|
||||||
for (var i = 0; i < emptyNum; i++) {
|
|
||||||
iconHtml += '<span class="treeTable-empty"></span>';
|
|
||||||
}
|
|
||||||
if (isDir) {
|
|
||||||
iconHtml += '<i class="layui-icon layui-icon-triangle-d"></i> <i class="layui-icon layui-icon-layer"></i>';
|
|
||||||
} else {
|
|
||||||
iconHtml += '<i class="layui-icon layui-icon-file"></i>';
|
|
||||||
}
|
|
||||||
iconHtml += ' ';
|
|
||||||
var ttype = isDir ? 'dir' : 'file';
|
|
||||||
var vg = '<span class="treeTable-icon open" lay-tid="' + mId + '" lay-tpid="' + mPid + '" lay-ttype="' + ttype + '">';
|
|
||||||
return vg + iconHtml + d[param.cols[0][param.treeColIndex].field] + '</span>'
|
|
||||||
};
|
|
||||||
|
|
||||||
param.done = function (res, curr, count) {
|
|
||||||
$(param.elem).next().addClass('treeTable');
|
|
||||||
$('.treeTable .layui-table-page').css('display', 'none');
|
|
||||||
$(param.elem).next().attr('treeLinkage', param.treeLinkage);
|
|
||||||
// 绑定事件换成对body绑定
|
|
||||||
/*$('.treeTable .treeTable-icon').click(function () {
|
|
||||||
treetable.toggleRows($(this), param.treeLinkage);
|
|
||||||
});*/
|
|
||||||
if (param.treeDefaultClose) {
|
|
||||||
treetable.foldAll(param.elem);
|
|
||||||
}
|
|
||||||
if (doneCallback) {
|
|
||||||
doneCallback(res, curr, count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 渲染表格
|
|
||||||
table.render(param);
|
|
||||||
},
|
|
||||||
// 计算缩进的数量
|
|
||||||
getEmptyNum: function (pid, data) {
|
|
||||||
var num = 0;
|
|
||||||
if (!pid) {
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
var tPid;
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
if (pid == data[i].id) {
|
|
||||||
num += 1;
|
|
||||||
tPid = data[i].pid;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num + treetable.getEmptyNum(tPid, data);
|
|
||||||
},
|
|
||||||
// 展开/折叠行
|
|
||||||
toggleRows: function ($dom, linkage) {
|
|
||||||
var type = $dom.attr('lay-ttype');
|
|
||||||
if ('file' == type) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var mId = $dom.attr('lay-tid');
|
|
||||||
var isOpen = $dom.hasClass('open');
|
|
||||||
if (isOpen) {
|
|
||||||
$dom.removeClass('open');
|
|
||||||
} else {
|
|
||||||
$dom.addClass('open');
|
|
||||||
}
|
|
||||||
$dom.closest('tbody').find('tr').each(function () {
|
|
||||||
var $ti = $(this).find('.treeTable-icon');
|
|
||||||
var pid = $ti.attr('lay-tpid');
|
|
||||||
var ttype = $ti.attr('lay-ttype');
|
|
||||||
var tOpen = $ti.hasClass('open');
|
|
||||||
if (mId == pid) {
|
|
||||||
if (isOpen) {
|
|
||||||
$(this).hide();
|
|
||||||
if ('dir' == ttype && tOpen == isOpen) {
|
|
||||||
$ti.trigger('click');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$(this).show();
|
|
||||||
if (linkage && 'dir' == ttype && tOpen == isOpen) {
|
|
||||||
$ti.trigger('click');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 检查参数
|
|
||||||
checkParam: function (param) {
|
|
||||||
if (!param.treeSpid && param.treeSpid != 0) {
|
|
||||||
layer.msg('参数treeSpid不能为空', {icon: 5});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!param.treeColIndex && param.treeColIndex != 0) {
|
|
||||||
layer.msg('参数treeColIndex不能为空', {icon: 5});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
// 展开所有
|
|
||||||
expandAll: function (dom) {
|
|
||||||
$(dom).next('.treeTable').find('.layui-table-body tbody tr').each(function () {
|
|
||||||
var $ti = $(this).find('.treeTable-icon');
|
|
||||||
var ttype = $ti.attr('lay-ttype');
|
|
||||||
var tOpen = $ti.hasClass('open');
|
|
||||||
if ('dir' == ttype && !tOpen) {
|
|
||||||
$ti.trigger('click');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 折叠所有
|
|
||||||
foldAll: function (dom) {
|
|
||||||
$(dom).next('.treeTable').find('.layui-table-body tbody tr').each(function () {
|
|
||||||
var $ti = $(this).find('.treeTable-icon');
|
|
||||||
var ttype = $ti.attr('lay-ttype');
|
|
||||||
var tOpen = $ti.hasClass('open');
|
|
||||||
if ('dir' == ttype && tOpen) {
|
|
||||||
$ti.trigger('click');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
layui.link(layui.cache.base + 'treetable-lay/treetable.css');
|
|
||||||
|
|
||||||
// 给图标列绑定事件
|
|
||||||
$('body').on('click', '.treeTable .treeTable-icon', function () {
|
|
||||||
var treeLinkage = $(this).parents('.treeTable').attr('treeLinkage');
|
|
||||||
if ('true' == treeLinkage) {
|
|
||||||
treetable.toggleRows($(this), true);
|
|
||||||
} else {
|
|
||||||
treetable.toggleRows($(this), false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
exports('treetable', treetable);
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user