update roles

This commit is contained in:
2026-03-19 10:50:19 +08:00
parent e4fd8ca380
commit e9de137e66
9 changed files with 1032 additions and 5 deletions
+235
View File
@@ -0,0 +1,235 @@
<script setup lang="ts">
import { useRouteGroupStore } from '@/stores/route-group'
import type { RouteGroupRecord } from '@/stores/route-group'
import {
PlusOutlined,
EditOutlined,
DeleteOutlined,
} from '@ant-design/icons-vue'
const groupStore = useRouteGroupStore()
// 路由组表单
const modalOpen = ref(false)
const modalTitle = ref('新建路由组')
const editingGroupId = ref<number | null>(null)
const formState = reactive({
name: '',
label: '',
description: '',
sort_order: 0,
})
const saving = ref(false)
// method 颜色映射
const methodColorMap: Record<string, string> = {
GET: 'green',
POST: 'blue',
PUT: 'orange',
DELETE: 'red',
PATCH: 'cyan',
}
const routeColumns = [
{ title: '方法', key: 'method', width: 100 },
{ title: '路径', dataIndex: 'path', key: 'path' },
{ title: '描述', dataIndex: 'description', key: 'description', width: 200 },
{ title: '所属组', key: 'group', width: 180 },
]
// 组选项(含"无"选项)
const groupOptions = computed(() => [
{ value: null as number | null, label: '无(未分组)' },
...groupStore.groups.map((g) => ({ value: g.id, label: g.label || g.name })),
])
// 筛选路由
const filteredRoutes = computed(() => {
if (groupStore.selectedGroupId === null) return groupStore.routes
if (groupStore.selectedGroupId === 'ungrouped') {
return groupStore.routes.filter((r) => r.group_id === null)
}
return groupStore.routes.filter((r) => r.group_id === groupStore.selectedGroupId)
})
function selectGroup(id: number | null | 'ungrouped') {
groupStore.selectedGroupId = id
}
function openCreateModal() {
editingGroupId.value = null
modalTitle.value = '新建路由组'
formState.name = ''
formState.label = ''
formState.description = ''
formState.sort_order = 0
modalOpen.value = true
}
function openEditModal(group: RouteGroupRecord) {
editingGroupId.value = group.id
modalTitle.value = '编辑路由组'
formState.name = group.name
formState.label = group.label
formState.description = group.description
formState.sort_order = group.sort_order
modalOpen.value = true
}
async function handleSubmit() {
saving.value = true
try {
if (editingGroupId.value) {
await groupStore.updateGroup(editingGroupId.value, { ...formState })
message.success('路由组更新成功')
} else {
await groupStore.createGroup({ ...formState })
message.success('路由组创建成功')
}
modalOpen.value = false
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : '操作失败'
message.error(msg)
} finally {
saving.value = false
}
}
async function handleDelete(group: RouteGroupRecord) {
try {
await groupStore.deleteGroup(group.id)
if (groupStore.selectedGroupId === group.id) {
groupStore.selectedGroupId = null
}
message.success('路由组删除成功')
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : '删除失败'
message.error(msg)
}
}
async function handleGroupChange(routeId: number, groupId: number | null) {
try {
await groupStore.assignRouteToGroup(routeId, groupId)
await groupStore.fetchRoutes()
await groupStore.fetchGroups()
message.success('路由分配成功')
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : '分配失败'
message.error(msg)
}
}
onMounted(async () => {
await Promise.all([groupStore.fetchGroups(), groupStore.fetchRoutes()])
})
</script>
<template>
<a-row :gutter="16">
<!-- 左侧路由组列表 -->
<a-col :span="8">
<a-card title="路由组" :loading="groupStore.loading">
<template #extra>
<a-button type="primary" size="small" @click="openCreateModal">
<PlusOutlined /> 新建
</a-button>
</template>
<!-- 未分组入口 -->
<div
class="cursor-pointer px-3 py-2 rounded mb-1"
:class="groupStore.selectedGroupId === 'ungrouped' ? 'bg-blue-50' : 'hover:bg-gray-50'"
@click="selectGroup('ungrouped')"
>
<div class="font-medium">未分组</div>
<div class="text-xs text-gray-400">未归入任何路由组的路由</div>
</div>
<a-divider class="my-2" />
<!-- 路由组列表 -->
<div
v-for="group in groupStore.groups"
:key="group.id"
class="cursor-pointer px-3 py-2 rounded mb-1 flex items-center justify-between"
:class="groupStore.selectedGroupId === group.id ? 'bg-blue-50' : 'hover:bg-gray-50'"
@click="selectGroup(group.id)"
>
<div>
<div class="font-medium">{{ group.label || group.name }}</div>
<div class="text-xs text-gray-400">{{ group.route_count ?? 0 }} 条路由</div>
</div>
<div class="flex gap-1" @click.stop>
<a-button type="text" size="small" @click="openEditModal(group)">
<EditOutlined />
</a-button>
<a-popconfirm
title="删除后,关联的角色授权将受影响,确认删除?"
@confirm="handleDelete(group)"
>
<a-button type="text" size="small" danger>
<DeleteOutlined />
</a-button>
</a-popconfirm>
</div>
</div>
</a-card>
</a-col>
<!-- 右侧路由列表 -->
<a-col :span="16">
<a-card title="路由列表">
<a-table
:columns="routeColumns"
:data-source="filteredRoutes"
:loading="groupStore.routesLoading"
:pagination="false"
row-key="id"
size="small"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'method'">
<a-tag :color="methodColorMap[record.method] || 'default'">
{{ record.method }}
</a-tag>
</template>
<template v-if="column.key === 'group'">
<a-select
:value="record.group_id"
:options="groupOptions"
size="small"
style="width: 150px"
placeholder="选择路由组"
@change="(val: unknown) => handleGroupChange(record.id, val as number | null)"
/>
</template>
</template>
</a-table>
</a-card>
</a-col>
</a-row>
<!-- 路由组新建/编辑 Modal -->
<a-modal
v-model:open="modalOpen"
:title="modalTitle"
:confirm-loading="saving"
@ok="handleSubmit"
>
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }" class="mt-4">
<a-form-item label="组标识" required>
<a-input v-model:value="formState.name" placeholder="如 user-management" />
</a-form-item>
<a-form-item label="显示名称" required>
<a-input v-model:value="formState.label" placeholder="如 用户管理" />
</a-form-item>
<a-form-item label="描述">
<a-textarea v-model:value="formState.description" :rows="2" />
</a-form-item>
<a-form-item label="排序">
<a-input-number v-model:value="formState.sort_order" :min="0" />
</a-form-item>
</a-form>
</a-modal>
</template>