ether-admin/src/views/system/Permissions.vue

293 lines
7.9 KiB
Vue

<script setup lang="ts">
import { ref, onMounted, reactive, computed } from 'vue'
import { Button, Drawer, Form, Space, message } from 'ant-design-vue'
import { PlusOutlined, SearchOutlined } from '@ant-design/icons-vue'
import type { Permission } from '@/types'
import { getPermissions, createPermission, updatePermission, deletePermission } from '@/api/permission'
import {
TableToolbar,
TableActions,
Pagination
} from '@/components'
// 表格列定义
const columns = [
{ title: '权限编码', dataIndex: 'code', key: 'code', width: 160 },
{ title: '权限名称', dataIndex: 'name', key: 'name', width: 120 },
{ title: '类型', dataIndex: 'type', key: 'type', width: 90 },
{ title: '资源', dataIndex: 'resource', key: 'resource', width: 200, ellipsis: true },
{ title: '方法', dataIndex: 'method', key: 'method', width: 80 },
{ title: '描述', dataIndex: 'description', key: 'description', width: 180, ellipsis: true },
{ title: '操作', key: 'action', width: 140, fixed: 'right' as const }
]
const permissions = ref<Permission[]>([])
const loading = ref(false)
const drawerVisible = ref(false)
const drawerTitle = ref('')
const formRef = ref()
const submitting = ref(false)
const editingId = ref<string | null>(null)
// 搜索相关
const searchKeyword = ref('')
// 分页相关
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0
})
// 分页后的数据
const paginatedData = computed(() => {
const start = (pagination.current - 1) * pagination.pageSize
const end = start + pagination.pageSize
return permissions.value.slice(start, end)
})
// 表单数据
const formState = reactive({
code: '',
name: '',
type: 'MENU',
resource: '',
method: 'GET',
description: ''
})
const typeOptions = [
{ value: 'MENU', label: '菜单' },
{ value: 'BUTTON', label: '按钮' },
{ value: 'API', label: '接口' }
]
const methodOptions = [
{ value: 'GET', label: 'GET' },
{ value: 'POST', label: 'POST' },
{ value: 'PUT', label: 'PUT' },
{ value: 'DELETE', label: 'DELETE' }
]
const fetchPermissions = async () => {
loading.value = true
try {
const res = await getPermissions()
permissions.value = res.data.data || []
pagination.total = permissions.value.length
} catch {
message.error('获取权限列表失败')
} finally {
loading.value = false
}
}
const getTypeColor = (type: string) => {
const map: Record<string, string> = {
MENU: 'blue',
BUTTON: 'green',
API: 'purple'
}
return map[type] || 'default'
}
const getTypeLabel = (type: string) => {
const map: Record<string, string> = {
MENU: '菜单',
BUTTON: '按钮',
API: '接口'
}
return map[type] || type
}
const resetForm = () => {
formState.code = ''
formState.name = ''
formState.type = 'MENU'
formState.resource = ''
formState.method = 'GET'
formState.description = ''
editingId.value = null
}
const handleAdd = () => {
resetForm()
drawerTitle.value = '新建权限'
drawerVisible.value = true
}
const handleEdit = (record: Permission) => {
editingId.value = record.id
formState.code = record.code
formState.name = record.name
formState.type = record.type
formState.resource = record.resource || ''
formState.method = record.method || 'GET'
formState.description = record.description || ''
drawerTitle.value = '编辑权限'
drawerVisible.value = true
}
const handleDelete = async (id: string) => {
try {
await deletePermission(id)
message.success('删除成功')
fetchPermissions()
} catch {
message.error('删除失败')
}
}
const handleSubmit = async () => {
try {
await formRef.value.validate()
submitting.value = true
if (editingId.value) {
await updatePermission(editingId.value, formState)
message.success('更新成功')
} else {
await createPermission(formState)
message.success('创建成功')
}
drawerVisible.value = false
fetchPermissions()
} catch (error: any) {
if (error.errorFields) return
message.error('操作失败')
} finally {
submitting.value = false
}
}
const handleClose = () => {
formRef.value?.resetFields()
drawerVisible.value = false
}
const handlePageChange = (page: number, pageSize: number) => {
pagination.current = page
pagination.pageSize = pageSize
fetchPermissions()
}
const handleSearch = () => {
pagination.current = 1
fetchPermissions()
}
const handleReset = () => {
searchKeyword.value = ''
pagination.current = 1
fetchPermissions()
}
onMounted(fetchPermissions)
</script>
<template>
<div class="page-container">
<!-- 页面标题 -->
<div class="page-header">
<h2 class="page-title">权限管理</h2>
<div class="page-header-actions">
<Button type="primary" @click="handleAdd">
<PlusOutlined /> 新建权限
</Button>
</div>
</div>
<!-- 筛选区 -->
<div class="filter-bar">
<a-space>
<a-input
v-model:value="searchKeyword"
placeholder="搜索权限编码或名称"
style="width: 240px"
allow-clear
@press-enter="handleSearch"
/>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button @click="handleReset">重置</a-button>
</a-space>
</div>
<!-- 表格 -->
<div class="table-card">
<TableToolbar @refresh="fetchPermissions" />
<a-table
:columns="columns"
:data-source="paginatedData"
:loading="loading"
:row-key="(record: Permission) => record.id"
:pagination="false"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'type'">
<a-tag :color="getTypeColor(record.type)">
{{ getTypeLabel(record.type) }}
</a-tag>
</template>
<template v-else-if="column.key === 'action'">
<TableActions @edit="handleEdit(record)" @delete="handleDelete(record.id)" />
</template>
</template>
</a-table>
<Pagination
v-model:current="pagination.current"
v-model:pageSize="pagination.pageSize"
:total="pagination.total"
@change="handlePageChange"
/>
</div>
<!-- 抽屉 -->
<Drawer
v-model:open="drawerVisible"
:title="drawerTitle"
width="480px"
:footer-style="{ textAlign: 'right' }"
@close="handleClose"
>
<Form
ref="formRef"
:model="formState"
layout="vertical"
:rules="{
code: [{ required: true, message: '请输入权限代码' }],
name: [{ required: true, message: '请输入权限名称' }],
type: [{ required: true, message: '请选择类型' }]
}"
>
<Form.Item label="权限代码" name="code">
<a-input v-model:value="formState.code" placeholder=": system:user:view" />
</Form.Item>
<Form.Item label="权限名称" name="name">
<a-input v-model:value="formState.name" placeholder=": 查看用户" />
</Form.Item>
<Form.Item label="类型" name="type">
<a-select v-model:value="formState.type" :options="typeOptions" />
</Form.Item>
<Form.Item label="资源路径" name="resource">
<a-input v-model:value="formState.resource" placeholder=": /api/users" />
</Form.Item>
<Form.Item label="请求方法" name="method">
<a-select v-model:value="formState.method" :options="methodOptions" />
</Form.Item>
<Form.Item label="描述" name="description">
<a-textarea v-model:value="formState.description" :rows="3" placeholder="权限描述" />
</Form.Item>
</Form>
<template #footer>
<Space>
<Button @click="handleClose">取消</Button>
<Button type="primary" :loading="submitting" @click="handleSubmit">确定</Button>
</Space>
</template>
</Drawer>
</div>
</template>