mirror of
https://gitee.com/ssssssss-team/magic-boot.git
synced 2025-01-31 17:12:49 +08:00
角色管理 分配权限 数据权限 封装pd-tree
This commit is contained in:
parent
e275ae841c
commit
c954eb5eed
@ -5,7 +5,7 @@
|
|||||||
"groupId" : "1952f25c81084e24b55b11385767dc38",
|
"groupId" : "1952f25c81084e24b55b11385767dc38",
|
||||||
"name" : "登录",
|
"name" : "登录",
|
||||||
"createTime" : null,
|
"createTime" : null,
|
||||||
"updateTime" : 1641276726183,
|
"updateTime" : 1642080684959,
|
||||||
"lock" : "0",
|
"lock" : "0",
|
||||||
"method" : "POST",
|
"method" : "POST",
|
||||||
"path" : "/login",
|
"path" : "/login",
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": \"565504aa-ecfa-4c0d-a22f-d35732672708\",\n \"timestamp\": 1641276642379,\n \"executeTime\": 13\n}",
|
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": \"565504aa-ecfa-4c0d-a22f-d35732672708\",\n \"timestamp\": 1641276642379,\n \"executeTime\": 13\n}",
|
||||||
"description" : null,
|
"description" : null,
|
||||||
"requestBodyDefinition" : {
|
"requestBodyDefinition" : {
|
||||||
"name" : "",
|
"name" : "root",
|
||||||
"value" : "",
|
"value" : "",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"required" : false,
|
"required" : false,
|
||||||
|
246
data/magic-api/api/后台/用户管理/所有机构.ms
Normal file
246
data/magic-api/api/后台/用户管理/所有机构.ms
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
{
|
||||||
|
"properties" : { },
|
||||||
|
"id" : "55ddadf56dfe433baae1b5c26580cf00",
|
||||||
|
"script" : null,
|
||||||
|
"groupId" : "4f0230049d7e4f39b1e0897cc0f46f9a",
|
||||||
|
"name" : "所有机构",
|
||||||
|
"createTime" : null,
|
||||||
|
"updateTime" : 1642085289368,
|
||||||
|
"lock" : "0",
|
||||||
|
"method" : "GET",
|
||||||
|
"path" : "/offices",
|
||||||
|
"parameters" : [ ],
|
||||||
|
"option" : "[]",
|
||||||
|
"requestBody" : "",
|
||||||
|
"headers" : [ ],
|
||||||
|
"paths" : [ ],
|
||||||
|
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": {\n \"list\": [{\n \"id\": \"4c37a80226134bce8bc91c8fc04a7d2f\",\n \"label\": \"马小东公司\",\n \"pid\": \"0\",\n \"children\": [{\n \"id\": \"81b5493cdb31477f9ceea85b2cd08f15\",\n \"label\": \"马小东子公司\",\n \"pid\": \"4c37a80226134bce8bc91c8fc04a7d2f\",\n \"children\": null\n }, {\n \"id\": \"31b365008f994e93850186126fce87cb\",\n \"label\": \"马小东子公司2\",\n \"pid\": \"4c37a80226134bce8bc91c8fc04a7d2f\",\n \"children\": null\n }]\n }, {\n \"id\": \"935126327e2a4f90b3f485f818f61ea5\",\n \"label\": \"吕金泽公司\",\n \"pid\": \"0\",\n \"children\": [{\n \"id\": \"1ae0299fc79b43c6b0ae2e14756ec41a\",\n \"label\": \"吕金泽子公司\",\n \"pid\": \"935126327e2a4f90b3f485f818f61ea5\",\n \"children\": null\n }]\n }],\n \"total\": 2\n },\n \"timestamp\": 1642085263335,\n \"executeTime\": 52\n}",
|
||||||
|
"description" : null,
|
||||||
|
"requestBodyDefinition" : null,
|
||||||
|
"responseBodyDefinition" : {
|
||||||
|
"name" : "",
|
||||||
|
"value" : "",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Object",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ {
|
||||||
|
"name" : "code",
|
||||||
|
"value" : "200",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Integer",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "message",
|
||||||
|
"value" : "success",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "String",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "data",
|
||||||
|
"value" : "",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Object",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ {
|
||||||
|
"name" : "list",
|
||||||
|
"value" : "",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Array",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ {
|
||||||
|
"name" : "",
|
||||||
|
"value" : "",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Object",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ {
|
||||||
|
"name" : "id",
|
||||||
|
"value" : "4c37a80226134bce8bc91c8fc04a7d2f",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "String",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "label",
|
||||||
|
"value" : "马小东公司",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "String",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "pid",
|
||||||
|
"value" : "0",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "String",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "children",
|
||||||
|
"value" : "",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Array",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ {
|
||||||
|
"name" : "",
|
||||||
|
"value" : "",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Object",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ {
|
||||||
|
"name" : "id",
|
||||||
|
"value" : "81b5493cdb31477f9ceea85b2cd08f15",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "String",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "label",
|
||||||
|
"value" : "马小东子公司",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "String",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "pid",
|
||||||
|
"value" : "4c37a80226134bce8bc91c8fc04a7d2f",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "String",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "children",
|
||||||
|
"value" : "null",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Object",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
} ]
|
||||||
|
} ]
|
||||||
|
} ]
|
||||||
|
} ]
|
||||||
|
}, {
|
||||||
|
"name" : "total",
|
||||||
|
"value" : "2",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Integer",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
} ]
|
||||||
|
}, {
|
||||||
|
"name" : "timestamp",
|
||||||
|
"value" : "1642085263335",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Long",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "executeTime",
|
||||||
|
"value" : "52",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Integer",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"optionMap" : { }
|
||||||
|
}
|
||||||
|
================================
|
||||||
|
var toTree = (list,pid) => select t.*,toTree(list,t.id) children from list t where t.pid = pid
|
||||||
|
var list = toTree(db.select('select id,name label,pid from sys_office where is_del = 0 order by sort'),'0')
|
||||||
|
|
||||||
|
return {
|
||||||
|
list: list,
|
||||||
|
total: list.getLength()
|
||||||
|
}
|
98
data/magic-api/api/后台/组织机构/根据角色获取组织机构.ms
Normal file
98
data/magic-api/api/后台/组织机构/根据角色获取组织机构.ms
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"properties" : { },
|
||||||
|
"id" : "dd6fd96602cb4905b5c2d79f584ab0a0",
|
||||||
|
"script" : null,
|
||||||
|
"groupId" : "8295fc13678d4144bf7363c465247a50",
|
||||||
|
"name" : "根据角色获取组织机构",
|
||||||
|
"createTime" : null,
|
||||||
|
"updateTime" : 1642080922495,
|
||||||
|
"lock" : "0",
|
||||||
|
"method" : "GET",
|
||||||
|
"path" : "/by/role",
|
||||||
|
"parameters" : [ ],
|
||||||
|
"option" : "[]",
|
||||||
|
"requestBody" : "",
|
||||||
|
"headers" : [ ],
|
||||||
|
"paths" : [ ],
|
||||||
|
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": [],\n \"timestamp\": 1642080916671,\n \"executeTime\": 90\n}",
|
||||||
|
"description" : null,
|
||||||
|
"requestBodyDefinition" : null,
|
||||||
|
"responseBodyDefinition" : {
|
||||||
|
"name" : "",
|
||||||
|
"value" : "",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Object",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ {
|
||||||
|
"name" : "code",
|
||||||
|
"value" : "200",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Integer",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "message",
|
||||||
|
"value" : "success",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "String",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "data",
|
||||||
|
"value" : "",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Array",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "timestamp",
|
||||||
|
"value" : "1642080916671",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Long",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
}, {
|
||||||
|
"name" : "executeTime",
|
||||||
|
"value" : "90",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Object",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"optionMap" : { }
|
||||||
|
}
|
||||||
|
================================
|
||||||
|
return db.select("""
|
||||||
|
select office_id id from sys_role_office where role_id = #{roleId}
|
||||||
|
""").map(it => it.id)
|
@ -4,8 +4,8 @@
|
|||||||
"script" : null,
|
"script" : null,
|
||||||
"groupId" : "67b2ce258e24491194b74992958c74aa",
|
"groupId" : "67b2ce258e24491194b74992958c74aa",
|
||||||
"name" : "根据角色获取菜单",
|
"name" : "根据角色获取菜单",
|
||||||
"createTime" : 1634724871165,
|
"createTime" : null,
|
||||||
"updateTime" : 1634724871165,
|
"updateTime" : 1642080927004,
|
||||||
"lock" : "0",
|
"lock" : "0",
|
||||||
"method" : "GET",
|
"method" : "GET",
|
||||||
"path" : "/by/role",
|
"path" : "/by/role",
|
||||||
@ -26,7 +26,7 @@
|
|||||||
"requestBody" : "",
|
"requestBody" : "",
|
||||||
"headers" : [ ],
|
"headers" : [ ],
|
||||||
"paths" : [ ],
|
"paths" : [ ],
|
||||||
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": [],\n \"timestamp\": 1634310952202,\n \"executeTime\": 9\n}",
|
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": [\"b1851d1b13594e71840103c11a37a669\", \"39be13ef6f0745568c80bf35202ddb2b\", \"414e5d31-fe05-4e69-9983-217c10b9740a\", \"d7e5280a2a8f4fb3b9e2f4a3e3093a38\", \"833b204d5f2c402190bfca677421cfeb\"],\n \"timestamp\": 1642080903333,\n \"executeTime\": 4\n}",
|
||||||
"description" : null,
|
"description" : null,
|
||||||
"requestBodyDefinition" : null,
|
"requestBodyDefinition" : null,
|
||||||
"responseBodyDefinition" : {
|
"responseBodyDefinition" : {
|
||||||
@ -75,10 +75,22 @@
|
|||||||
"validateType" : "",
|
"validateType" : "",
|
||||||
"error" : "",
|
"error" : "",
|
||||||
"expression" : "",
|
"expression" : "",
|
||||||
"children" : [ ]
|
"children" : [ {
|
||||||
|
"name" : "",
|
||||||
|
"value" : "b1851d1b13594e71840103c11a37a669",
|
||||||
|
"description" : "",
|
||||||
|
"required" : false,
|
||||||
|
"dataType" : "Object",
|
||||||
|
"type" : null,
|
||||||
|
"defaultValue" : null,
|
||||||
|
"validateType" : "",
|
||||||
|
"error" : "",
|
||||||
|
"expression" : "",
|
||||||
|
"children" : [ ]
|
||||||
|
} ]
|
||||||
}, {
|
}, {
|
||||||
"name" : "timestamp",
|
"name" : "timestamp",
|
||||||
"value" : "1634310952202",
|
"value" : "1642080903333",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"required" : false,
|
"required" : false,
|
||||||
"dataType" : "Long",
|
"dataType" : "Long",
|
||||||
@ -90,10 +102,10 @@
|
|||||||
"children" : [ ]
|
"children" : [ ]
|
||||||
}, {
|
}, {
|
||||||
"name" : "executeTime",
|
"name" : "executeTime",
|
||||||
"value" : "9",
|
"value" : "4",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"required" : false,
|
"required" : false,
|
||||||
"dataType" : "Integer",
|
"dataType" : "Object",
|
||||||
"type" : null,
|
"type" : null,
|
||||||
"defaultValue" : null,
|
"defaultValue" : null,
|
||||||
"validateType" : "",
|
"validateType" : "",
|
||||||
@ -106,5 +118,5 @@
|
|||||||
}
|
}
|
||||||
================================
|
================================
|
||||||
return db.select("""
|
return db.select("""
|
||||||
select sm.id,sm.name from sys_menu sm join sys_role_menu srm on sm.id = srm.menu_id and srm.role_id = #{roleId} where sm.is_del = 0
|
select menu_id id from sys_role_menu where role_id = #{roleId}
|
||||||
""")
|
""").map(it => it.id)
|
@ -5,7 +5,7 @@
|
|||||||
"groupId" : "89130d496f6f467c88b22ae4a7f688eb",
|
"groupId" : "89130d496f6f467c88b22ae4a7f688eb",
|
||||||
"name" : "保存",
|
"name" : "保存",
|
||||||
"createTime" : null,
|
"createTime" : null,
|
||||||
"updateTime" : 1641215973180,
|
"updateTime" : 1642081039231,
|
||||||
"lock" : "0",
|
"lock" : "0",
|
||||||
"method" : "POST",
|
"method" : "POST",
|
||||||
"path" : "/save",
|
"path" : "/save",
|
||||||
@ -26,6 +26,9 @@
|
|||||||
import '@get:/role/cache/delete' as cacheDelete
|
import '@get:/role/cache/delete' as cacheDelete
|
||||||
|
|
||||||
var role = {
|
var role = {
|
||||||
|
code,
|
||||||
|
permission,
|
||||||
|
descRibe,
|
||||||
type,
|
type,
|
||||||
name,
|
name,
|
||||||
sort,
|
sort,
|
||||||
@ -39,4 +42,11 @@ if(id){
|
|||||||
id = db.table("sys_role").primary("id").saveOrUpdate(role);
|
id = db.table("sys_role").primary("id").saveOrUpdate(role);
|
||||||
for(menuId in menus.split(',')){
|
for(menuId in menus.split(',')){
|
||||||
db.table("sys_role_menu").column("menu_id",menuId).column("role_id", id).insert();
|
db.table("sys_role_menu").column("menu_id",menuId).column("role_id", id).insert();
|
||||||
}
|
}
|
||||||
|
if(offices && permission == 1){
|
||||||
|
for(officeId in offices.split(',')){
|
||||||
|
db.table("sys_role_office").column("office_id",officeId).column("role_id", id).insert();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
db.table("sys_role_office").where().eq("role_id",id).delete()
|
||||||
|
}
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
"script" : null,
|
"script" : null,
|
||||||
"groupId" : "89130d496f6f467c88b22ae4a7f688eb",
|
"groupId" : "89130d496f6f467c88b22ae4a7f688eb",
|
||||||
"name" : "列表",
|
"name" : "列表",
|
||||||
"createTime" : 1634724871177,
|
"createTime" : null,
|
||||||
"updateTime" : 1634724871177,
|
"updateTime" : 1642061119644,
|
||||||
"lock" : "0",
|
"lock" : "0",
|
||||||
"method" : "GET",
|
"method" : "GET",
|
||||||
"path" : "/list",
|
"path" : "/list",
|
||||||
@ -156,6 +156,6 @@
|
|||||||
}
|
}
|
||||||
================================
|
================================
|
||||||
return db.page("""
|
return db.page("""
|
||||||
select id, name from sys_role where is_del = 0
|
select id, name, code, permission, desc_ribe from sys_role where is_del = 0
|
||||||
?{name, and name like concat('%',#{name},'%')}
|
?{name, and name like concat('%',#{name},'%')}
|
||||||
""")
|
""")
|
@ -7,16 +7,6 @@ export function getMenuTree() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMenusByRoleId(roleId) {
|
|
||||||
return request({
|
|
||||||
url: 'menu/by/role',
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
roleId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCurrentUserMenu() {
|
export function getCurrentUserMenu() {
|
||||||
return request({
|
return request({
|
||||||
url: 'menu/current/menus',
|
url: 'menu/current/menus',
|
||||||
|
120
magic-boot-ui/src/components/Psyduck/pd-tree.vue
Normal file
120
magic-boot-ui/src/components/Psyduck/pd-tree.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-button v-if="expand" type="primary" size="mini" icon="el-icon-sort" plain @click="doExpand">展开/折叠</el-button>
|
||||||
|
<el-button v-if="checked" type="primary" size="mini" icon="el-icon-check" plain @click="() => { treeAllChecked = !treeAllChecked; checkedAll(treeData, treeAllChecked) }">全选/全不选</el-button>
|
||||||
|
<el-tree
|
||||||
|
v-if="refreshTree"
|
||||||
|
ref="tree"
|
||||||
|
:data="treeData"
|
||||||
|
v-bind="el"
|
||||||
|
node-key="id"
|
||||||
|
:default-expand-all="defaultExpandAll"
|
||||||
|
@check-change="checkChange"
|
||||||
|
@node-click="nodeClick"
|
||||||
|
:props="defaultProps"
|
||||||
|
:style="{ 'max-height': maxHeight ? maxHeight : '100%' }"
|
||||||
|
style="overflow: auto"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PdTree',
|
||||||
|
props: {
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
selectValues: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
maxHeight: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
el: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
expand: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
checked: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async selectValues() {
|
||||||
|
await this.loadTreeData()
|
||||||
|
this.checkedAll(this.treeData, false)
|
||||||
|
var values = this.selectValues.split(',');
|
||||||
|
for(var i in values){
|
||||||
|
this.$refs.tree.setChecked(values[i], true, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
treeData: [],
|
||||||
|
defaultProps: {
|
||||||
|
children: 'children',
|
||||||
|
label: 'name'
|
||||||
|
},
|
||||||
|
defaultExpandAll: true,
|
||||||
|
refreshTree: true,
|
||||||
|
treeAllChecked: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
await this.loadTreeData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
doExpand() {
|
||||||
|
this.refreshTree = false
|
||||||
|
this.defaultExpandAll = !this.defaultExpandAll
|
||||||
|
this.$nextTick(() => this.refreshTree = true)
|
||||||
|
},
|
||||||
|
async loadTreeData() {
|
||||||
|
if(this.treeData.length == 0){
|
||||||
|
await this.$get(this.url, this.params).then((res) => {
|
||||||
|
this.treeData = res.data.list
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getTree() {
|
||||||
|
return this.$refs.tree
|
||||||
|
},
|
||||||
|
checkChange(node) {
|
||||||
|
var selectMenus = []
|
||||||
|
var checkedNodes = this.$refs.tree.getCheckedNodes(false, true)
|
||||||
|
for (var i = 0; i < checkedNodes.length; i++) {
|
||||||
|
selectMenus.push(checkedNodes[i].id)
|
||||||
|
}
|
||||||
|
this.$emit('update:select-values', selectMenus.join(','))
|
||||||
|
},
|
||||||
|
nodeClick(param1, param2, param3){
|
||||||
|
this.$emit('node-click', param1, param2, param3)
|
||||||
|
},
|
||||||
|
checkedAll(children, checked) {
|
||||||
|
if (this.$refs.tree) {
|
||||||
|
for (var i in children) {
|
||||||
|
var id = children[i].id
|
||||||
|
if(children[i].children && children[i].children.length > 0){
|
||||||
|
this.checkedAll(children[i].children, checked)
|
||||||
|
}
|
||||||
|
this.$refs.tree.setChecked(id, checked, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -7,7 +7,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||||
<el-form>
|
<el-form v-if="!isCollapse">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<treeselect
|
<treeselect
|
||||||
style="width: 90%;margin: 0px 5%"
|
style="width: 90%;margin: 0px 5%"
|
||||||
@ -99,20 +99,10 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.$get('menu/search').then(res => {
|
this.$get('menu/search').then(res => {
|
||||||
this.menuTree = res.data.list
|
this.menuTree = res.data.list
|
||||||
this.deleteEmptyChildren(this.menuTree)
|
this.$treeTable.deleteEmptyChildren(this.menuTree)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deleteEmptyChildren(children) {
|
|
||||||
for(var i in children){
|
|
||||||
var chi = children[i]
|
|
||||||
if(chi.children && chi.children.length == 0){
|
|
||||||
delete chi.children
|
|
||||||
}else{
|
|
||||||
this.deleteEmptyChildren(chi.children)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectMenu(node) {
|
selectMenu(node) {
|
||||||
if(node.url){
|
if(node.url){
|
||||||
this.$router.push({ path: node.url })
|
this.$router.push({ path: node.url })
|
||||||
|
@ -7,4 +7,7 @@ body {
|
|||||||
border: none;
|
border: none;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: #F3F3F3;
|
background: #F3F3F3;
|
||||||
}
|
}
|
||||||
|
.toolbar-container{
|
||||||
|
margin-bottom: 10px
|
||||||
|
}
|
||||||
|
@ -209,9 +209,7 @@ export default {
|
|||||||
expand(){
|
expand(){
|
||||||
this.refreshTable = false
|
this.refreshTable = false
|
||||||
this.tableOptions.el["default-expand-all"] = !this.tableOptions.el["default-expand-all"]
|
this.tableOptions.el["default-expand-all"] = !this.tableOptions.el["default-expand-all"]
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => this.refreshTable = true)
|
||||||
this.refreshTable = true
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
searchMenu() {
|
searchMenu() {
|
||||||
var _this = this
|
var _this = this
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<pd-tree ref="tree" :el="{ 'show-checkbox': true }" url="menu/tree" :select-values.sync="menus" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RoleAssignPermissions',
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
menus: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$get('menu/by/role',{ roleId: this.id }).then(res => {
|
||||||
|
this.menus = res.data.map((row) => row.id).join(',')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
save() {
|
||||||
|
this.$post('role/save', {
|
||||||
|
id: this.id,
|
||||||
|
menus: this.menus
|
||||||
|
}).then((response) => {
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '分配成功',
|
||||||
|
type: 'success',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
this.$emit('close')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -8,55 +8,94 @@
|
|||||||
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="reloadTable">
|
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="reloadTable">
|
||||||
搜索
|
搜索
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-permission="'role:save'" class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
|
<el-button class="filter-item" icon="el-icon-delete" @click="tableOptions.where = {}">
|
||||||
添加
|
清空
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<el-row class="toolbar-container">
|
||||||
|
<el-button v-permission="'role:save'" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">
|
||||||
|
添加
|
||||||
|
</el-button>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<pd-table ref="table" v-bind="tableOptions" />
|
<pd-table ref="table" v-bind="tableOptions" />
|
||||||
|
|
||||||
<pd-dialog ref="roleFormDialog" @confirm-click="save()">
|
<pd-dialog ref="roleFormDialog" @confirm-click="save()">
|
||||||
<template #content>
|
<template #content>
|
||||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="120px" style="width: 400px; margin-left:50px;">
|
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="right" label-width="120px" style="width: 900px;">
|
||||||
<el-form-item label="角色名称" prop="name">
|
<el-row :gutter="24">
|
||||||
<el-input v-model="temp.name" />
|
<el-col :span="12">
|
||||||
</el-form-item>
|
<el-form-item label="角色名称" prop="name">
|
||||||
<el-form-item label="选择菜单">
|
<el-input v-model="temp.name" />
|
||||||
<el-tree
|
</el-form-item>
|
||||||
ref="tree"
|
</el-col>
|
||||||
:data="menuTree"
|
<el-col :span="12">
|
||||||
show-checkbox
|
<el-form-item label="角色编码" prop="code">
|
||||||
node-key="id"
|
<el-input v-model="temp.code" />
|
||||||
:default-expand-all="true"
|
</el-form-item>
|
||||||
:props="defaultProps"
|
</el-col>
|
||||||
/>
|
</el-row>
|
||||||
</el-form-item>
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="角色描述" prop="descRibe">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入描述"
|
||||||
|
v-model="temp.descRibe">
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="菜单权限" prop="menus">
|
||||||
|
<pd-tree ref="tree" :el="{ 'show-checkbox': true }" max-height="320px" url="menu/tree" :select-values.sync="temp.menus" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="数据权限" prop="permission">
|
||||||
|
<pd-select v-model="temp.permission" :data="permissionData" />
|
||||||
|
<pd-tree v-if="temp.permission == 1" max-height="320px" :el="{ 'check-strictly': true, 'show-checkbox': true }" ref="office" url="office/tree" :select-values.sync="temp.offices" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
</pd-dialog>
|
</pd-dialog>
|
||||||
|
|
||||||
|
<pd-dialog ref="assignPermissionsDialog" @confirm-click="$refs.assignPermissions.save()">
|
||||||
|
<template #content>
|
||||||
|
<role-assign-permissions ref="assignPermissions" :key="Math.random()" :id="temp.id" @close="() => { $refs.assignPermissionsDialog.hide(); temp.id = '' }" />
|
||||||
|
</template>
|
||||||
|
</pd-dialog>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getMenusByRoleId } from '@/api/menu'
|
import RoleAssignPermissions from './role-assign-permissions'
|
||||||
import { getMenuTree } from '@/api/menu'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RoleList',
|
name: 'RoleList',
|
||||||
filters: {
|
components: { RoleAssignPermissions },
|
||||||
statusFilter(status) {
|
|
||||||
const statusMap = {
|
|
||||||
published: 'success',
|
|
||||||
draft: 'info',
|
|
||||||
deleted: 'danger'
|
|
||||||
}
|
|
||||||
return statusMap[status]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
permissionData: [{
|
||||||
|
label: '全部',
|
||||||
|
value: 0
|
||||||
|
}, {
|
||||||
|
label: '自定义',
|
||||||
|
value: 1
|
||||||
|
}, {
|
||||||
|
label: '本级及子级',
|
||||||
|
value: 2
|
||||||
|
}, {
|
||||||
|
label: '本级',
|
||||||
|
value: 3
|
||||||
|
}],
|
||||||
tableOptions: {
|
tableOptions: {
|
||||||
url: 'role/list',
|
url: 'role/list',
|
||||||
where: {
|
where: {
|
||||||
@ -67,6 +106,21 @@ export default {
|
|||||||
field: 'name',
|
field: 'name',
|
||||||
title: '角色名称'
|
title: '角色名称'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'code',
|
||||||
|
title: '角色编码'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'descRibe',
|
||||||
|
title: '角色描述'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'permission',
|
||||||
|
title: '数据权限',
|
||||||
|
templet: (row) => {
|
||||||
|
return this.permissionData[row.permission].label
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
type: 'btns',
|
type: 'btns',
|
||||||
@ -76,7 +130,8 @@ export default {
|
|||||||
{
|
{
|
||||||
permission: 'role:save',
|
permission: 'role:save',
|
||||||
title: '修改',
|
title: '修改',
|
||||||
type: 'primary',
|
type: 'text',
|
||||||
|
icon: 'el-icon-edit',
|
||||||
click: (row) => {
|
click: (row) => {
|
||||||
this.handleUpdate(row)
|
this.handleUpdate(row)
|
||||||
}
|
}
|
||||||
@ -84,7 +139,8 @@ export default {
|
|||||||
{
|
{
|
||||||
permission: 'role:delete',
|
permission: 'role:delete',
|
||||||
title: '删除',
|
title: '删除',
|
||||||
type: 'danger',
|
type: 'text',
|
||||||
|
icon: 'el-icon-delete',
|
||||||
click: (row) => {
|
click: (row) => {
|
||||||
this.$common.handleDelete({
|
this.$common.handleDelete({
|
||||||
url: 'role/delete',
|
url: 'role/delete',
|
||||||
@ -92,52 +148,60 @@ export default {
|
|||||||
done: () => this.reloadTable()
|
done: () => this.reloadTable()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
permission: 'role:permission',
|
||||||
|
title: '权限',
|
||||||
|
type: 'text',
|
||||||
|
icon: 'el-icon-plus',
|
||||||
|
click: (row) => {
|
||||||
|
this.temp.id = row.id
|
||||||
|
this.$refs.assignPermissionsDialog.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
menuTree: [],
|
temp: this.getTemp(),
|
||||||
defaultProps: {
|
|
||||||
children: 'children',
|
|
||||||
label: 'name'
|
|
||||||
},
|
|
||||||
selectMenus: [],
|
|
||||||
temp: {
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
menus: []
|
|
||||||
},
|
|
||||||
dialogStatus: '',
|
dialogStatus: '',
|
||||||
textMap: {
|
textMap: {
|
||||||
update: '修改',
|
update: '修改',
|
||||||
create: '添加'
|
create: '添加'
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
name: [{ required: true, message: '请输入角色名称', trigger: 'change' }]
|
name: [{ required: true, message: '请输入角色名称', trigger: 'change' }],
|
||||||
|
code: [{ required: true, message: '请输入角色编码', trigger: 'change' }]
|
||||||
},
|
},
|
||||||
downloadLoading: false
|
downloadLoading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
watch: {
|
||||||
getMenuTree().then(response => {
|
'temp.permission'() {
|
||||||
const { data } = response
|
if(this.temp.permission != 1){
|
||||||
this.menuTree = data.list
|
this.temp.offices = ''
|
||||||
})
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reloadTable() {
|
reloadTable() {
|
||||||
this.$refs.table.reloadList()
|
this.$refs.table.reloadList()
|
||||||
},
|
},
|
||||||
resetTemp() {
|
getTemp(){
|
||||||
this.temp = {
|
return {
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
menus: []
|
menus: '',
|
||||||
|
offices: '',
|
||||||
|
permission: 0,
|
||||||
|
code: '',
|
||||||
|
descRibe: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetTemp() {
|
||||||
|
this.temp = this.getTemp()
|
||||||
|
},
|
||||||
handleCreate() {
|
handleCreate() {
|
||||||
this.cancelSelectMenu()
|
|
||||||
this.resetTemp()
|
this.resetTemp()
|
||||||
this.dialogStatus = 'create'
|
this.dialogStatus = 'create'
|
||||||
this.$refs.roleFormDialog.show()
|
this.$refs.roleFormDialog.show()
|
||||||
@ -146,14 +210,8 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
save() {
|
save() {
|
||||||
this.selectMenus = []
|
|
||||||
var checkedNodes = this.$refs.tree.getCheckedNodes(false, true)
|
|
||||||
for (var i = 0; i < checkedNodes.length; i++) {
|
|
||||||
this.selectMenus.push(checkedNodes[i].id)
|
|
||||||
}
|
|
||||||
this.$refs['dataForm'].validate((valid) => {
|
this.$refs['dataForm'].validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
this.temp.menus = this.selectMenus.join(',')
|
|
||||||
this.$post('role/save', this.temp).then((response) => {
|
this.$post('role/save', this.temp).then((response) => {
|
||||||
this.reloadTable()
|
this.reloadTable()
|
||||||
this.$refs.roleFormDialog.hide()
|
this.$refs.roleFormDialog.hide()
|
||||||
@ -167,24 +225,15 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
cancelSelectMenu() {
|
|
||||||
if (this.$refs.tree) {
|
|
||||||
for (var i in this.menuTree) {
|
|
||||||
var menu = this.menuTree[i]
|
|
||||||
this.$refs.tree.setChecked(menu, false, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleUpdate(row) {
|
handleUpdate(row) {
|
||||||
this.cancelSelectMenu()
|
|
||||||
for (var t in this.temp) {
|
for (var t in this.temp) {
|
||||||
this.temp[t] = row[t]
|
this.temp[t] = row[t]
|
||||||
}
|
}
|
||||||
getMenusByRoleId(row.id).then(response => {
|
this.$get('menu/by/role',{ roleId: row.id }).then(res => {
|
||||||
const { data } = response
|
this.temp.menus = res.data.join(',')
|
||||||
for (var i = 0; i < data.length; i++) {
|
})
|
||||||
this.$refs.tree.setChecked(data[i], true, false)
|
this.$get('office/by/role',{ roleId: row.id }).then(res => {
|
||||||
}
|
this.temp.offices = res.data.join(',')
|
||||||
})
|
})
|
||||||
this.dialogStatus = 'update'
|
this.dialogStatus = 'update'
|
||||||
this.$refs.roleFormDialog.show()
|
this.$refs.roleFormDialog.show()
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<description>magic-boot</description>
|
<description>magic-boot</description>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<magic-api.version>1.7.4</magic-api.version>
|
<magic-api.version>1.7.5</magic-api.version>
|
||||||
<druid.version>1.2.1</druid.version>
|
<druid.version>1.2.1</druid.version>
|
||||||
<hutool-all.version>5.7.13</hutool-all.version>
|
<hutool-all.version>5.7.13</hutool-all.version>
|
||||||
<sa-token.version>1.26.0</sa-token.version>
|
<sa-token.version>1.26.0</sa-token.version>
|
||||||
|
Loading…
Reference in New Issue
Block a user