diff --git a/backend/controllers/user.go b/backend/controllers/user.go
index 67c4bf98..b2e39ec3 100644
--- a/backend/controllers/user.go
+++ b/backend/controllers/user.go
@@ -44,6 +44,7 @@ func decodeUserRequest(w http.ResponseWriter, req *http.Request, dst any) error
}
type userCreateRequest struct {
+ GroupIDs *[]int `json:"group_ids"`
Id int `json:"id"`
Account string `json:"account"`
Username string `json:"username"`
@@ -93,6 +94,15 @@ func CreateUser(ctx *context.Context, w http.ResponseWriter, req *http.Request)
}
user := models.User{Name: username, Account: account, Disabled: reqData.Disabled, SessionVersion: 1, SecurityVersion: 1}
user.ApplyRole(targetRole)
+ groupIDs := []int{0}
+ if reqData.GroupIDs != nil {
+ groupIDs = *reqData.GroupIDs
+ }
+ if len(groupIDs) > 0 && (len(groupIDs) != 1 || groupIDs[0] != 0) {
+ if !requireAccountGroupManagement(ctx, w) {
+ return
+ }
+ }
if err := auth.SetPassword(&user, reqData.Password); err != nil {
if writePasswordBusy(w, err) {
return
@@ -105,12 +115,46 @@ func CreateUser(ctx *context.Context, w http.ResponseWriter, req *http.Request)
response.NewErrorResponse(response.ServerError, "密码设置失败", "").FPrint(w)
return
}
- if _, err := db.Instance.Insert(&user); err != nil {
+ if db.Instance == nil {
+ writeAccountGroupError(w, accountgroup.ErrDenied)
+ return
+ }
+ tx := db.Instance.NewSession()
+ defer tx.Close()
+ if err := tx.Begin(); err != nil {
+ writeAccountGroupError(w, err)
+ return
+ }
+ defer tx.Rollback()
+ actor := models.User{ID: ctx.UserID, SessionVersion: ctx.SessionVersion, SecurityVersion: ctx.SecurityVersion}
+ actor.ApplyRole(ctx.EffectiveRole())
+ if err := accountgroup.CheckWrite(tx, accountWriteGuard(ctx, actor)); err != nil {
+ writeAccountGroupError(w, err)
+ return
+ }
+ groupIDs, err = accountgroup.ValidateMembership(tx, user.EffectiveRole(), groupIDs)
+ if err != nil {
+ writeAccountGroupError(w, err)
+ return
+ }
+ if _, err := tx.Insert(&user); err != nil {
log.WithContext(ctx).WithError(err).Error("create user failed")
response.NewErrorResponse(response.ServerError, "创建用户失败", "").FPrint(w)
return
}
- response.NewSuccessResponse(user).FPrint(w)
+ if err := accountgroup.ReplaceMembership(tx, user.ID, groupIDs); err != nil {
+ writeAccountGroupError(w, err)
+ return
+ }
+ if err := tx.Commit(); err != nil {
+ writeAccountGroupError(w, err)
+ return
+ }
+ response.NewSuccessResponse(struct {
+ models.User
+ GroupIDs []int
+ MembershipVersion int
+ }{user, groupIDs, user.SecurityVersion}).FPrint(w)
}
type userListRequest struct {
diff --git a/frontend/src/views/components/SystemNotification/SystemNotification.js b/frontend/src/views/components/SystemNotification/SystemNotification.js
index 937ba1ec..34b25c0f 100644
--- a/frontend/src/views/components/SystemNotification/SystemNotification.js
+++ b/frontend/src/views/components/SystemNotification/SystemNotification.js
@@ -4,7 +4,7 @@ import { http } from '@/utils/axios'
import { useGlobalStatusStore } from '@/stores/useGlobalStatusStore'
import { isRecentAuthenticationCancelled, requestWithRecentAuthentication } from '@/utils/recentAuthentication'
-export function usePage() {
+export function usePage(props = {}) {
const globalStatus = useGlobalStatusStore()
const manager = computed(() => globalStatus.hasCapability('notification.manage'))
const loading = ref(false)
@@ -14,7 +14,7 @@ export function usePage() {
const accounts = ref([])
const page = ref(1)
const total = ref(0)
- const selectedID = ref(Number(globalStatus.userInfos?.id || 0))
+ const selectedID = ref(Number(props.userId || globalStatus.userInfos?.id || 0))
const form = reactive({ enabled: false, minute_limit: 10, day_limit: 100 })
const secret = ref('')
const secretAccount = ref('')
@@ -70,7 +70,7 @@ export function usePage() {
}
}
const loadAccounts = async () => {
- if (!manager.value) return
+ if (!manager.value || props.userId) return
const sequence = ++accountsSequence
try {
const data = dataOf(await http.get('/api/v1/system/notification/accounts', { params: { page: page.value } }))
diff --git a/frontend/src/views/components/SystemNotification/SystemNotification.vue b/frontend/src/views/components/SystemNotification/SystemNotification.vue
index ac23aa99..f3feaf39 100644
--- a/frontend/src/views/components/SystemNotification/SystemNotification.vue
+++ b/frontend/src/views/components/SystemNotification/SystemNotification.vue
@@ -16,7 +16,7 @@
:closable="false"
/>
-
+
账号授权管理
@@ -111,11 +111,13 @@
diff --git a/frontend/src/views/components/UserManagement/UserManagement.css b/frontend/src/views/components/UserManagement/UserManagement.css
index ea99906c..904beb41 100644
--- a/frontend/src/views/components/UserManagement/UserManagement.css
+++ b/frontend/src/views/components/UserManagement/UserManagement.css
@@ -305,3 +305,23 @@
flex: 0 0 auto;
margin: 0;
}
+
+.account-form {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ column-gap: 18px;
+}
+
+.account-form-wide {
+ grid-column: 1 / -1;
+}
+
+.account-form > :deep(.el-form-item) {
+ min-width: 0;
+}
+
+@media (max-width: 560px) {
+ .account-form {
+ grid-template-columns: minmax(0, 1fr);
+ }
+}
diff --git a/frontend/src/views/components/UserManagement/UserManagement.js b/frontend/src/views/components/UserManagement/UserManagement.js
index 1aed045f..2cc1ff16 100644
--- a/frontend/src/views/components/UserManagement/UserManagement.js
+++ b/frontend/src/views/components/UserManagement/UserManagement.js
@@ -67,7 +67,7 @@ export function usePage() {
const selectedMembershipGroups = computed(() => {
const user = selectedUser.value
- if (!user) return []
+ if (!user && editModel.value !== 'create') return []
return groups.accountGroups.value.filter(group =>
groups.canManageGroups.value
? editingGroupIDs.value.includes(group.id)
@@ -76,8 +76,8 @@ export function usePage() {
})
const openMembershipDialog = () => {
- if (submitting.value || !selectedUser.value) return
- groups.editMembership(selectedUser.value, {
+ if (submitting.value || (!selectedUser.value && editModel.value !== 'create')) return
+ groups.editMembership(selectedUser.value || { Role: editUserInfo.role }, {
role: editUserInfo.role,
groupIDs: editingGroupIDs.value,
onApply: ids => { editingGroupIDs.value = ids }
@@ -88,6 +88,13 @@ export function usePage() {
const externalEmailDialogVisible = ref(false)
+ const notificationDialogVisible = ref(false)
+ const notificationPanel = ref(null)
+ const canManageNotification = computed(() => globalStatus.hasCapability('notification.manage'))
+ const closeNotificationDialog = async done => {
+ if (await notificationPanel.value?.beforeLeave()) done()
+ }
+
const securityDialogOpening = ref('')
const submitting = ref(false)
@@ -148,6 +155,8 @@ export function usePage() {
const createUser = function () {
if (!canCreateUser.value) return
selectedUser.value = null
+ editingGroupIDs.value = [0]
+ editingMembershipVersion.value = 0
editUserInfo.account = ""
editUserInfo.name = ""
editUserInfo.disabled = false
@@ -206,12 +215,12 @@ export function usePage() {
}
if (creating || roleChanged.value) newData.role = editUserInfo.role
if (creating || editUserInfo.password !== '') newData.password = editUserInfo.password
- if (!creating && groups.canManageGroups.value && (membershipChanged.value || roleChanged.value)) {
+ if (groups.canManageGroups.value && (creating || membershipChanged.value || roleChanged.value)) {
if (editUserInfo.role !== 'admin' && editingGroupIDs.value.length !== 1) {
throw new Error('普通用户只能属于一个分组,请在分组管理中选择一个分组')
}
newData.group_ids = [...editingGroupIDs.value]
- newData.membership_version = editingMembershipVersion.value
+ if (!creating) newData.membership_version = editingMembershipVersion.value
}
const res = await requestWithRecentAuthentication(() => creating
? http.post('/api/v1/user/create', newData)
@@ -221,8 +230,12 @@ export function usePage() {
}
ElNotification({ title: lang.succ, message: '用户信息已保存', type: 'success' })
editUserInfo.password = ''
- reflushList()
- userInfoDialog.value = false
+ if (creating) {
+ handleEdit(0, res.data)
+ } else {
+ userInfoDialog.value = false
+ }
+ await reflushList()
} catch (error) {
if (!isRecentAuthenticationCancelled(error)) {
ElNotification({
@@ -262,6 +275,10 @@ export function usePage() {
recoveryCodeWebManaged,
recoveryCodeDialogVisible,
externalEmailDialogVisible,
+ notificationDialogVisible,
+ notificationPanel,
+ canManageNotification,
+ closeNotificationDialog,
securityDialogOpening,
openRecoveryCodeDialog,
openExternalEmailDialog,
diff --git a/frontend/src/views/components/UserManagement/UserManagement.vue b/frontend/src/views/components/UserManagement/UserManagement.vue
index 3ddd3cd4..4452fc62 100644
--- a/frontend/src/views/components/UserManagement/UserManagement.vue
+++ b/frontend/src/views/components/UserManagement/UserManagement.vue
@@ -162,14 +162,14 @@
-
+
@@ -211,7 +211,7 @@
-
+