rbac-permission-interface-impl
This commit is contained in:
@@ -14,13 +14,20 @@ const emit = defineEmits<{
|
||||
success: []
|
||||
}>()
|
||||
|
||||
interface RoleOption {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
const formRef = ref()
|
||||
const submitting = ref(false)
|
||||
const roles = ref<RoleOption[]>([])
|
||||
const formState = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
email: '',
|
||||
status: 1 as number,
|
||||
role_id: undefined as number | undefined,
|
||||
})
|
||||
|
||||
const rules = computed<Record<string, Rule[]>>(() => {
|
||||
@@ -46,20 +53,33 @@ const rules = computed<Record<string, Rule[]>>(() => {
|
||||
|
||||
const title = computed(() => (props.mode === 'create' ? '新建用户' : '编辑用户'))
|
||||
|
||||
async function fetchRoles() {
|
||||
try {
|
||||
const data = await api.get<RoleOption[]>('/api/v1/roles')
|
||||
roles.value = data
|
||||
} catch {
|
||||
roles.value = []
|
||||
message.warning('获取角色列表失败,角色选择不可用')
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
(val) => {
|
||||
if (val) {
|
||||
fetchRoles()
|
||||
if (props.mode === 'edit' && props.userData) {
|
||||
formState.username = props.userData.username
|
||||
formState.email = props.userData.email
|
||||
formState.status = props.userData.status
|
||||
formState.role_id = props.userData.role_id
|
||||
formState.password = ''
|
||||
} else {
|
||||
formState.username = ''
|
||||
formState.password = ''
|
||||
formState.email = ''
|
||||
formState.status = 1
|
||||
formState.role_id = undefined
|
||||
}
|
||||
// 清除上一次的校验状态
|
||||
nextTick(() => formRef.value?.clearValidate())
|
||||
@@ -76,20 +96,36 @@ async function handleSubmit() {
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
let userId: number
|
||||
if (props.mode === 'create') {
|
||||
await api.post('/api/v1/users', {
|
||||
const created = await api.post<{ id: number }>('/api/v1/users', {
|
||||
username: formState.username,
|
||||
password: formState.password,
|
||||
email: formState.email,
|
||||
status: formState.status,
|
||||
})
|
||||
userId = created.id
|
||||
} else {
|
||||
await api.put(`/api/v1/users/${props.userData!.id}`, {
|
||||
username: formState.username,
|
||||
email: formState.email,
|
||||
status: formState.status,
|
||||
})
|
||||
userId = props.userData!.id
|
||||
}
|
||||
|
||||
// 角色分配(独立 try-catch,用户信息保存不受影响)
|
||||
const needsRoleUpdate = props.mode === 'create'
|
||||
? !!formState.role_id
|
||||
: formState.role_id && formState.role_id !== props.userData!.role_id
|
||||
if (needsRoleUpdate) {
|
||||
try {
|
||||
await api.put(`/api/v1/users/${userId}/role`, { role_id: formState.role_id })
|
||||
} catch {
|
||||
message.warning('用户已保存,但角色分配失败,请稍后在用户列表中重试')
|
||||
}
|
||||
}
|
||||
|
||||
message.success('操作成功')
|
||||
emit('update:open', false)
|
||||
emit('success')
|
||||
@@ -130,6 +166,13 @@ function handleCancel() {
|
||||
<a-select-option :value="0">禁用</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="角色" name="role_id">
|
||||
<a-select v-model:value="formState.role_id" placeholder="请选择角色" allow-clear>
|
||||
<a-select-option v-for="role in roles" :key="role.id" :value="role.id">
|
||||
{{ role.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
@@ -21,6 +21,7 @@ interface MenuItem {
|
||||
key: string
|
||||
icon: Component
|
||||
label: string
|
||||
adminOnly?: boolean
|
||||
children?: MenuItem[]
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ watch(() => route.path, initOpenKeys)
|
||||
// 导航菜单配置
|
||||
const menuItems: MenuItem[] = [
|
||||
{ key: '/', icon: DashboardOutlined, label: '首页' },
|
||||
{ key: '/users', icon: UserOutlined, label: '用户管理' },
|
||||
{ key: '/users', icon: UserOutlined, label: '用户管理', adminOnly: true },
|
||||
{ key: '/products', icon: ShoppingOutlined, label: '产品管理' },
|
||||
{
|
||||
key: 'orders-group',
|
||||
@@ -74,14 +75,19 @@ const menuItems: MenuItem[] = [
|
||||
{ key: '/refund-items', icon: UnorderedListOutlined, label: '退款子项' },
|
||||
],
|
||||
},
|
||||
{ key: '/mq-status', icon: MonitorOutlined, label: '队列监控' },
|
||||
{ key: '/mq-status', icon: MonitorOutlined, label: '队列监控', adminOnly: true },
|
||||
]
|
||||
|
||||
const filteredMenuItems = computed(() =>
|
||||
menuItems.filter((item) => !item.adminOnly || userStore.isAdmin),
|
||||
)
|
||||
|
||||
const username = computed(() => userStore.username || 'admin')
|
||||
|
||||
const handleMenuClick = ({ key }: { key: string }) => {
|
||||
if (key.startsWith('/')) {
|
||||
router.push(key)
|
||||
const handleMenuClick = ({ key }: { key: string | number }) => {
|
||||
const path = String(key)
|
||||
if (path.startsWith('/')) {
|
||||
router.push(path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +174,7 @@ const breadcrumbItems = computed(() => {
|
||||
theme="dark"
|
||||
@click="handleMenuClick"
|
||||
>
|
||||
<template v-for="item in menuItems" :key="item.key">
|
||||
<template v-for="item in filteredMenuItems" :key="item.key">
|
||||
<a-sub-menu v-if="item.children" :key="item.key">
|
||||
<template #icon><component :is="item.icon" /></template>
|
||||
<template #title>{{ item.label }}</template>
|
||||
|
||||
Reference in New Issue
Block a user