mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
feat: add user management CRUD and platform_user table
V24 migration creates platform_user table. Backend UserAdminController provides list/create/update/toggleStatus. Frontend UserManagementView enables admin to add/edit/disable users. Replaces hardcoded auth with database-backed user lifecycle. Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -461,6 +461,20 @@ export function createSkuMapping(contractLineId, body) { return axios.post(`/api
|
||||
export function updateSkuMapping(id, body) { return axios.put(`/api/v1/integration/sku-mappings/${id}`, body); }
|
||||
export function deleteSkuMapping(id) { return axios.delete(`/api/v1/integration/sku-mappings/${id}`); }
|
||||
|
||||
// —— M11-F14 用户管理 ————————————————————————————
|
||||
export function listUsers() {
|
||||
return axios.get('/api/v1/admin/users');
|
||||
}
|
||||
export function createUser(body) {
|
||||
return axios.post('/api/v1/admin/users', body);
|
||||
}
|
||||
export function updateUser(id, body) {
|
||||
return axios.put(`/api/v1/admin/users/${id}`, body);
|
||||
}
|
||||
export function patchUserStatus(id, body) {
|
||||
return axios.patch(`/api/v1/admin/users/${id}/status`, body);
|
||||
}
|
||||
|
||||
export function listStakeholders(projectId) {
|
||||
return axios.get(`/api/v1/projects/${projectId}/stakeholders`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { apiErrorMessage } from '../utils/apiErrorMessage'
|
||||
import { listUsers, createUser, updateUser, patchUserStatus } from '../api/platform'
|
||||
|
||||
const loading = ref(false)
|
||||
const users = ref([])
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const editingId = ref(null)
|
||||
const form = ref({ username: '', displayName: '', password: '', role: 'SALES' })
|
||||
|
||||
const roles = [
|
||||
{ value: 'SYS_ADMIN', label: '系统管理员' },
|
||||
{ value: 'SALES', label: '商务经理' },
|
||||
{ value: 'DELIVERY', label: '交付工程师' },
|
||||
{ value: 'LICENSE_OPS', label: '授权运营' },
|
||||
]
|
||||
|
||||
onMounted(loadData)
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const { data } = await listUsers()
|
||||
users.value = data
|
||||
} catch (e) {
|
||||
ElMessage.error(apiErrorMessage(e, '加载用户列表失败'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
editingId.value = null
|
||||
dialogTitle.value = '新建用户'
|
||||
form.value = { username: '', displayName: '', password: '', role: 'SALES' }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
editingId.value = row.id
|
||||
dialogTitle.value = '编辑用户'
|
||||
form.value = {
|
||||
username: row.username,
|
||||
displayName: row.displayName || '',
|
||||
password: '',
|
||||
role: row.role || 'SALES',
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
const body = { ...form.value }
|
||||
if (!editingId.value) {
|
||||
if (!body.password || body.password.length < 6) {
|
||||
ElMessage.error('密码至少6位')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await createUser(body)
|
||||
ElMessage.success('用户创建成功')
|
||||
dialogVisible.value = false
|
||||
await loadData()
|
||||
} catch (e) {
|
||||
ElMessage.error(apiErrorMessage(e, '创建失败'))
|
||||
}
|
||||
} else {
|
||||
if (body.password && body.password.length < 6) {
|
||||
ElMessage.error('密码至少6位')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await updateUser(editingId.value, body)
|
||||
ElMessage.success('用户更新成功')
|
||||
dialogVisible.value = false
|
||||
await loadData()
|
||||
} catch (e) {
|
||||
ElMessage.error(apiErrorMessage(e, '更新失败'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleToggleStatus(row) {
|
||||
const newStatus = row.status === 'ACTIVE' ? 'DISABLED' : 'ACTIVE'
|
||||
const label = newStatus === 'ACTIVE' ? '启用' : '禁用'
|
||||
try {
|
||||
await patchUserStatus(row.id, { status: newStatus })
|
||||
ElMessage.success(`用户已${label}`)
|
||||
await loadData()
|
||||
} catch (e) {
|
||||
ElMessage.error(apiErrorMessage(e, `${label}失败`))
|
||||
}
|
||||
}
|
||||
|
||||
function statusTagType(status) {
|
||||
return status === 'ACTIVE' ? 'success' : 'danger'
|
||||
}
|
||||
|
||||
function statusLabel(status) {
|
||||
return status === 'ACTIVE' ? '正常' : '已禁用'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h2>用户管理</h2>
|
||||
<el-card shadow="never" style="margin-top: 16px">
|
||||
<template #header>
|
||||
<el-button type="primary" @click="openCreate">新建用户</el-button>
|
||||
</template>
|
||||
<el-table v-loading="loading" :data="users" stripe style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="username" label="用户名" min-width="140" />
|
||||
<el-table-column prop="displayName" label="显示名" min-width="160" />
|
||||
<el-table-column label="角色" width="140">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small">{{ row.role }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusTagType(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createdAt" label="创建时间" width="180" />
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="openEdit(row)">编辑</el-button>
|
||||
<el-button
|
||||
:type="row.status === 'ACTIVE' ? 'warning' : 'success'"
|
||||
link
|
||||
@click="handleToggleStatus(row)"
|
||||
>
|
||||
{{ row.status === 'ACTIVE' ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="480px" @closed="dialogVisible = false">
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="用户名" required>
|
||||
<el-input v-model="form.username" :disabled="!!editingId" maxlength="64" placeholder="登录名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示名">
|
||||
<el-input v-model="form.displayName" maxlength="128" placeholder="选填,默认同用户名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色" required>
|
||||
<el-select v-model="form.role" style="width: 100%">
|
||||
<el-option v-for="r in roles" :key="r.value" :label="r.label" :value="r.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="editingId ? '新密码' : '密码'" :required="!editingId">
|
||||
<el-input v-model="form.password" type="password" show-password :placeholder="editingId ? '留空则不修改' : '至少6位'" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user