2026-07-21 09:23:43 +08:00
|
|
|
|
<script setup lang="ts">
|
2026-08-21 09:49:48 +08:00
|
|
|
|
import { onMounted, reactive, ref } from 'vue'
|
2026-07-21 09:23:43 +08:00
|
|
|
|
import { useRouter } from 'vue-router'
|
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
|
import { createUser } from '@/api/modules/system'
|
2026-08-21 09:49:48 +08:00
|
|
|
|
import { getTenants, type Tenant } from '@/api/modules/tenant'
|
|
|
|
|
|
import type { CreateUserPayload, PermissionCode } from '@/types'
|
2026-07-21 09:23:43 +08:00
|
|
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
const submitting = ref(false)
|
2026-08-21 09:49:48 +08:00
|
|
|
|
const tenants = ref<Tenant[]>([])
|
|
|
|
|
|
|
|
|
|
|
|
const permissionOptions: Array<{ value: PermissionCode; label: string }> = [
|
|
|
|
|
|
{ value: 'dashboard', label: '服务看板' },
|
|
|
|
|
|
{ value: 'fine-tune', label: '模型训练' },
|
|
|
|
|
|
{ value: 'model-eval', label: '模型评测' },
|
|
|
|
|
|
{ value: 'model-inference', label: '模型推理' },
|
|
|
|
|
|
{ value: 'model-manage', label: '模型管理' },
|
|
|
|
|
|
{ value: 'dataset', label: '数据集管理' },
|
|
|
|
|
|
{ value: 'data-process', label: '数据处理' },
|
|
|
|
|
|
{ value: 'data-convert', label: '数据类型转换' },
|
|
|
|
|
|
{ value: 'compute', label: '算力节点' },
|
|
|
|
|
|
{ value: 'hardware', label: '平台性能' },
|
|
|
|
|
|
{ value: 'logs', label: '运行日志' },
|
|
|
|
|
|
{ value: 'user-settings', label: '组织与权限' },
|
|
|
|
|
|
]
|
2026-07-21 09:23:43 +08:00
|
|
|
|
|
|
|
|
|
|
const form = reactive<CreateUserPayload>({
|
|
|
|
|
|
username: '',
|
|
|
|
|
|
display_name: '',
|
2026-08-21 09:49:48 +08:00
|
|
|
|
password: '123456',
|
2026-08-19 11:33:45 +08:00
|
|
|
|
role: 'operator',
|
2026-07-21 09:23:43 +08:00
|
|
|
|
status: 'active',
|
2026-08-21 09:49:48 +08:00
|
|
|
|
permissions: permissionOptions.filter((item) => !['compute', 'user-settings'].includes(item.value)).map((item) => item.value),
|
|
|
|
|
|
tenant_id: 'admin',
|
|
|
|
|
|
tenant_role: 'member',
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
tenants.value = await getTenants().catch(() => [])
|
2026-07-21 09:23:43 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2026-08-21 09:49:48 +08:00
|
|
|
|
function handleRoleChange() {
|
|
|
|
|
|
form.permissions = form.role === 'admin'
|
|
|
|
|
|
? permissionOptions.map((item) => item.value)
|
|
|
|
|
|
: permissionOptions.filter((item) => !['compute', 'user-settings'].includes(item.value)).map((item) => item.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-21 09:23:43 +08:00
|
|
|
|
async function submit() {
|
2026-08-21 09:49:48 +08:00
|
|
|
|
if (!form.username.trim() || !form.display_name.trim()) {
|
|
|
|
|
|
ElMessage.warning('请填写账号和显示名称')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!form.password.trim()) {
|
|
|
|
|
|
ElMessage.warning('请输入初始密码')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-07-21 09:23:43 +08:00
|
|
|
|
submitting.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
await createUser(form)
|
|
|
|
|
|
ElMessage.success('用户已创建')
|
|
|
|
|
|
router.push('/user-settings')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
submitting.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<section class="user-create">
|
|
|
|
|
|
<h1>创建用户</h1>
|
|
|
|
|
|
<el-form :model="form" label-width="110px" class="user-form">
|
|
|
|
|
|
<el-form-item label="账号">
|
2026-08-17 16:04:04 +08:00
|
|
|
|
<el-input v-model="form.username" placeholder="登录用户名" />
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="显示名称">
|
2026-08-17 16:04:04 +08:00
|
|
|
|
<el-input v-model="form.display_name" placeholder="如:张三" />
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="初始密码">
|
|
|
|
|
|
<el-input v-model="form.password" type="password" show-password />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="角色">
|
2026-08-21 09:49:48 +08:00
|
|
|
|
<el-select v-model="form.role" style="width: 100%" @change="handleRoleChange">
|
2026-07-21 09:23:43 +08:00
|
|
|
|
<el-option label="管理员" value="admin" />
|
2026-08-21 09:49:48 +08:00
|
|
|
|
<el-option label="操作员" value="operator" />
|
|
|
|
|
|
<el-option label="观察员" value="viewer" />
|
|
|
|
|
|
<el-option label="普通用户" value="user" />
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="所属租户">
|
|
|
|
|
|
<el-select v-model="form.tenant_id" filterable style="width: 100%" placeholder="选择用户所属租户">
|
|
|
|
|
|
<el-option v-for="tenant in tenants" :key="tenant.id" :label="tenant.name + '(' + tenant.id + ')'" :value="tenant.id" />
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="租户角色">
|
|
|
|
|
|
<el-select v-model="form.tenant_role" style="width: 100%">
|
|
|
|
|
|
<el-option label="成员" value="member" />
|
|
|
|
|
|
<el-option label="管理员" value="admin" />
|
|
|
|
|
|
<el-option label="只读" value="viewer" />
|
|
|
|
|
|
<el-option v-if="form.role === 'admin'" label="所有者" value="owner" />
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="状态">
|
|
|
|
|
|
<el-radio-group v-model="form.status">
|
|
|
|
|
|
<el-radio value="active">启用</el-radio>
|
|
|
|
|
|
<el-radio value="disabled">禁用</el-radio>
|
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
|
</el-form-item>
|
2026-08-21 09:49:48 +08:00
|
|
|
|
<el-form-item label="页面权限">
|
|
|
|
|
|
<el-checkbox-group v-model="form.permissions" :disabled="form.role === 'admin'">
|
|
|
|
|
|
<el-checkbox v-for="item in permissionOptions" :key="item.value" :value="item.value">{{ item.label }}</el-checkbox>
|
|
|
|
|
|
</el-checkbox-group>
|
|
|
|
|
|
</el-form-item>
|
2026-08-17 16:04:04 +08:00
|
|
|
|
<el-form-item label="权限说明">
|
|
|
|
|
|
<el-alert type="info" :closable="false" show-icon>
|
|
|
|
|
|
<template #title>
|
|
|
|
|
|
<span v-if="form.role === 'admin'">管理员:拥有全部权限,包括用户管理、平台治理、算力节点</span>
|
2026-08-21 09:49:48 +08:00
|
|
|
|
<span v-else>非管理员:页面权限控制功能入口;数据集、训练模型等资源仍由创建者、ACL 和审批结果决定。算力节点与组织权限需要管理员单独授予。</span>
|
2026-08-17 16:04:04 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-alert>
|
2026-07-21 09:23:43 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button @click="router.back()">返回</el-button>
|
|
|
|
|
|
<el-button type="primary" :loading="submitting" @click="submit">保存</el-button>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.user-create {
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.user-form {
|
2026-08-17 16:04:04 +08:00
|
|
|
|
max-width: 640px;
|
2026-07-21 09:23:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|