diff --git a/backend/controllers/account_group.go b/backend/controllers/account_group.go index e80b099..a5400d0 100644 --- a/backend/controllers/account_group.go +++ b/backend/controllers/account_group.go @@ -122,7 +122,10 @@ func SetAccountGroupMembership(ctx *context.Context, w http.ResponseWriter, req return } InvalidateSecurityRuntimeUser(user.ID, user.Account) - response.NewSuccessResponse(map[string]any{"user_id": user.ID}).FPrint(w) + response.NewSuccessResponse(map[string]any{ + "user_id": user.ID, + "membership_version": user.SecurityVersion + 1, + }).FPrint(w) } func writeAccountGroupError(w http.ResponseWriter, err error) { diff --git a/frontend/src/views/components/UserManagement/AccountGroups.js b/frontend/src/views/components/UserManagement/AccountGroups.js index c8c7983..57b6ec5 100644 --- a/frontend/src/views/components/UserManagement/AccountGroups.js +++ b/frontend/src/views/components/UserManagement/AccountGroups.js @@ -3,7 +3,7 @@ import { ElMessage, ElMessageBox } from 'element-plus' import { http } from '@/utils/axios' import { isRecentAuthenticationCancelled, requestWithRecentAuthentication } from '@/utils/recentAuthentication' -export function useAccountGroups(globalStatus) { +export function useAccountGroups(globalStatus, onMembershipSaved = () => {}) { const accountGroups = ref([]) const groupsLoading = ref(false) const groupsError = ref('') @@ -125,6 +125,13 @@ export function useAccountGroups(globalStatus) { group_ids: [...membershipSelection.value] })) if (response?.errorNo !== 0) throw new Error(response?.errorMsg || '保存成员分组失败') + const updatedUser = { + ...membershipUser.value, + GroupIDs: [...membershipSelection.value], + MembershipVersion: response.data.membership_version + } + membershipUser.value = updatedUser + onMembershipSaved(updatedUser) membershipVisible.value = false ElMessage.success('成员分组已更新,目标账号需重新登录') await refreshGroups() diff --git a/frontend/src/views/components/UserManagement/UserManagement.css b/frontend/src/views/components/UserManagement/UserManagement.css index f007855..895a2b3 100644 --- a/frontend/src/views/components/UserManagement/UserManagement.css +++ b/frontend/src/views/components/UserManagement/UserManagement.css @@ -217,3 +217,32 @@ .membership-select { width: 100%; } + +.membership-summary { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 12px; + width: 100%; +} + +.membership-tags { + display: flex; + flex-wrap: wrap; + gap: 8px; + min-width: 0; +} + +.membership-tags :deep(.el-tag) { + height: auto; + white-space: normal; + overflow-wrap: anywhere; +} + +.membership-hint { + margin: 8px 0 0; + color: var(--pm-text-secondary); + font-size: 12px; + line-height: 1.6; +} diff --git a/frontend/src/views/components/UserManagement/UserManagement.js b/frontend/src/views/components/UserManagement/UserManagement.js index 5a78d34..cb22792 100644 --- a/frontend/src/views/components/UserManagement/UserManagement.js +++ b/frontend/src/views/components/UserManagement/UserManagement.js @@ -48,7 +48,9 @@ export function canManageRecoveryCode(user) { export function usePage() { const globalStatus = useGlobalStatusStore() - const groups = useAccountGroups(globalStatus) + const groups = useAccountGroups(globalStatus, user => { + if (selectedUser.value?.ID === user.ID) selectedUser.value = user + }) const userInfoDialog = ref(false) @@ -56,6 +58,21 @@ export function usePage() { const selectedUser = ref(null) + const selectedMembershipGroups = computed(() => { + const user = selectedUser.value + if (!user) return [] + return groups.accountGroups.value.filter(group => + groups.canManageGroups.value + ? (user.GroupIDs || [0]).includes(group.id) + : group.rows.some(member => member.ID === user.ID) + ) + }) + + const openMembershipDialog = () => { + if (submitting.value || !selectedUser.value) return + groups.editMembership(selectedUser.value) + } + const recoveryCodeDialogVisible = ref(false) const externalEmailDialogVisible = ref(false) @@ -219,6 +236,8 @@ export function usePage() { roleChanged, submitting, selectedUser, + selectedMembershipGroups, + openMembershipDialog, editingCurrentUser, recoveryCodeWebManaged, recoveryCodeDialogVisible, diff --git a/frontend/src/views/components/UserManagement/UserManagement.vue b/frontend/src/views/components/UserManagement/UserManagement.vue index c5e920b..27f646e 100644 --- a/frontend/src/views/components/UserManagement/UserManagement.vue +++ b/frontend/src/views/components/UserManagement/UserManagement.vue @@ -119,7 +119,7 @@ - +

账号:{{ membershipUser?.Account }}

@@ -188,6 +188,33 @@ + +
+
+ + {{ group.name }} + +
+ + 分组管理 + +
+

+ 分组单独保存,不会提交本窗口其他修改;选择数量按账号已保存的权限级别校验。 +

+

+ 仅显示你有权查看的分组,成员归属由超级管理员维护。 +

+
+
@@ -301,6 +328,8 @@ const { roleChanged, submitting, selectedUser, + selectedMembershipGroups, + openMembershipDialog, editingCurrentUser, recoveryCodeWebManaged, recoveryCodeDialogVisible,