@@ -35,8 +35,8 @@
< el-option label = "全部角色" value = "" / >
< el-option
v-for = "role in roleList"
: key= "role.role_code"
:label = "role.role_name "
: key= "`${role.club_id || ''}-${ role.role_code}` "
:label = "roleClubLabel(role) "
:value = "role.role_code"
/ >
< / el-select >
@@ -53,12 +53,31 @@
< el -table -column prop = "phone" label = "账号" width = "140" / >
< el-table-column prop = "yonghuid" label = "用户ID" width = "100" / >
< el-table-column prop = "nicheng" label = "昵称" width = "150" / >
< el-table-column label = "角色 " min -width = " 18 0" >
< el-table-column label = "俱乐部任职 " min -width = " 20 0" >
< template # default = "{ row }" >
< el-tag v-for = "role in row.roles" :key="role.role_code" size="small" style="margin-right: 6px;" >
{{ role.role_name }}
< el-tag
v-for = "a in row.assignments || []"
:key = "a.id"
size = "small"
: type = "a.is_primary ? 'success' : 'info'"
style = "margin-right: 6px; margin-bottom: 4px;"
>
{ { a . club _label } } { { a . is _primary ? '·主' : '' } }
< / el-tag >
< span v-if = "!row.roles.length " > 无角色 < / span >
< span v-if = "!( row.assignments || []).length" class="muted-text " > 未分配 < / span >
< / template >
< / el -table -column >
< el-table-column label = "角色" min -width = " 200 " >
< template # default = "{ row }" >
< el-tag
v-for = "role in (row.roles || [])"
: key = "`${role.club_id || ''}-${role.role_code}`"
size = "small"
style = "margin-right: 6px;"
>
{ { roleClubLabel ( role ) } }
< / el-tag >
< span v-if = "!(row.roles || []).length" > 无角色 < / span >
< / template >
< / el -table -column >
< el-table-column prop = "status" label = "状态" width = "100" >
@@ -91,13 +110,15 @@
< / div >
< / div >
<!-- 用户详情弹窗 -- >
<!-- 用户详情弹窗 ( 须在 v - if / v - e l s e 外 , 避 免 被 条 件 渲 染 挡 住 ) - - >
< el-dialog
v-model = "detailDialogVisible"
: title= "`编辑用户 - ${currentUser?.phone}` "
width = "70 0px"
: title= "detailDialogTitle "
width = "72 0px"
class = "user-dialog"
destroy -on -close
append -t o -body
align -center
:close-on-click-modal = "false"
>
< div class = "dialog-content" >
<!-- 基本信息 -- >
@@ -140,10 +161,36 @@
< / el-form >
< / div >
<!-- 俱乐部任职 -- >
< div class = "club-section" >
< div class = "section-title" >
< span > 俱乐部任职 ( 切换范围 ) < / span >
< el-button type = "primary" size = "small" @click ="openAddClubDialog" >
< el -icon > < Plus / > < / el-icon > 添加俱乐部
< / el-button >
< / div >
< div class = "club-assign-list" >
< div
v-for = "a in currentUserAssignments"
:key = "a.id"
class = "club-assign-item"
>
< el-tag : type = "a.is_primary ? 'success' : 'info'" size = "small" >
{ { a . club _label } } { { a . is _primary ? '(主任职)' : '' } }
< / el-tag >
< span class = "assign-role-label" > { { a . role _name } } < / span >
< el-button type = "danger" link size = "small" @click ="removeClubAssignment(a)" > 移除 < / el -button >
< / div >
< div v-if = "currentUserAssignments.length === 0" class="empty-role" >
暂未分配俱乐部 , 添加后该用户顶栏可切换 ; 无角色则看不到菜单
< / div >
< / div >
< / div >
< ! - - 角色管理 - - >
< div class = "role-section" >
< div class = "section-title" >
< span > 所属角色 < / span >
< span > 功能角色 ( 按俱乐部 ) < / span >
< el-button type = "primary" size = "small" @click ="openAddRoleForUser" >
< el -icon > < Plus / > < / el-icon > 添加角色
< / el-button >
@@ -151,10 +198,10 @@
< div class = "role-list" >
< div
v-for = "role in currentUserRoles"
:key = "role.role_code"
:key = "`${role.club_id}-${ role.role_code}` "
class = "role-tag"
>
{ { role . role _name } }
{ { roleClubLabel ( role ) } }
< el-icon class = "remove-icon" @click ="removeUserRole(role)" >
< Close / >
< / el-icon >
@@ -176,14 +223,28 @@
< el-dialog
v-model = "addRoleForUserVisible"
title = "添加角色"
width = "50 0px"
width = "52 0px"
class = "role-select-dialog"
append -to -body
align -center
>
< el-form label -width = " 100px " >
< el-form-item label = "所属俱乐部" required >
< el-select v-model = "roleBindClubId" placeholder="先选俱乐部" style="width: 100%" @change="selectedRoleCodes = []" >
< el -option
v-for = "c in clubOptions"
:key = "c.club_id"
:label = "c.name"
:value = "c.club_id"
/ >
< / el-select >
< / el-form-item >
< / el-form >
< div class = "role-select-list" >
< el-checkbox-group v-model = "selectedRoleCodes" >
< div
v-for = "role in availableRolesForUser"
:key = "role.role_code"
:key = "`${role.club_id}-${ role.role_code}` "
class = "role-select-item"
>
< el-checkbox :label = "role.role_code" >
@@ -191,6 +252,8 @@
< / el-checkbox >
< / div >
< / el-checkbox-group >
< div v-if = "!roleBindClubId" class="empty-role" > 请先选择俱乐部 < / div >
< div v-else-if = "!availableRolesForUser.length" class="empty-role" > 该俱乐部下无可选角色 < / div >
< / div >
< template # footer >
< el-button @click ="addRoleForUserVisible = false" > 取消 < / el -button >
@@ -198,13 +261,47 @@
< / template >
< / el-dialog >
<!-- 添加俱乐部任职 -- >
< el-dialog v-model = "addClubVisible" title="添加俱乐部任职" width="480px" append -to -body align -center >
< el-form label -width = " 100px " >
< el-form-item label = "俱乐部" required >
< el-select v-model = "clubAssignForm.club_id" placeholder="选择俱乐部" style="width: 100%" >
< el -option
v-for = "c in availableClubsForAssign"
:key = "c.club_id"
:label = "c.name"
:value = "c.club_id"
/ >
< / el-select >
< / el-form-item >
< el-form-item label = "任职标签" >
< el-select v-model = "clubAssignForm.role_code" style="width: 100%" >
< el -option label = "俱乐部管理员" value = "CLUB_ADMIN" / >
< el-option label = "俱乐部总负责人" value = "CLUB_OWNER" / >
< el-option label = "俱乐部财务" value = "CLUB_FINANCE" / >
< el-option label = "俱乐部售后" value = "CLUB_AFTER_SALES" / >
< el-option label = "俱乐部运营" value = "CLUB_OPERATOR" / >
< / el-select >
< / el-form-item >
< el-form-item label = "主任职" >
< el-switch v-model = "clubAssignForm.is_primary" / >
< / el-form-item >
< / el-form >
< div class = "form-hint" > 添加后用户顶栏可切到该俱乐部 ; 再为该俱乐部绑功能角色才有菜单 。 < / div >
< template # footer >
< el-button @click ="addClubVisible = false" > 取消 < / el -button >
< el-button type = "primary" :loading = "assigningClub" @click ="confirmAddClubAssignment" > 保存 < / el -button >
< / template >
< / el-dialog >
<!-- 添加用户弹窗 -- >
< el-dialog
v-model = "addUserDialogVisible"
title = "添加后台用户"
width = "600px"
class = "user-dialog"
destroy -on -close
append -t o -body
align -center
>
< div class = "dialog-content" >
< el-form :model = "newUserForm" label -width = " 100px " ref = "addUserFormRef" >
@@ -235,11 +332,11 @@
< el-checkbox-group v-model = "newUserRoleCodes" >
< div
v-for = "role in roleList"
: key= "role.role_code"
: key= "`${role.club_id || ''}-${ role.role_code}` "
class = "role-select-item"
>
< el-checkbox :label = "role.role_code" >
< span > { { role . role _name } } < / span >
< span > { { roleClubLabel ( role ) } } < / span >
< / el-checkbox >
< / div >
< / el-checkbox-group >
@@ -249,20 +346,35 @@
< / div >
< template # footer >
< el-button @click ="addUserDialogVisible = false" > 取消 < / el -button >
< el-button type = "primary" @click ="confirmAddUser" > 确认添加 < / el -button >
< el-button type = "primary" :loading = "addingUser" :disabled = "addingUser" @click ="confirmAddUser" > 确认添加 < / el -button >
< / template >
< / el-dialog >
< / div >
< / template >
< script setup >
import { ref , computed , onMounted } from 'vue'
import { ref , computed , onMounted , nextTick } from 'vue'
import { ElMessage , ElMessageBox } from 'element-plus'
import { Plus , Close } from '@element-plus/icons-vue'
import request from '@/utils/request'
import { JITUAN _API , getAdminClubContext } from '@/utils/club-context'
const username = localStorage . getItem ( 'username' ) || ''
const clubOptions = ref ( [ ] )
const clubNameMap = computed ( ( ) => {
const m = { }
for ( const c of clubOptions . value ) m [ c . club _id ] = c . name
return m
} )
const roleClubLabel = ( role ) => {
if ( ! role ) return ''
const cid = role . club _id || ''
const cname = cid ? ( clubNameMap . value [ cid ] || cid ) : '全局'
return ` [ ${ cname } ] ${ role . role _name || role . role _code || '' } `
}
// 权限状态
const hasPermission = ref ( true )
@@ -293,13 +405,39 @@ const editForm = ref({
status : 1
} )
const currentUserRoles = ref ( [ ] )
const currentUserAssignments = ref ( [ ] )
const detailDialogTitle = computed ( ( ) => {
const u = currentUser . value
if ( ! u ) return '编辑用户'
return ` 编辑用户 - ${ u . phone || u . yonghuid || '' } `
} )
// 为用户添加角色弹窗
const addRoleForUserVisible = ref ( false )
const selectedRoleCodes = ref ( [ ] )
const roleBindClubId = ref ( '' )
// 俱乐部任职
const addClubVisible = ref ( false )
const assigningClub = ref ( false )
const clubAssignForm = ref ( { club _id : '' , role _code : 'CLUB_ADMIN' , is _primary : false } )
const availableClubsForAssign = computed ( ( ) => {
const assigned = new Set ( currentUserAssignments . value . map ( ( a ) => a . club _id ) )
return clubOptions . value . filter ( ( c ) => ! assigned . has ( c . club _id ) )
} )
const availableRolesForUser = computed ( ( ) => {
const existingCodes = currentUserRoles . value . map ( r => r . role _code )
return roleList . value . filter ( r => ! existingCodes . includes ( r . role _code ) )
if ( ! roleBindClubId . value ) return [ ]
const existing = new Set (
currentUserRoles . value
. filter ( ( r ) => ( r . club _id || '' ) === roleBindClubId . value )
. map ( ( r ) => r . role _code )
)
return roleList . value . filter (
( r ) => ( r . club _id || '' ) === roleBindClubId . value && ! existing . has ( r . role _code )
)
} )
// 添加用户弹窗
@@ -311,12 +449,16 @@ const newUserForm = ref({
nicheng : ''
} )
const newUserRoleCodes = ref ( [ ] )
const addingUser = ref ( false )
// ==================== 接口调用 ====================
// 获取角色列表(第一个接口 )
// 获取角色列表(全部俱乐部,用于分俱乐部绑角色 )
const fetchRoleList = async ( ) => {
try {
cons t res = await request . post ( '/houtai/hqyhjsgl' , { username } )
le t res = await request . post ( '/houtai/hqyhjsgl' , { username , all _clubs : true } )
if ( res . code !== 0 ) {
res = await request . post ( '/houtai/hqyhjsgl' , { username } )
}
if ( res . code === 0 ) {
roleList . value = res . data . roles || [ ]
return true
@@ -333,6 +475,20 @@ const fetchRoleList = async () => {
}
}
const loadClubOptions = async ( ) => {
try {
const res = await request . get ( JITUAN _API . clubList , { skipErrorToast : true } )
if ( res . code === 0 && Array . isArray ( res . data ) ) {
clubOptions . value = res . data . map ( ( c ) => ( { club _id : c . club _id , name : c . name } ) )
return
}
} catch {
// fallback
}
const ctx = getAdminClubContext ( )
clubOptions . value = ctx ? . clubs || [ ]
}
// 获取用户列表
const fetchUserList = async ( ) => {
if ( ! hasPermission . value ) return
@@ -372,16 +528,25 @@ const resetFilters = () => {
}
// 打开用户详情弹窗
const openDetailDialog = ( user ) => {
currentUser . value = user
editForm . value = {
nicheng : user . nicheng || '' ,
password : '' ,
erjimima : '' ,
status : user . status
const openDetailDialog = async ( user ) => {
if ( ! user ) return
try {
currentUser . value = { ... user }
editForm . value = {
nicheng : user . nicheng || '' ,
password : '' ,
erjimima : '' ,
status : user . status ? ? 1 ,
}
currentUserRoles . value = Array . isArray ( user . roles ) ? [ ... user . roles ] : [ ]
currentUserAssignments . value = Array . isArray ( user . assignments ) ? [ ... user . assignments ] : [ ]
detailDialogVisible . value = false
await nextTick ( )
detailDialogVisible . value = true
} catch ( e ) {
console . error ( 'openDetailDialog failed:' , e )
ElMessage . error ( '打开详情失败,请刷新页面重试' )
}
currentUserRoles . value = [ ... ( user . roles || [ ] ) ]
detailDialogVisible . value = true
}
// 保存用户修改(昵称、密码、二级密码、状态、角色已通过其他接口单独处理)
@@ -418,11 +583,81 @@ const saveUserInfo = async () => {
// 为用户添加角色(打开弹窗)
const openAddRoleForUser = ( ) => {
selectedRoleCodes . value = [ ]
roleBindClubId . value = currentUserAssignments . value [ 0 ] ? . club _id || clubOptions . value [ 0 ] ? . club _id || ''
addRoleForUserVisible . value = true
}
const openAddClubDialog = ( ) => {
clubAssignForm . value = {
club _id : availableClubsForAssign . value [ 0 ] ? . club _id || '' ,
role _code : 'CLUB_ADMIN' ,
is _primary : currentUserAssignments . value . length === 0 ,
}
addClubVisible . value = true
}
const confirmAddClubAssignment = async ( ) => {
if ( ! clubAssignForm . value . club _id ) {
ElMessage . warning ( '请选择俱乐部' )
return
}
assigningClub . value = true
try {
const res = await request . post ( '/houtai/xghtyhsj' , {
username ,
action : 'add_club_assignment' ,
yonghuid : currentUser . value . yonghuid ,
phone : currentUser . value . phone ,
club _id : clubAssignForm . value . club _id ,
role _code : clubAssignForm . value . role _code ,
is _primary : clubAssignForm . value . is _primary ,
} )
if ( res . code === 0 ) {
ElMessage . success ( '俱乐部任职已添加' )
addClubVisible . value = false
await fetchUserList ( )
const updated = userList . value . find ( ( u ) => u . yonghuid === currentUser . value . yonghuid )
if ( updated ) {
currentUserAssignments . value = [ ... ( updated . assignments || [ ] ) ]
}
} else {
ElMessage . error ( res . msg || '添加失败' )
}
} catch {
ElMessage . error ( '网络错误' )
} finally {
assigningClub . value = false
}
}
const removeClubAssignment = async ( row ) => {
try {
await ElMessageBox . confirm ( ` 移除 ${ row . club _label } 任职? ` , '提示' , { type : 'warning' } )
const res = await request . post ( '/houtai/xghtyhsj' , {
username ,
action : 'remove_club_assignment' ,
yonghuid : currentUser . value . yonghuid ,
phone : currentUser . value . phone ,
assignment _id : row . id ,
} )
if ( res . code === 0 ) {
ElMessage . success ( '已移除' )
currentUserAssignments . value = currentUserAssignments . value . filter ( ( a ) => a . id !== row . id )
fetchUserList ( )
} else {
ElMessage . error ( res . msg || '移除失败' )
}
} catch {
// cancel
}
}
// 确认添加角色
const confirmAddUserRoles = async ( ) => {
if ( ! roleBindClubId . value ) {
ElMessage . warning ( '请先选择俱乐部' )
return
}
if ( selectedRoleCodes . value . length === 0 ) {
ElMessage . warning ( '请至少选择一个角色' )
return
@@ -433,17 +668,17 @@ const confirmAddUserRoles = async () => {
action : 'add_user_roles' ,
yonghuid : currentUser . value . yonghuid ,
phone : currentUser . value . phone ,
role _codes : selectedRoleCodes . value
role _codes : selectedRoleCodes . value ,
target _club _id : roleBindClubId . value ,
} )
if ( res . code === 0 ) {
ElMessage . success ( '角色添加成功' )
// 刷新当前用户的角色列表
const updatedUser = userList . value . find ( u => u . phone === currentUser . value . phone )
if ( updatedUser ) {
currentUserRoles . value = [ ... ( updatedUser . roles || [ ] ) , ... roleList . value . filter ( r => selectedRoleCodes . value . includes ( r . role _code ) ) ]
}
addRoleForUserVisible . value = false
fetchUserList ( ) // 刷新列表
await fetchUserList( )
const updated = userList . value . find ( ( u ) => u . yonghuid === currentUser . value . yonghuid )
if ( updated ) {
currentUserRoles . value = [ ... ( updated . roles || [ ] ) ]
}
} else {
ElMessage . error ( res . msg || '添加失败' )
}
@@ -460,7 +695,8 @@ const removeUserRole = async (role) => {
action : 'remove_user_role' ,
yonghuid : currentUser . value . yonghuid ,
phone : currentUser . value . phone ,
role _code : role . role _code
role _code : role . role _code ,
target _club _id : role . club _id || '' ,
} )
if ( res . code === 0 ) {
ElMessage . success ( '角色移除成功' )
@@ -484,6 +720,7 @@ const openAddUserDialog = () => {
// 确认添加用户
const confirmAddUser = async ( ) => {
if ( addingUser . value ) return
if ( ! newUserForm . value . phone || ! newUserForm . value . password || ! newUserForm . value . erjimima || ! newUserForm . value . nicheng ) {
ElMessage . warning ( '请填写完整信息' )
return
@@ -492,6 +729,7 @@ const confirmAddUser = async () => {
ElMessage . warning ( '密码和二级密码至少6位' )
return
}
addingUser . value = true
try {
const res = await request . post ( '/houtai/tjyhjs' , {
username ,
@@ -510,6 +748,8 @@ const confirmAddUser = async () => {
}
} catch ( error ) {
ElMessage . error ( '网络错误' )
} finally {
addingUser . value = false
}
}
@@ -519,6 +759,7 @@ onMounted(async () => {
ElMessage . error ( '未获取到用户信息' )
return
}
await loadClubOptions ( )
const success = await fetchRoleList ( )
if ( success ) {
await fetchUserList ( )
@@ -672,6 +913,28 @@ onMounted(async () => {
padding : 20 px 0 ;
text - align : center ;
}
. muted - text {
color : rgba ( 255 , 255 , 255 , 0.45 ) ;
font - size : 13 px ;
}
. club - assign - list {
display : flex ;
flex - direction : column ;
gap : 10 px ;
}
. club - assign - item {
display : flex ;
align - items : center ;
gap : 12 px ;
padding : 8 px 12 px ;
background : rgba ( 255 , 255 , 255 , 0.05 ) ;
border - radius : 8 px ;
}
. assign - role - label {
flex : 1 ;
color : rgba ( 255 , 255 , 255 , 0.65 ) ;
font - size : 13 px ;
}
. role - select - list {
max - height : 400 px ;
overflow - y : auto ;