Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
N
nse-ui
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Members
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
吴超
nse-ui
Commits
32dc9cfc
Commit
32dc9cfc
authored
Aug 22, 2025
by
周海峰
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
用户组
parent
723e4652
显示空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
546 行增加
和
861 行删除
+546
-861
src/api/safetyManagement/groupConfig.js
+52
-0
src/views/safetyManagement/groupConfig/edit.vue
+310
-273
src/views/safetyManagement/groupConfig/index.vue
+184
-90
src/views/safetyManagement/roleConfig/edit.vue.new
+0
-498
没有找到文件。
src/api/safetyManagement/groupConfig.js
View file @
32dc9cfc
...
...
@@ -14,3 +14,55 @@ export function queryAll(query) {
params
:
query
})
}
export
function
query
(
data
)
{
return
request
({
url
:
'/console/usergroup/query'
,
method
:
'post'
,
data
:
data
})
}
export
function
initEdit
(
data
)
{
return
request
({
url
:
'/console/usergroup/initEdit'
,
method
:
'post'
,
data
:
data
})
}
export
function
add
(
data
)
{
return
request
({
url
:
'/console/usergroup/add'
,
method
:
'post'
,
data
:
data
})
}
export
function
modify
(
data
)
{
return
request
({
url
:
'/console/usergroup/modify'
,
method
:
'post'
,
data
:
data
})
}
export
function
del
(
data
)
{
return
request
({
url
:
'/console/usergroup/del'
,
method
:
'post'
,
data
:
data
})
}
export
function
checkNameExit
(
query
)
{
return
request
({
url
:
'/console/usergroup/checkNameExit'
,
method
:
'get'
,
params
:
query
})
}
src/views/safetyManagement/groupConfig/edit.vue
View file @
32dc9cfc
...
...
@@ -3,7 +3,7 @@
title=
"编辑用户组"
:model-value=
"visible"
@
update:model-value=
"$emit('update:visible', $event)"
width=
"
8
00px"
width=
"
6
00px"
:close-on-click-modal=
"false"
append-to-body
destroy-on-close
...
...
@@ -20,103 +20,61 @@
label-width=
"100px"
>
<!-- 姓名和用户组名 -->
<el-row
:gutter=
"20"
>
<el-col
:span=
"12"
>
<el-form-item
label=
"用户组名"
prop=
"groupname"
>
<el-input
v-model=
"form.groupname"
placeholder=
"请输入用户组名
"
/>
<el-input
v-model=
"form.groupname"
placeholder=
"请输入用户组名"
:disabled=
"form.id !== null"
@
blur=
"changeCheckNameExit
"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 备注 -->
<el-form-item
label=
"备注"
prop=
"remark"
>
<el-input
v-model=
"form.remark"
type=
"textarea"
placeholder=
"请输入备注"
/>
</el-form-item>
</el-form>
<!-- 角色选择 -->
<el-form-item
label=
"选择角色"
>
<div
class=
"role-transfer"
>
<div
class=
"transfer-list"
>
<div
class=
"transfer-header"
>
<span>
角色列表
</span>
<el-checkbox
v-model=
"allSelected"
@
change=
"handleSelectAll"
>
全选
</el-checkbox>
</div>
<el-input
v-model=
"searchRole"
placeholder=
"请输入搜索内容"
/>
<div
class=
"role-list"
>
<div
v-for=
"role in filteredRoles"
:key=
"role.id"
class=
"role-item"
>
<el-checkbox
v-model=
"role.selected"
@
change=
"(val) => handleRoleSelect(role, val)"
>
{{
role
.
name
}}
</el-checkbox>
</div>
</div>
</div>
<div
class=
"transfer-operation"
>
<el-button
type=
"primary"
plain
icon=
"ArrowRight"
@
click=
"addSelected"
/>
<el-button
type=
"primary"
plain
icon=
"ArrowLeft"
@
click=
"removeSelected"
/>
</div>
<div
class=
"transfer-list"
>
<div
class=
"transfer-header"
>
<span>
已选择角色列表
</span>
<el-button
link
type=
"primary"
@
click=
"clearSelected"
>
清空
</el-button>
</div>
<div
class=
"selected-list"
>
<div
v-for=
"role in selectedRoles"
:key=
"role.id"
class=
"role-item"
>
{{
role
.
name
}}
</div>
<div
class=
"component-wrapper"
>
<RoleTransfer
:roles=
"roles"
:selectedRoles=
"selectedRoles"
:searchRole=
"searchRole"
@
update:roles=
"val => roles = val"
@
update:selectedRoles=
"val => selectedRoles = val"
@
update:searchRole=
"val => searchRole = val"
/>
<div
v-if=
"isAdminUser"
class=
"overlay-mask"
>
<div
class=
"mask-message"
>
<el-alert
title=
"超级管理员用户组的角色不可修改"
type=
"info"
:closable=
"false"
show-icon
/>
</div>
</div>
</div>
</el-form-item>
<!-- 权限设置 -->
<el-form-item
label=
"权限设置"
>
<div
class=
"permission-area"
>
<div
class=
"all-select"
>
<el-checkbox
v-model=
"allPermissionsSelected"
@
change=
"handleAllPermissionsChange"
>
全选
</el-checkbox>
</div>
<div
class=
"permission-list"
>
<div
v-for=
"(item, index) in permissionList"
:key=
"index"
class=
"permission-item"
>
<!-- 父级权限项 -->
<div
class=
"permission-header"
@
click=
"toggleExpand(item)"
>
<div
class=
"left"
>
<el-icon
class=
"expand-icon"
:class=
"
{ expanded: item.expanded }">
<ArrowRight
/>
</el-icon>
<el-checkbox
v-model=
"item.selected"
:indeterminate=
"item.indeterminate"
@
change=
"(val) => handlePermissionChange(item, val)"
>
{{
item
.
name
}}
</el-checkbox>
</div>
<div
class=
"right"
>
<el-tag
size=
"small"
v-if=
"item.children"
>
{{
item
.
children
.
length
}}
</el-tag>
</div>
</div>
<!-- 子级权限列表 -->
<div
v-if=
"item.children"
class=
"sub-permissions"
v-show=
"item.expanded"
>
<el-checkbox
v-for=
"child in item.children"
:key=
"child.id"
v-model=
"child.selected"
@
change=
"(val) => handleSubPermissionChange(item, child, val)"
>
{{
child
.
name
}}
</el-checkbox>
</div>
<div
class=
"component-wrapper"
>
<PermissionArea
:permissionList=
"permissionList"
:allPermissionsSelected=
"allPermissionsSelected"
@
update:allPermissionsSelected=
"val => allPermissionsSelected = val"
@
permission-change=
"handlePermissionChange"
@
sub-permission-change=
"handleSubPermissionChange"
/>
<div
v-if=
"isAdminUser"
class=
"overlay-mask"
>
<div
class=
"mask-message"
>
<el-alert
title=
"超级管理员用户组的权限不可修改"
type=
"info"
:closable=
"false"
show-icon
/>
</div>
</div>
</div>
</el-form-item>
</el-form>
</div>
<template
#
footer
>
<div
class=
"dialog-footer"
>
...
...
@@ -129,10 +87,21 @@
</el-dialog>
</template>
<
script
>
export
default
{
name
:
'GroupEdit'
,
props
:
{
<
script
setup
>
import
{
ref
,
reactive
,
computed
,
watch
,
getCurrentInstance
}
from
'vue'
import
{
queryAll
as
queryAllRole
}
from
'@/api/safetyManagement/roleConfig.js'
import
{
initEdit
,
add
,
modify
,
checkNameExit
}
from
'@/api/safetyManagement/groupConfig.js'
import
{
queryAll
as
queryAllMenu
}
from
'@/api/safetyManagement/menuConfig.js'
import
RoleTransfer
from
'@/components/RoleTransfer/index.vue'
import
PermissionArea
from
'@/components/PermissionArea/index.vue'
// 定义组件名称
defineOptions
({
name
:
'GroupEdit'
})
// 定义props和emit
const
props
=
defineProps
({
visible
:
{
type
:
Boolean
,
default
:
false
...
...
@@ -141,186 +110,218 @@ export default {
type
:
Object
,
default
:
()
=>
null
}
},
emits
:
[
'update:visible'
,
'success'
],
watch
:
{
visible
(
val
)
{
if
(
val
&&
this
.
formData
)
{
this
.
form
=
{
...
this
.
formData
,
password
:
''
,
confirmPassword
:
''
}
}
}
},
data
()
{
// 密码验证
const
validatePassword
=
(
rule
,
value
,
callback
)
=>
{
if
(
value
===
''
)
{
callback
(
new
Error
(
'请输入密码'
))
})
const
emit
=
defineEmits
([
'update:visible'
,
'success'
])
// 表单引用
const
formRef
=
ref
(
null
)
// 响应式数据
const
form
=
reactive
({
id
:
null
,
groupname
:
''
,
groupList
:
[],
rolesList
:
[],
menuList
:
[]
})
const
searchRole
=
ref
(
''
)
const
roles
=
ref
([])
const
selectedRoles
=
ref
([])
const
allPermissionsSelected
=
ref
(
false
)
const
permissionList
=
ref
([])
const
instance
=
getCurrentInstance
()
// 判断是否为超级管理员用户组(roleId = 1)
const
isAdminUser
=
ref
(
false
)
const
rules
=
{
groupname
:
[
{
required
:
true
,
message
:
'请输入用户组名'
,
trigger
:
'blur'
}
]
}
// 监听visible变化
watch
(()
=>
props
.
visible
,
(
val
)
=>
{
if
(
val
&&
props
.
formData
&&
props
.
formData
.
id
)
{
// 弹窗打开且有id时,拉取用户组详情和相关数据
initEditData
(
props
.
formData
.
id
)
}
else
{
if
(
this
.
form
.
confirmPassword
!==
''
)
{
this
.
$refs
.
formRef
.
validateField
(
'confirmPassword'
)
// 新增,只需查询角色、用户组组、菜单权限
Promise
.
all
([
queryAllRole
(),
queryAllMenu
()
]).
then
(([
roleRes
,
menuRes
])
=>
{
// 角色
roles
.
value
=
(
roleRes
.
data
||
[]).
map
(
r
=>
({
id
:
r
.
id
,
name
:
r
.
rolename
||
r
.
name
||
''
,
selected
:
false
}))
selectedRoles
.
value
=
[]
// 权限
function
mapMenu
(
menuArr
)
{
return
(
menuArr
||
[]).
filter
(
item
=>
item
.
type
!==
'3'
||
item
.
type
===
1
).
map
(
item
=>
{
const
hasChildren
=
Array
.
isArray
(
item
.
children
)
&&
item
.
children
.
length
>
0
let
children
=
hasChildren
?
mapMenu
(
item
.
children
)
:
null
return
{
id
:
item
.
id
,
name
:
item
.
type
===
'2'
?
'项目管理-'
+
item
.
name
:
item
.
name
,
selected
:
false
,
indeterminate
:
false
,
expanded
:
false
,
children
}
callback
(
)
}
)
}
permissionList
.
value
=
mapMenu
(
menuRes
.
data
)
})
}
// 确认密码验证
const
validateConfirmPassword
=
(
rule
,
value
,
callback
)
=>
{
if
(
value
===
''
)
{
callback
(
new
Error
(
'请再次输入密码'
))
}
else
if
(
value
!==
this
.
form
.
password
)
{
callback
(
new
Error
(
'两次输入密码不一致!'
))
})
// 初始化编辑数据
const
initEditData
=
async
(
id
)
=>
{
// 1. 查询用户组详情
const
groupRes
=
await
initEdit
({
groupid
:
id
})
// 2. 查询角色、菜单权限
const
[
roleRes
,
menuRes
]
=
await
Promise
.
all
([
queryAllRole
(),
queryAllMenu
()
])
// 3. 数据匹配
Object
.
assign
(
form
,
{
...
groupRes
.
data
.
tBaseGroup
})
// 未选中角色
roles
.
value
=
roleRes
.
data
.
filter
(
r
=>
!
(
groupRes
.
data
.
tBaseGrouproleList
.
map
(
m
=>
m
.
roleid
)
||
[]).
includes
(
r
.
id
))
.
map
(
r
=>
({
id
:
r
.
id
,
name
:
r
.
rolename
||
r
.
name
||
''
}))
// 选中的角色
selectedRoles
.
value
=
roleRes
.
data
.
filter
(
r
=>
(
groupRes
.
data
.
tBaseGrouproleList
.
map
(
m
=>
m
.
roleid
)
||
[]).
includes
(
r
.
id
))
.
map
(
r
=>
({
id
:
r
.
id
,
name
:
r
.
rolename
||
r
.
name
||
''
}))
// 权限
function
mapMenu
(
menuArr
,
selectedIds
=
[])
{
return
(
menuArr
||
[]).
filter
(
item
=>
item
.
type
!==
'3'
||
item
.
type
===
1
).
map
(
item
=>
{
const
hasChildren
=
Array
.
isArray
(
item
.
children
)
&&
item
.
children
.
length
>
0
let
children
=
hasChildren
?
mapMenu
(
item
.
children
,
selectedIds
)
:
null
let
selected
=
selectedIds
.
includes
(
item
.
id
)
let
indeterminate
=
false
if
(
hasChildren
)
{
const
selectedCount
=
children
.
filter
(
c
=>
c
.
selected
).
length
if
(
selectedCount
===
0
)
{
selected
=
false
indeterminate
=
false
}
else
if
(
selectedCount
===
children
.
length
)
{
selected
=
true
indeterminate
=
false
}
else
{
callback
()
selected
=
false
indeterminate
=
true
}
}
return
{
form
:
{
groupname
:
''
,
remark
:
''
,
},
rules
:
{
groupname
:
[
{
required
:
true
,
message
:
'请输入用户组名'
,
trigger
:
'blur'
}
]
},
allSelected
:
false
,
searchRole
:
''
,
roles
:
[
{
id
:
1
,
name
:
'系统管理员'
,
selected
:
false
},
{
id
:
2
,
name
:
'2222'
,
selected
:
false
}
],
selectedRoles
:
[],
allPermissionsSelected
:
false
,
permissionList
:
[
{
id
:
1
,
name
:
'管理首页'
,
selected
:
false
,
id
:
item
.
id
,
name
:
item
.
type
===
'2'
?
'项目管理-'
+
item
.
name
:
item
.
name
,
selected
,
indeterminate
,
expanded
:
false
,
indeterminate
:
false
},
{
id
:
2
,
name
:
'项目管理'
,
selected
:
false
,
expanded
:
false
},
{
id
:
3
,
name
:
'规则管理'
,
selected
:
false
,
children
:
[
{
id
:
'3-1'
,
name
:
'规则管理子项1'
,
selected
:
false
},
{
id
:
'3-2'
,
name
:
'规则管理子项2'
,
selected
:
false
}
]
},
{
id
:
4
,
name
:
'资产库'
,
selected
:
false
},
{
id
:
5
,
name
:
'系统设置'
,
selected
:
false
,
children
:
[
{
id
:
'5-1'
,
name
:
'设置子项1'
,
selected
:
false
},
{
id
:
'5-2'
,
name
:
'设置子项2'
,
selected
:
false
}
]
},
{
id
:
6
,
name
:
'用户组管理'
,
selected
:
false
,
children
:
[
{
id
:
'6-1'
,
name
:
'用户组管理子项1'
,
selected
:
false
},
{
id
:
'6-2'
,
name
:
'用户组管理子项2'
,
selected
:
false
}
]
}
]
children
}
},
computed
:
{
filteredRoles
()
{
return
this
.
roles
.
filter
(
role
=>
role
.
name
.
toLowerCase
().
includes
(
this
.
searchRole
.
toLowerCase
())
)
})
}
},
methods
:
{
handleClose
()
{
this
.
$emit
(
'update:visible'
,
false
)
this
.
$refs
.
formRef
?.
resetFields
()
this
.
form
=
{
realname
:
''
,
permissionList
.
value
=
mapMenu
(
menuRes
.
data
,
groupRes
.
data
.
tBaseGroupmenuList
?
groupRes
.
data
.
tBaseGroupmenuList
.
map
(
m
=>
m
.
menuid
)
:
[])
}
// 关闭弹窗
const
handleClose
=
()
=>
{
emit
(
'update:visible'
,
false
)
formRef
.
value
?.
resetFields
()
Object
.
assign
(
form
,
{
groupname
:
''
,
password
:
''
,
confirmPassword
:
''
,
email
:
''
,
userGroup
:
''
,
remark
:
''
,
isDisabled
:
false
}
},
handleSubmit
()
{
this
.
$refs
.
formRef
.
validate
((
valid
)
=>
{
remark
:
''
})
}
// 提交表单
const
handleSubmit
=
()
=>
{
formRef
.
value
.
validate
((
valid
)
=>
{
if
(
valid
)
{
// 提交表单逻辑
const
params
=
{
...
this
.
form
}
// 如果是编辑模式且没有修改密码,则不提交密码字段
if
(
this
.
formData
&&
!
params
.
password
)
{
delete
params
.
password
delete
params
.
confirmPassword
}
console
.
log
(
'submit form'
,
params
)
// 调用接口保存数据
this
.
$emit
(
'success'
)
this
.
handleClose
()
const
params
=
{
group
:
{
id
:
form
.
id
||
null
,
remark
:
form
.
remark
||
''
,
groupname
:
form
.
groupname
||
''
}
}
// 选中的角色和分组和权限
params
.
roleList
=
selectedRoles
.
value
.
map
(
r
=>
r
.
id
)
const
selectedMenuIds
=
[]
function
collectSelectedMenus
(
menus
)
{
menus
.
forEach
(
menu
=>
{
if
(
menu
.
selected
)
{
selectedMenuIds
.
push
(
menu
.
id
)
}
if
(
menu
.
children
&&
menu
.
children
.
length
>
0
)
{
collectSelectedMenus
(
menu
.
children
)
}
})
},
handleSelectAll
(
val
)
{
this
.
roles
.
forEach
(
role
=>
{
role
.
selected
=
val
}
collectSelectedMenus
(
permissionList
.
value
)
params
.
menuList
=
selectedMenuIds
.
map
(
id
=>
id
)
// console.log('submit form', params)
if
(
params
.
group
.
id
){
modify
(
params
).
then
(
res
=>
{
if
(
res
.
code
===
'POP_00014'
)
{
instance
.
appContext
.
config
.
globalProperties
.
$modal
.
msgSuccess
(
'保存成功'
)
emit
(
'success'
)
handleClose
()
}
else
{
instance
.
appContext
.
config
.
globalProperties
.
$modal
.
msgError
(
res
.
msg
||
'保存失败'
)
}
}).
catch
(()
=>
{
instance
.
appContext
.
config
.
globalProperties
.
$modal
.
msgError
(
'保存异常'
)
})
},
handleRoleSelect
(
role
,
val
)
{
role
.
selected
=
val
this
.
allSelected
=
this
.
roles
.
every
(
role
=>
role
.
selected
)
},
addSelected
()
{
const
selectedRoles
=
this
.
roles
.
filter
(
role
=>
role
.
selected
)
this
.
selectedRoles
=
[...
new
Set
([...
this
.
selectedRoles
,
...
selectedRoles
])]
this
.
roles
=
this
.
roles
.
filter
(
role
=>
!
role
.
selected
)
this
.
allSelected
=
false
},
removeSelected
()
{
this
.
roles
=
[...
this
.
roles
,
...
this
.
selectedRoles
]
this
.
selectedRoles
=
[]
},
clearSelected
()
{
this
.
roles
=
[...
this
.
roles
,
...
this
.
selectedRoles
]
this
.
selectedRoles
=
[]
},
// 权限相关方法
handleAllPermissionsChange
(
val
)
{
// 处理全选
this
.
permissionList
.
forEach
(
item
=>
{
item
.
selected
=
val
if
(
item
.
children
)
{
item
.
children
.
forEach
(
child
=>
{
child
.
selected
=
val
}
else
{
add
(
params
).
then
(
res
=>
{
if
(
res
.
code
===
'POP_00014'
)
{
instance
.
appContext
.
config
.
globalProperties
.
$modal
.
msgSuccess
(
'保存成功'
)
emit
(
'success'
)
handleClose
()
}
else
{
instance
.
appContext
.
config
.
globalProperties
.
$modal
.
msgError
(
res
.
msg
||
'保存失败'
)
}
}).
catch
(()
=>
{
instance
.
appContext
.
config
.
globalProperties
.
$modal
.
msgError
(
'保存异常'
)
})
}
}
})
},
handlePermissionChange
(
item
,
val
)
{
// 处理父级权限选择
}
// 用户组名失去焦点时验证名字是否存在
const
changeCheckNameExit
=
async
()
=>
{
if
(
form
.
groupname
)
{
try
{
const
res
=
await
checkNameExit
({
groupname
:
form
.
groupname
});
if
(
res
.
code
!==
'POP_00014'
)
{
instance
.
appContext
.
config
.
globalProperties
.
$modal
.
msgError
(
'用户组名已存在'
);
}
}
catch
(
error
)
{
instance
.
appContext
.
config
.
globalProperties
.
$modal
.
msgError
(
'验证用户组名异常'
);
}
}
}
// 处理权限选择
const
handlePermissionChange
=
(
item
,
val
)
=>
{
item
.
selected
=
val
item
.
indeterminate
=
false
// 清除半选状态
if
(
item
.
children
)
{
...
...
@@ -328,10 +329,10 @@ export default {
child
.
selected
=
val
})
}
this
.
checkAllPermissionsStatus
()
},
handleSubPermissionChange
(
parent
,
child
,
val
)
{
// 处理子级权限选择
checkAllPermissionsStatus
()
}
const
handleSubPermissionChange
=
(
parent
,
child
,
val
)
=>
{
child
.
selected
=
val
// 检查父级状态
if
(
parent
.
children
)
{
...
...
@@ -343,22 +344,16 @@ export default {
parent
.
selected
=
allSelected
parent
.
indeterminate
=
!
allSelected
&&
someSelected
}
this
.
checkAllPermissionsStatus
()
},
checkAllPermissionsStatus
()
{
// 检查是否全部选中
this
.
allPermissionsSelected
=
this
.
permissionList
.
every
(
item
=>
{
checkAllPermissionsStatus
()
}
const
checkAllPermissionsStatus
=
()
=>
{
allPermissionsSelected
.
value
=
permissionList
.
value
.
every
(
item
=>
{
if
(
item
.
children
)
{
return
item
.
selected
&&
item
.
children
.
every
(
child
=>
child
.
selected
)
}
return
item
.
selected
})
},
toggleExpand
(
item
)
{
// 展开/收起子菜单
item
.
expanded
=
!
item
.
expanded
}
}
}
</
script
>
...
...
@@ -375,6 +370,7 @@ export default {
height
:
300px
;
display
:
flex
;
flex-direction
:
column
;
width
:
310px
;
.transfer-header
{
padding
:
8px
12px
;
...
...
@@ -383,6 +379,18 @@ export default {
justify-content
:
space-between
;
align-items
:
center
;
background-color
:
#f5f7fa
;
.select-all-action
{
color
:
#409EFF
;
cursor
:
pointer
;
font-size
:
14px
;
margin-left
:
10px
;
user-select
:
none
;
transition
:
color
0.2s
;
&:hover
{
color
:
#66b1ff
;
cursor
:
pointer
;
}
}
}
.role-list
,
.selected-list
{
...
...
@@ -415,9 +423,11 @@ export default {
width
:
100%
;
display
:
flex
;
flex-direction
:
column
;
height
:
350px
;
overflow-y
:
auto
;
.all-select
{
padding
:
8
px
12px
;
padding
:
2
px
12px
;
background-color
:
#f5f7fa
;
border-bottom
:
1px
solid
#dcdfe6
;
}
...
...
@@ -435,34 +445,26 @@ export default {
.permission-header
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
padding
:
12
px
0
;
justify-content
:
flex-start
;
padding
:
5
px
0
;
cursor
:
pointer
;
gap
:
12px
;
.left
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
.expand-icon
{
transition
:
transform
0.3s
;
font-size
:
12px
;
color
:
#909399
;
&.expanded
{
transform
:
rotate
(
90deg
);
}
}
}
}
.sub-permissions
{
padding
:
8px
0
8
px
32px
;
padding
:
5px
0
5
px
32px
;
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
background-color
:
#f8f9fb
;
border-top
:
1px
solid
#ebeef5
;
.el-checkbox
{
margin-left
:
0
!important
;
}
}
}
}
...
...
@@ -512,4 +514,39 @@ export default {
}
}
.permission-header
.expand-icon-placeholder
{
display
:
inline-block
;
min-width
:
22px
;
height
:
22px
;
vertical-align
:
middle
;
}
.permission-header
.expand-icon
{
min-width
:
22px
;
text-align
:
center
;
display
:
inline-flex
;
}
.component-wrapper
{
position
:
relative
;
}
.overlay-mask
{
position
:
absolute
;
top
:
0
;
left
:
0
;
right
:
0
;
bottom
:
0
;
background-color
:
rgba
(
255
,
255
,
255
,
0.7
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
z-index
:
10
;
}
.mask-message
{
padding
:
10px
;
border-radius
:
4px
;
width
:
80%
;
max-width
:
400px
;
}
</
style
>
src/views/safetyManagement/groupConfig/index.vue
View file @
32dc9cfc
...
...
@@ -11,10 +11,10 @@
<el-form
:inline=
"true"
:model=
"searchForm"
class=
"search-form"
>
<div
class=
"left-area"
>
<el-form-item
label=
"用户组名:"
>
<el-input
v-model=
"searchForm.groupname"
placeholder=
"请输入用户组名"
></el-input>
<el-input
v-model=
"searchForm.groupname"
clearable
placeholder=
"请输入用户组名"
></el-input>
</el-form-item>
<el-form-item
label=
"备注:"
>
<el-input
v-model=
"searchForm.remark"
placeholder=
"请输入备注"
></el-input>
<el-input
v-model=
"searchForm.remark"
clearable
placeholder=
"请输入备注"
></el-input>
</el-form-item>
<el-form-item>
<el-button
type=
"primary"
icon=
"Search"
@
click=
"handleSearch"
>
搜索
</el-button>
...
...
@@ -29,24 +29,36 @@
<!-- 用户列表 -->
<div
class=
"user-list"
>
<div
class=
"user-grid"
>
<div
v-for=
"(user, index) in userList"
:key=
"index"
class=
"user-card"
>
<div
v-for=
"(user, index) in groupList"
:key=
"index"
class=
"user-card"
>
<div
class=
"card-left-bar"
></div>
<div
class=
"user-info"
>
<div
class=
"avatar"
>
<el-avatar
:size=
"50"
icon=
"User"
></el-avatar>
</div>
<div
class=
"info"
>
<div
class=
"name"
>
{{
user
.
groupname
}}
</div>
<div
class=
"remark"
>
备注:
{{
user
.
remark
}}
</div>
<div
class=
"remark"
>
<el-icon><Edit
/></el-icon>
备注
{{
user
.
remark
}}
</div>
</div>
<div
class=
"avatar"
>
<el-avatar
:size=
"80"
icon=
"UserFilled"
style=
"background: #f6f8fa; color: #d3d8e0;"
/>
</div>
</div>
<div
class=
"card-divider"
></div>
<div
class=
"card-bottom"
>
<div>
<el-icon><Clock
/></el-icon>
{{
user
.
createtime
||
''
}}
</div>
<el-icon
class=
"lock"
><Lock
/></el-icon>
</div>
<!-- 遮罩层和操作按钮 -->
<div
class=
"hover-mask"
>
<div
class=
"operation-buttons"
>
<div
class=
"operation-btn"
@
click=
"handleDelete(user)"
>
<i
class=
"el-icon-delete"
></i
>
<el-icon><Delete
/></el-icon
>
<span>
删除
</span>
</div>
<div
class=
"operation-btn"
@
click=
"handleEdit(user)"
>
<i
class=
"el-icon-edit"
></i
>
<el-icon><FolderOpened
/></el-icon
>
<span>
编辑
</span>
</div>
</div>
...
...
@@ -54,11 +66,12 @@
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div
class=
"pagination"
>
<div
class=
"pagination-info"
>
共有记录 1条,每页显示 8条,共 1页
</div>
<div
class=
"pagination-info"
>
共有记录
{{
total
}}
条,每页显示
{{
pageSize
}}
条,共
{{
Math
.
ceil
(
total
/
pageSize
)
||
1
}}
页
</div>
<el-pagination
background
layout=
"prev, pager, next, jumper"
...
...
@@ -78,72 +91,85 @@
</div>
</
template
>
<
script
>
<
script
setup
>
import
{
ref
,
reactive
,
onMounted
}
from
'vue'
import
{
ElMessageBox
}
from
'element-plus'
import
GroupEdit
from
'./edit.vue'
import
{
query
,
del
}
from
'@/api/safetyManagement/groupConfig.js'
export
default
{
name
:
'GroupConfig'
,
components
:
{
GroupEdit
},
data
()
{
return
{
searchForm
:
{
const
searchForm
=
reactive
({
name
:
''
,
groupname
:
''
,
remark
:
''
},
userList
:
[
})
const
groupList
=
ref
(
[
{
realname
:
'admin'
,
groupname
:
'admin'
,
remark
:
''
// realname: 'admin',
// groupname: 'admin',
// remark: ''
}
])
const
total
=
ref
(
1
)
const
currentPage
=
ref
(
1
)
const
pageSize
=
ref
(
8
)
const
editVisible
=
ref
(
false
)
const
editData
=
ref
(
null
)
const
getList
=
async
()
=>
{
try
{
const
res
=
await
query
({
groupname
:
searchForm
.
groupname
,
remark
:
searchForm
.
remark
,
page
:
currentPage
.
value
,
rows
:
pageSize
.
value
})
if
(
res
.
code
===
'POP_00014'
)
{
groupList
.
value
=
res
.
data
.
list
||
[]
total
.
value
=
res
.
data
.
total
||
0
}
}
catch
(
error
)
{
console
.
error
(
'获取用户组列表失败'
,
error
)
}
],
total
:
1
,
currentPage
:
1
,
pageSize
:
8
,
editVisible
:
false
,
editData
:
null
}
},
methods
:
{
handleSearch
()
{
// 实现搜索逻辑
},
handleAdd
()
{
// 打开新增用户弹窗
this
.
editData
=
null
this
.
editVisible
=
true
},
handlePageChange
(
page
)
{
// 实现分页逻辑
},
handleDelete
(
row
)
{
// 实现删除用户逻辑
this
.
$confirm
(
'确认删除该用户组吗?'
,
'提示'
,
{
}
const
handleSearch
=
()
=>
{
currentPage
.
value
=
1
getList
()
}
const
handleAdd
=
()
=>
{
editData
.
value
=
null
editVisible
.
value
=
true
}
const
handlePageChange
=
(
page
)
=>
{
currentPage
.
value
=
page
getList
()
}
const
handleDelete
=
async
(
row
)
=>
{
ElMessageBox
.
confirm
(
'确认删除该用户组吗?'
,
'提示'
,
{
type
:
'warning'
}).
then
(
()
=>
{
}).
then
(
async
()
=>
{
// 调用删除接口
console
.
log
(
'删除用户组'
,
row
)
}).
catch
(()
=>
{})
},
handleEdit
(
row
)
{
// 打开编辑用户弹窗
this
.
editData
=
{
...
row
}
this
.
editVisible
=
true
},
handleEditSuccess
()
{
// 编辑成功后的回调
this
.
editVisible
=
false
// 刷新列表数据
this
.
getList
()
},
getList
()
{
// 获取用户列表数据
try
{
const
res
=
await
del
({
groupid
:
row
.
id
})
if
(
res
.
code
===
'POP_00014'
)
{
getList
()
}
}
catch
(
error
)
{
console
.
error
(
'删除用户组失败'
,
error
)
}
}).
catch
(()
=>
{})
}
const
handleEdit
=
(
row
)
=>
{
editData
.
value
=
{
...
row
}
editVisible
.
value
=
true
}
const
handleEditSuccess
=
()
=>
{
editVisible
.
value
=
false
getList
()
}
onMounted
(()
=>
{
getList
()
})
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -202,37 +228,105 @@ export default {
.user-card
{
background
:
#fff
;
border-radius
:
8
px
;
box-shadow
:
0
2px
12px
0
rgba
(
0
,
0
,
0
,
0.
1
);
border-radius
:
12
px
;
box-shadow
:
0
2px
12px
0
rgba
(
0
,
0
,
0
,
0.
04
);
overflow
:
hidden
;
position
:
relative
;
padding
:
0
;
border
:
none
;
transition
:
box-shadow
0.2s
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
&:hover
{
box-shadow
:
0
4px
24px
rgba
(
0
,
0
,
0
,
0.08
);
}
&
:hover
.hover-mask
{
display
:
flex
!important
;
}
}
.card-left-bar
{
position
:
absolute
;
left
:
0
;
top
:
24px
;
bottom
:
24px
;
width
:
8px
;
background
:
#409EFF
;
border-radius
:
4px
;
}
.user-info
{
display
:
flex
;
align-items
:
center
;
padding
:
2
0
px
;
align-items
:
flex-start
;
padding
:
2
4px
24px
0
24
px
;
position
:
relative
;
height
:
auto
;
cursor
:
pointer
;
height
:
100%
;
.avatar
{
margin-right
:
15px
;
margin-left
:
auto
;
margin-right
:
0
;
width
:
80px
;
height
:
80px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
.info
{
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
flex-start
;
.name
{
font-size
:
1
6
px
;
font-weight
:
bold
;
margin-bottom
:
8px
;
color
:
#333
;
font-size
:
1
5
px
;
font-weight
:
600
;
color
:
#409EFF
;
margin-bottom
:
12px
;
}
.groupname
,
.remark
{
.remark
{
font-size
:
12px
;
color
:
#666
;
font-size
:
14px
;
margin-bottom
:
5px
;
margin-bottom
:
8px
;
display
:
flex
;
align-items
:
center
;
gap
:
6px
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
max-width
:
300px
;
}
}
}
.card-divider
{
height
:
2px
;
background
:
#f2f6fa
;
margin
:
12px
24px
0
24px
;
border-radius
:
1px
;
}
.card-bottom
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
padding
:
8px
24px
12px
24px
;
color
:
#909399
;
font-size
:
15px
;
.el-icon
{
margin-right
:
6px
;
}
.lock
{
font-size
:
18px
;
color
:
#bdbdbd
;
margin-left
:
8px
;
}
}
...
...
@@ -243,13 +337,16 @@ export default {
width
:
100%
;
height
:
100%
;
background-color
:
rgba
(
0
,
0
,
0
,
0.6
);
display
:
none
;
display
:
none
!important
;
justify-content
:
center
;
align-items
:
center
;
z-index
:
2
;
.operation-buttons
{
display
:
flex
;
gap
:
30px
;
gap
:
80px
;
justify-content
:
center
;
align-items
:
center
;
.operation-btn
{
display
:
flex
;
...
...
@@ -257,14 +354,18 @@ export default {
align-items
:
center
;
color
:
white
;
cursor
:
pointer
;
background
:
none
;
border-radius
:
0
;
padding
:
0
;
box-shadow
:
none
;
transition
:
color
0.2s
;
i
{
font-size
:
24px
;
margin-bottom
:
8px
;
}
span
{
font-size
:
14px
;
font-weight
:
500
;
}
&
:hover
{
...
...
@@ -274,13 +375,6 @@ export default {
}
}
&
:hover
{
.hover-mask
{
display
:
flex
;
}
}
}
.operation-time
{
color
:
#666
;
i
{
...
...
src/views/safetyManagement/roleConfig/edit.vue.new
deleted
100644 → 0
View file @
723e4652
<template>
<el-dialog
title="编辑角色"
:model-value="visible"
@update:model-value="$emit('update:visible', $event)"
width="800px"
:close-on-click-modal="false"
append-to-body
destroy-on-close
:align-center="true"
:fullscreen="false"
@close="handleClose"
class="user-edit-dialog"
>
<div class="dialog-content" style="padding: 20px; padding-bottom: 80px;height: 700px;overflow-y: auto;">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
>
<!-- 姓名和角色名 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="角色名" prop="roleName">
<el-input v-model="form.roleName" placeholder="请输入角色名" />
</el-form-item>
</el-col>
</el-row>
<!-- 备注 -->
<el-form-item label="备注" prop="remark">
<el-input
v-model="form.remark"
type="textarea"
placeholder="请输入备注"
/>
</el-form-item>
<!-- 权限设置 -->
<el-form-item label="权限设置">
<div class="permission-area">
<div class="all-select">
<el-checkbox v-model="allPermissionsSelected" @change="handleAllPermissionsChange">全选</el-checkbox>
</div>
<div class="permission-list">
<div v-for="(item, index) in permissionList" :key="index" class="permission-item">
<!-- 父级权限项 -->
<div class="permission-header" @click="toggleExpand(item)">
<div class="left">
<el-icon class="expand-icon" :class="{ expanded: item.expanded }">
<component :is="item.expanded ? 'ArrowDown' : 'ArrowRight'" />
</el-icon>
<el-checkbox
v-model="item.selected"
:indeterminate="item.indeterminate"
@change="(val) => handlePermissionChange(item, val)"
>
{{ item.name }}
</el-checkbox>
</div>
<div class="right">
<el-tag size="small" v-if="item.children">{{ item.children.length }}</el-tag>
</div>
</div>
<!-- 子级权限列表 -->
<div v-if="item.children" class="sub-permissions" v-show="item.expanded">
<el-checkbox
v-for="child in item.children"
:key="child.id"
v-model="child.selected"
@change="(val) => handleSubPermissionChange(item, child, val)"
>
{{ child.name }}
</el-checkbox>
</div>
</div>
</div>
</div>
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<div class="footer-buttons">
<el-button @click="handleClose">取 消</el-button>
<el-button type="primary" @click="handleSubmit">确 定</el-button>
</div>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, computed, watch, nextTick } from 'vue'
import { queryAll as queryAllMenu } from '@/api/safetyManagement/menuConfig.js'
import { ArrowRight, ArrowDown } from '@element-plus/icons-vue'
// 定义组件名称
defineOptions({
name: 'RoleEdit'
})
// 定义props
const props = defineProps({
visible: {
type: Boolean,
default: false
},
formData: {
type: Object,
default: () => null
}
})
// 定义emits
const emit = defineEmits(['update:visible', 'success'])
// 表单引用
const formRef = ref(null)
// 表单数据
const form = reactive({
realname: '',
roleName: '',
password: '',
confirmPassword: '',
email: '',
userGroup: '',
remark: '',
isDisabled: false
})
// 表单校验规则
const rules = {
roleName: [
{ required: true, message: '请输入角色名', trigger: 'blur' }
]
}
// 响应式数据
const allSelected = ref(false)
const searchRole = ref('')
const selectedRoles = ref([])
const allPermissionsSelected = ref(false)
const permissionList = ref([
{
id: 1,
name: '管理首页',
selected: false,
expanded: false,
indeterminate: false
},
{
id: 2,
name: '项目管理',
selected: false,
expanded: false
},
{
id: 3,
name: '规则管理',
selected: false,
expanded: false,
indeterminate: false,
children: [
{ id: '3-1', name: '规则管理子项1', selected: false },
{ id: '3-2', name: '规则管理子项2', selected: false }
]
},
{
id: 4,
name: '资产库',
selected: false
},
{
id: 5,
name: '系统设置',
selected: false,
expanded: false,
indeterminate: false,
children: [
{ id: '5-1', name: '设置子项1', selected: false },
{ id: '5-2', name: '设置子项2', selected: false }
]
},
{
id: 6,
name: '用户管理',
selected: false,
expanded: false,
indeterminate: false,
children: [
{ id: '6-1', name: '用户管理子项1', selected: false },
{ id: '6-2', name: '用户管理子项2', selected: false }
]
}
])
// 计算属性
const filteredRoles = computed(() => {
return props.roles?.filter(role =>
role.name.toLowerCase().includes(searchRole.value.toLowerCase())
) || []
})
// 监听props变化
watch(() => props.visible, (val) => {
if (val && props.formData) {
Object.assign(form, {
...props.formData,
password: '',
confirmPassword: ''
})
}
})
// 方法
const handleClose = () => {
emit('update:visible', false)
nextTick(() => {
formRef.value?.resetFields()
Object.assign(form, {
realname: '',
roleName: '',
password: '',
confirmPassword: '',
email: '',
userGroup: '',
remark: '',
isDisabled: false
})
})
}
const handleSubmit = () => {
formRef.value.validate((valid) => {
if (valid) {
// 提交表单逻辑
const params = { ...form }
// 如果是编辑模式且没有修改密码,则不提交密码字段
if (props.formData && !params.password) {
delete params.password
delete params.confirmPassword
}
console.log('submit form', params)
// 调用接口保存数据
emit('success')
handleClose()
}
})
}
const handleSelectAll = (val) => {
if (props.roles) {
props.roles.forEach(role => {
role.selected = val
})
}
}
const handleRoleSelect = (role, val) => {
role.selected = val
allSelected.value = props.roles?.every(role => role.selected) || false
}
const addSelected = () => {
const selected = props.roles?.filter(role => role.selected) || []
selectedRoles.value = [...new Set([...selectedRoles.value, ...selected])]
// 这里应该更新props.roles,但由于是props,需要通过emit事件通知父组件更新
allSelected.value = false
}
const removeSelected = () => {
// 这里应该将selectedRoles添加回props.roles,但由于是props,需要通过emit事件通知父组件更新
selectedRoles.value = []
}
const clearSelected = () => {
// 这里应该将selectedRoles添加回props.roles,但由于是props,需要通过emit事件通知父组件更新
selectedRoles.value = []
}
// 权限相关方法
const handleAllPermissionsChange = (val) => {
// 处理全选
permissionList.value.forEach(item => {
item.selected = val
if (item.children) {
item.children.forEach(child => {
child.selected = val
})
}
item.indeterminate = false
})
allPermissionsSelected.value = val
}
const handlePermissionChange = (item, val) => {
// 处理父级权限选择
item.selected = val
item.indeterminate = false // 清除半选状态
if (item.children) {
item.children.forEach(child => {
child.selected = val
})
}
checkAllPermissionsStatus()
}
const handleSubPermissionChange = (parent, child, val) => {
// 处理子级权限选择
child.selected = val
// 检查父级状态
if (parent.children) {
// 如果所有子项都选中,父级完全选中
const allSelected = parent.children.every(c => c.selected)
// 如果部分子项选中,父级半选
const someSelected = parent.children.some(c => c.selected)
parent.selected = allSelected
parent.indeterminate = !allSelected && someSelected
}
checkAllPermissionsStatus()
}
const checkAllPermissionsStatus = () => {
// 检查是否全部选中
allPermissionsSelected.value = permissionList.value.every(item => {
if (item.children) {
return item.selected && item.children.every(child => child.selected)
}
return item.selected
})
}
const toggleExpand = (item) => {
// 展开/收起子菜单
item.expanded = !item.expanded
}
</script>
<style lang="scss" scoped>
.role-transfer {
display: flex;
align-items: flex-start;
gap: 10px;
.transfer-list {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 4px;
height: 300px;
display: flex;
flex-direction: column;
.transfer-header {
padding: 8px 12px;
border-bottom: 1px solid #dcdfe6;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f5f7fa;
}
.role-list, .selected-list {
flex: 1;
overflow-y: auto;
padding: 6px 0;
}
.role-item {
padding: 6px 12px;
cursor: pointer;
&:hover {
background-color: #f5f7fa;
}
}
}
.transfer-operation {
display: flex;
flex-direction: column;
gap: 10px;
padding: 10px 0;
}
}
.permission-area {
border: 1px solid #dcdfe6;
border-radius: 4px;
width: 100%;
display: flex;
flex-direction: column;
max-height: 400px;
overflow-y: auto;
.all-select {
padding: 8px 12px;
background-color: #f5f7fa;
border-bottom: 1px solid #dcdfe6;
position: sticky;
top: 0;
z-index: 1;
}
.permission-list {
padding: 12px;
.permission-item {
border-bottom: 1px solid #ebeef5;
&:last-child {
border-bottom: none;
}
.permission-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
cursor: pointer;
.left {
display: flex;
align-items: center;
gap: 8px;
.expand-icon {
transition: transform 0.3s;
font-size: 12px;
color: #909399;
&.expanded {
transform: rotate(90deg);
}
}
}
}
.sub-permissions {
padding: 8px 0 8px 32px;
display: flex;
flex-direction: column;
gap: 12px;
background-color: #f8f9fb;
border-top: 1px solid #ebeef5;
}
}
}
}
:deep(.el-transfer-panel) {
width: 100%;
}
.user-edit-dialog {
:deep(.el-dialog) {
display: flex;
flex-direction: column;
margin: 0 auto !important;
max-height: 90vh;
position: relative;
.el-dialog__body {
flex: 1;
overflow: hidden;
padding: 0;
}
.dialog-content {
height: 100%;
overflow-y: auto;
padding: 20px;
padding-bottom: 80px;
}
}
}
.dialog-footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 20px;
background-color: #fff;
border-top: 1px solid #dcdfe6;
z-index: 9999;
.footer-buttons {
display: flex;
gap: 10px;
justify-content: flex-end;
}
}
</style>
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论