239 lines
8.4 KiB
Vue
239 lines
8.4 KiB
Vue
<script setup lang="ts">
|
|
import { NCheckbox, NInput } from 'naive-ui'
|
|
import { computed, reactive, ref, watch } from 'vue'
|
|
|
|
import {
|
|
createUser,
|
|
getIdentitySetting,
|
|
getUserDetail,
|
|
updateUser,
|
|
type UpdateUserBody,
|
|
} from '@/api/tenant'
|
|
import FormField from '@/components/FormField.vue'
|
|
import FormModal from '@/components/FormModal.vue'
|
|
import type { IamUser, IamUserDetail } from '@/types/api'
|
|
import { useToast } from '@/composables/useToast'
|
|
|
|
// user 非空即编辑模式:打开时拉取域档案预填充,提交只发送与原值不同的字段
|
|
const props = defineProps<{ show: boolean; cfgId: number; domainId?: string; user?: IamUser | null }>()
|
|
const emit = defineEmits<{ 'update:show': [boolean]; created: [] }>()
|
|
|
|
const message = useToast()
|
|
const submitting = ref(false)
|
|
// 域开启「用户需要提供主电子邮件地址」时邮箱必填
|
|
const emailRequired = ref(false)
|
|
// 编辑模式的域档案;加载失败保持 null,降级为「留空不修改、勾选仅添加」
|
|
const detail = ref<IamUserDetail | null>(null)
|
|
const detailLoading = ref(false)
|
|
|
|
const blank = {
|
|
name: '',
|
|
givenName: '',
|
|
familyName: '',
|
|
email: '',
|
|
description: '',
|
|
grantDomainAdmin: false,
|
|
addToAdminGroup: false,
|
|
}
|
|
const form = reactive({ ...blank })
|
|
const editing = computed(() => !!props.user)
|
|
// 经典 IAM 用户不在身份域中,姓名与管理员状态均不可操作
|
|
const classicUser = computed(() => editing.value && !!detail.value && !detail.value.inDomain)
|
|
|
|
watch(
|
|
() => props.show,
|
|
(v) => {
|
|
if (!v) return
|
|
Object.assign(form, blank)
|
|
detail.value = null
|
|
if (props.user) {
|
|
form.name = props.user.name
|
|
form.email = props.user.email
|
|
form.description = props.user.description
|
|
void loadDetail(props.user.id)
|
|
}
|
|
void loadIdentitySetting()
|
|
},
|
|
)
|
|
|
|
async function loadIdentitySetting() {
|
|
try {
|
|
emailRequired.value = (await getIdentitySetting(props.cfgId, props.domainId)).primaryEmailRequired
|
|
} catch {
|
|
emailRequired.value = false
|
|
}
|
|
}
|
|
|
|
// 拉取域档案并回填表单;返回时弹窗已切到别的用户则丢弃结果
|
|
async function loadDetail(userId: string) {
|
|
detailLoading.value = true
|
|
try {
|
|
const d = await getUserDetail(props.cfgId, userId, props.domainId)
|
|
if (props.user?.id !== userId) return
|
|
detail.value = d
|
|
if (!d.inDomain) return
|
|
form.givenName = d.givenName
|
|
form.familyName = d.familyName
|
|
form.grantDomainAdmin = d.isDomainAdmin
|
|
form.addToAdminGroup = d.inAdminGroup
|
|
} catch {
|
|
if (props.user?.id === userId)
|
|
message.warning('用户详情加载失败:姓名留空表示不修改,管理员选项仅执行添加')
|
|
} finally {
|
|
if (props.user?.id === userId) detailLoading.value = false
|
|
}
|
|
}
|
|
|
|
const nameRequired = computed(() => !editing.value || !!detail.value?.inDomain)
|
|
|
|
const nameHint = computed(() => {
|
|
if (!editing.value || detailLoading.value) return undefined
|
|
if (classicUser.value) return '经典 IAM 用户不支持修改姓名'
|
|
if (!detail.value) return '详情加载失败:留空表示不修改,仅身份域用户可改姓名'
|
|
return undefined
|
|
})
|
|
|
|
const adminHint = computed(() => {
|
|
if (!editing.value)
|
|
return form.grantDomainAdmin && form.addToAdminGroup
|
|
? '同时勾选时先授予身份域管理员,再加入管理员组'
|
|
: undefined
|
|
if (detailLoading.value) return '正在加载当前管理员状态…'
|
|
if (classicUser.value) return '经典 IAM 用户不在身份域中,无法调整管理员角色与组'
|
|
if (!detail.value) return '详情加载失败:仅执行添加操作,不会撤销已有的管理员角色或组成员'
|
|
return '已按当前状态勾选;取消勾选保存后将撤销对应的管理员角色 / 组成员'
|
|
})
|
|
|
|
const canSubmit = computed(() => {
|
|
const emailOk = !emailRequired.value || !!form.email.trim()
|
|
if (!editing.value)
|
|
return !!form.name.trim() && !!form.givenName.trim() && !!form.familyName.trim() && emailOk
|
|
if (detailLoading.value) return false
|
|
if (detail.value?.inDomain && (!form.givenName.trim() || !form.familyName.trim())) return false
|
|
return emailOk
|
|
})
|
|
|
|
// 编辑只提交与原值不同的字段;域档案在手时管理员勾选与当前状态比对,取消勾选即撤销
|
|
function buildUpdateBody(u: IamUser): UpdateUserBody {
|
|
const body: UpdateUserBody = {}
|
|
if (form.email.trim() !== u.email) body.email = form.email.trim()
|
|
if (form.description.trim() !== u.description) body.description = form.description.trim()
|
|
if (classicUser.value) return body
|
|
const d = detail.value
|
|
if (d?.inDomain) {
|
|
if (form.givenName.trim() !== d.givenName) body.givenName = form.givenName.trim()
|
|
if (form.familyName.trim() !== d.familyName) body.familyName = form.familyName.trim()
|
|
if (form.grantDomainAdmin !== d.isDomainAdmin) body.grantDomainAdmin = form.grantDomainAdmin
|
|
if (form.addToAdminGroup !== d.inAdminGroup) body.addToAdminGroup = form.addToAdminGroup
|
|
return body
|
|
}
|
|
if (form.givenName.trim()) body.givenName = form.givenName.trim()
|
|
if (form.familyName.trim()) body.familyName = form.familyName.trim()
|
|
if (form.grantDomainAdmin) body.grantDomainAdmin = true
|
|
if (form.addToAdminGroup) body.addToAdminGroup = true
|
|
return body
|
|
}
|
|
|
|
async function submitEdit(u: IamUser) {
|
|
const body = buildUpdateBody(u)
|
|
if (!Object.keys(body).length) {
|
|
emit('update:show', false)
|
|
return
|
|
}
|
|
await updateUser(props.cfgId, u.id, body, props.domainId)
|
|
message.success(`用户「${u.name}」已更新`)
|
|
}
|
|
|
|
async function submitCreate() {
|
|
const created = await createUser(
|
|
props.cfgId,
|
|
{
|
|
name: form.name.trim(),
|
|
givenName: form.givenName.trim(),
|
|
familyName: form.familyName.trim(),
|
|
email: form.email.trim() || undefined,
|
|
description: form.description.trim() || undefined,
|
|
grantDomainAdmin: form.grantDomainAdmin,
|
|
addToAdminGroup: form.addToAdminGroup,
|
|
},
|
|
props.domainId,
|
|
)
|
|
message.success(`用户「${created.name}」已创建,可再执行「重置密码」下发初始密码`)
|
|
}
|
|
|
|
async function submit() {
|
|
if (!canSubmit.value) return
|
|
submitting.value = true
|
|
try {
|
|
if (props.user) await submitEdit(props.user)
|
|
else await submitCreate()
|
|
emit('update:show', false)
|
|
emit('created')
|
|
} catch (e) {
|
|
message.error(e instanceof Error ? e.message : editing.value ? '更新失败' : '创建失败')
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<FormModal
|
|
:show="show"
|
|
:title="editing ? `修改用户 · ${user?.name ?? ''}` : '添加用户'"
|
|
:submitting="submitting"
|
|
:submit-disabled="!canSubmit"
|
|
:submit-text="editing ? '保存' : '创建'"
|
|
@update:show="emit('update:show', $event)"
|
|
@submit="submit"
|
|
>
|
|
<FormField v-if="!editing" label="用户名" required>
|
|
<NInput v-model:value="form.name" placeholder="new-user" @keyup.enter="submit" />
|
|
</FormField>
|
|
<div class="grid grid-cols-2 gap-3 max-md:grid-cols-1">
|
|
<FormField label="名字" :required="nameRequired" :hint="nameHint">
|
|
<NInput
|
|
v-model:value="form.givenName"
|
|
placeholder="First name"
|
|
:disabled="classicUser"
|
|
:loading="detailLoading"
|
|
/>
|
|
</FormField>
|
|
<FormField label="姓氏" :required="nameRequired">
|
|
<NInput
|
|
v-model:value="form.familyName"
|
|
placeholder="Last name"
|
|
:disabled="classicUser"
|
|
:loading="detailLoading"
|
|
/>
|
|
</FormField>
|
|
</div>
|
|
<FormField
|
|
label="邮箱"
|
|
:required="emailRequired"
|
|
:hint="
|
|
emailRequired
|
|
? '域已开启「用户需要提供主电子邮件地址」,必须填写'
|
|
: '用于接收欢迎邮件与找回密码'
|
|
"
|
|
>
|
|
<NInput v-model:value="form.email" placeholder="user@example.com" />
|
|
</FormField>
|
|
<FormField label="备注" :hint="editing ? undefined : '留空默认取「名字 姓氏」'">
|
|
<NInput v-model:value="form.description" placeholder="可选" />
|
|
</FormField>
|
|
<div class="mb-2 flex flex-col gap-1.5">
|
|
<NCheckbox
|
|
v-model:checked="form.grantDomainAdmin"
|
|
:disabled="classicUser || detailLoading"
|
|
>
|
|
{{ editing ? '身份域管理员' : '添加到身份域管理员' }}
|
|
</NCheckbox>
|
|
<NCheckbox v-model:checked="form.addToAdminGroup" :disabled="classicUser || detailLoading">
|
|
{{ editing ? '租户管理员组成员' : '添加到租户管理员组' }}
|
|
</NCheckbox>
|
|
<div v-if="adminHint" class="text-xs text-ink-3">{{ adminHint }}</div>
|
|
</div>
|
|
</FormModal>
|
|
</template>
|