200 lines
5.5 KiB
Vue
200 lines
5.5 KiB
Vue
<template>
|
|
<div class="page-container">
|
|
<!-- 操作栏 -->
|
|
<a-card class="table-card" :bordered="false">
|
|
<template #title>
|
|
<a-button type="primary" @click="handleAdd">
|
|
<PlusOutlined /> 新增角色
|
|
</a-button>
|
|
</template>
|
|
|
|
<!-- 表格 -->
|
|
<a-table
|
|
:columns="columns"
|
|
:data-source="tableData"
|
|
:loading="loading"
|
|
:pagination="false"
|
|
row-key="id"
|
|
>
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.key === 'status'">
|
|
<a-tag :color="record.status === 1 ? 'green' : 'red'">
|
|
{{ record.status === 1 ? '正常' : '禁用' }}
|
|
</a-tag>
|
|
</template>
|
|
<template v-else-if="column.key === 'action'">
|
|
<a-space>
|
|
<a-button type="link" size="small" @click="handleEdit(record as RoleRecord)">编辑</a-button>
|
|
<a-button type="link" size="small" @click="handleAssignMenus(record as RoleRecord)">分配权限</a-button>
|
|
<a-popconfirm
|
|
title="确定删除该角色吗?"
|
|
@confirm="handleDelete(record.id)"
|
|
:disabled="['super_admin', 'admin'].includes(record.code)"
|
|
>
|
|
<a-button
|
|
type="link"
|
|
size="small"
|
|
danger
|
|
:disabled="['super_admin', 'admin'].includes(record.code)"
|
|
>
|
|
删除
|
|
</a-button>
|
|
</a-popconfirm>
|
|
</a-space>
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-card>
|
|
|
|
<!-- 新增/编辑弹窗 -->
|
|
<RoleFormModal
|
|
v-model:visible="modalVisible"
|
|
:record="currentRecord"
|
|
@success="loadData"
|
|
/>
|
|
|
|
<!-- 分配权限弹窗 -->
|
|
<a-modal
|
|
v-model:open="assignMenusVisible"
|
|
title="分配菜单权限"
|
|
:confirm-loading="assignMenusLoading"
|
|
width="500px"
|
|
@ok="handleAssignMenusOk"
|
|
>
|
|
<a-spin :spinning="menuTreeLoading">
|
|
<a-tree
|
|
v-model:checkedKeys="checkedMenuIds"
|
|
:tree-data="menuTree"
|
|
checkable
|
|
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
|
default-expand-all
|
|
/>
|
|
</a-spin>
|
|
</a-modal>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue'
|
|
import { message } from 'ant-design-vue'
|
|
import { PlusOutlined } from '@ant-design/icons-vue'
|
|
|
|
import { buildMenuTree } from '@/utils/route'
|
|
|
|
import { getRoleList, getRolePage, deleteRole, getRoleMenuIds, assignRoleMenus } from '@/api/system/role'
|
|
import { getMenuList } from '@/api/system/menu'
|
|
import type { RoleRecord, RoleFormData } from '@/types/system/role'
|
|
import type { SysMenu } from '@/types/api/auth'
|
|
|
|
import RoleFormModal from '@/components/system/role/RoleFormModal.vue'
|
|
|
|
const loading = ref(false)
|
|
const tableData = ref<RoleRecord[]>([])
|
|
|
|
// 表格列定义
|
|
const columns = [
|
|
{ title: '角色编码', dataIndex: 'code', key: 'code' },
|
|
{ title: '角色名称', dataIndex: 'name', key: 'name' },
|
|
{ title: '描述', dataIndex: 'description', key: 'description' },
|
|
{ title: '排序', dataIndex: 'sort', key: 'sort', width: 80 },
|
|
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
|
|
{ title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', width: 180 },
|
|
{ title: '操作', key: 'action', width: 220, fixed: 'right' as const }
|
|
]
|
|
|
|
// 弹窗相关
|
|
const modalVisible = ref(false)
|
|
const currentRecord = ref<RoleFormData | undefined>(undefined)
|
|
|
|
// 分配权限相关
|
|
const assignMenusVisible = ref(false)
|
|
const assignMenusLoading = ref(false)
|
|
const menuTreeLoading = ref(false)
|
|
const menuTree = ref<any[]>([])
|
|
const checkedMenuIds = ref<number[]>([])
|
|
const currentRoleId = ref<number>()
|
|
|
|
// 加载数据
|
|
async function loadData() {
|
|
loading.value = true
|
|
try {
|
|
const res = await getRolePage({ pageSize: 100 })
|
|
tableData.value = res.records || []
|
|
} catch (error) {
|
|
console.error('加载数据失败:', error)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function handleAdd() {
|
|
currentRecord.value = undefined
|
|
modalVisible.value = true
|
|
}
|
|
|
|
function handleEdit(record: RoleRecord) {
|
|
currentRecord.value = { ...record }
|
|
modalVisible.value = true
|
|
}
|
|
|
|
async function handleDelete(id: number) {
|
|
try {
|
|
await deleteRole(id)
|
|
message.success('删除成功')
|
|
loadData()
|
|
} catch (error) {
|
|
console.error('删除失败:', error)
|
|
}
|
|
}
|
|
|
|
async function handleAssignMenus(record: RoleRecord) {
|
|
currentRoleId.value = record.id
|
|
checkedMenuIds.value = []
|
|
assignMenusVisible.value = true
|
|
menuTreeLoading.value = true
|
|
|
|
try {
|
|
// 加载菜单树
|
|
// 加载菜单树
|
|
const menuRes = await getMenuList()
|
|
menuTree.value = buildMenuTree(menuRes.data.data || [])
|
|
|
|
// 加载角色已有的菜单
|
|
const roleMenuRes = await getRoleMenuIds(record.id)
|
|
checkedMenuIds.value = roleMenuRes || []
|
|
} catch (error) {
|
|
console.error('加载数据失败:', error)
|
|
} finally {
|
|
menuTreeLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function handleAssignMenusOk() {
|
|
if (!currentRoleId.value) return
|
|
assignMenusLoading.value = true
|
|
try {
|
|
await assignRoleMenus(currentRoleId.value, checkedMenuIds.value)
|
|
message.success('权限分配成功')
|
|
assignMenusVisible.value = false
|
|
} catch (error) {
|
|
console.error('权限分配失败:', error)
|
|
} finally {
|
|
assignMenusLoading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadData()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page-container {
|
|
padding: 0;
|
|
}
|
|
|
|
.table-card {
|
|
background: #fff;
|
|
}
|
|
</style>
|