561 lines
16 KiB
Vue
561 lines
16 KiB
Vue
<template>
|
||
<ArtTableFullScreen>
|
||
<div class="permission-page" id="table-full-screen">
|
||
<!-- 搜索栏 -->
|
||
<ArtSearchBar
|
||
v-model:filter="searchForm"
|
||
:items="searchFormItems"
|
||
:show-expand="false"
|
||
@reset="handleReset"
|
||
@search="handleSearch"
|
||
></ArtSearchBar>
|
||
|
||
<ElCard shadow="never" class="art-table-card">
|
||
<!-- 表格头部 -->
|
||
<ArtTableHeader
|
||
:columnList="columnOptions"
|
||
v-model:columns="columnChecks"
|
||
@refresh="handleRefresh"
|
||
>
|
||
<template #left>
|
||
<ElButton @click="showDialog('add')" v-permission="'permission:add'">新增权限</ElButton>
|
||
</template>
|
||
</ArtTableHeader>
|
||
|
||
<!-- 表格 -->
|
||
<ArtTable
|
||
ref="tableRef"
|
||
row-key="id"
|
||
:data="permissionList"
|
||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||
:default-expand-all="false"
|
||
:marginTop="10"
|
||
:show-pagination="false"
|
||
>
|
||
<template #default>
|
||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||
</template>
|
||
</ArtTable>
|
||
|
||
<!-- 新增/编辑对话框 -->
|
||
<ElDialog
|
||
v-model="dialogVisible"
|
||
:title="dialogType === 'add' ? '新增权限' : '编辑权限'"
|
||
width="600px"
|
||
align-center
|
||
>
|
||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||
<ElFormItem label="权限名称" prop="perm_name">
|
||
<ElInput v-model="form.perm_name" placeholder="请输入权限名称" />
|
||
</ElFormItem>
|
||
<ElFormItem label="权限标识" prop="perm_code">
|
||
<ElInput
|
||
v-model="form.perm_code"
|
||
placeholder="例如:菜单权限:menu:role 按钮权限: role:add"
|
||
type="textarea"
|
||
:rows="2"
|
||
/>
|
||
</ElFormItem>
|
||
<ElFormItem label="权限类型" prop="perm_type">
|
||
<ElSelect v-model="form.perm_type" placeholder="请选择权限类型" style="width: 100%">
|
||
<ElOption
|
||
v-for="option in PERMISSION_TYPE_OPTIONS"
|
||
:key="option.value"
|
||
:label="option.label"
|
||
:value="option.value"
|
||
/>
|
||
</ElSelect>
|
||
</ElFormItem>
|
||
<ElFormItem label="父级权限" prop="parent_id">
|
||
<ElTreeSelect
|
||
v-model="form.parent_id"
|
||
:data="permissionTreeOptions"
|
||
placeholder="请选择父级权限(可选)"
|
||
clearable
|
||
check-strictly
|
||
:render-after-expand="false"
|
||
style="width: 100%"
|
||
/>
|
||
</ElFormItem>
|
||
<ElFormItem v-if="form.perm_type === PermissionType.MENU" label="菜单路径" prop="url">
|
||
<ElInput v-model="form.url" placeholder="请输入菜单路径,如:/system/user" />
|
||
</ElFormItem>
|
||
<ElFormItem label="适用端口" prop="platform">
|
||
<ElSelect v-model="form.platform" placeholder="请选择适用端口" style="width: 100%">
|
||
<ElOption label="全部" value="all" />
|
||
<ElOption label="Web后台" value="web" />
|
||
<ElOption label="H5端" value="h5" />
|
||
</ElSelect>
|
||
</ElFormItem>
|
||
<ElFormItem label="排序序号" prop="sort">
|
||
<ElInputNumber v-model="form.sort" :min="0" :max="9999" style="width: 100%" />
|
||
</ElFormItem>
|
||
<ElFormItem label="状态" prop="status">
|
||
<ElRadioGroup v-model="form.status">
|
||
<ElRadio :value="1">启用</ElRadio>
|
||
<ElRadio :value="0">禁用</ElRadio>
|
||
</ElRadioGroup>
|
||
</ElFormItem>
|
||
</ElForm>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||
<ElButton type="primary" @click="handleSubmit" :loading="submitLoading"
|
||
>提交</ElButton
|
||
>
|
||
</div>
|
||
</template>
|
||
</ElDialog>
|
||
</ElCard>
|
||
</div>
|
||
</ArtTableFullScreen>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { h } from 'vue'
|
||
import {
|
||
ElMessage,
|
||
ElMessageBox,
|
||
ElTag,
|
||
ElRadio,
|
||
ElRadioGroup,
|
||
type FormInstance,
|
||
type FormRules
|
||
} from 'element-plus'
|
||
import { PermissionService } from '@/api/modules'
|
||
import type { CreatePermissionParams, PermissionTreeNode } from '@/types/api'
|
||
import type { SearchFormItem } from '@/types'
|
||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||
import { useAuth } from '@/composables/useAuth'
|
||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||
import {
|
||
PermissionType,
|
||
PERMISSION_TYPE_OPTIONS,
|
||
PERMISSION_TYPE_SELECT_OPTIONS,
|
||
getPermissionTypeText,
|
||
getPermissionTypeTag,
|
||
CommonStatus,
|
||
getStatusText
|
||
} from '@/config/constants'
|
||
|
||
defineOptions({ name: 'Permission' })
|
||
|
||
const { hasAuth } = useAuth()
|
||
|
||
// 搜索表单初始值
|
||
const initialSearchState = {
|
||
perm_name: '',
|
||
perm_code: '',
|
||
perm_type: undefined as number | undefined
|
||
}
|
||
|
||
// 搜索表单
|
||
const searchForm = reactive({ ...initialSearchState })
|
||
|
||
// 搜索表单配置
|
||
const searchFormItems: SearchFormItem[] = [
|
||
{
|
||
label: '权限名称',
|
||
prop: 'perm_name',
|
||
type: 'input',
|
||
config: {
|
||
clearable: true,
|
||
placeholder: '请输入权限名称'
|
||
}
|
||
},
|
||
{
|
||
label: '权限标识',
|
||
prop: 'perm_code',
|
||
type: 'input',
|
||
config: {
|
||
clearable: true,
|
||
placeholder: '请输入权限标识'
|
||
}
|
||
},
|
||
{
|
||
label: '权限类型',
|
||
prop: 'perm_type',
|
||
type: 'select',
|
||
options: PERMISSION_TYPE_SELECT_OPTIONS,
|
||
config: {
|
||
clearable: true,
|
||
placeholder: '请选择'
|
||
}
|
||
}
|
||
]
|
||
|
||
// 列配置
|
||
const columnOptions = [
|
||
{ label: '权限名称', prop: 'perm_name' },
|
||
{ label: '权限标识', prop: 'perm_code' },
|
||
{ label: '权限类型', prop: 'perm_type' },
|
||
{ label: '菜单路径', prop: 'url' },
|
||
{ label: '适用端口', prop: 'platform' },
|
||
// { label: '可用角色类型', prop: 'available_for_role_types' },
|
||
{ label: '状态', prop: 'status' },
|
||
{ label: '排序', prop: 'sort' },
|
||
{ label: '操作', prop: 'operation' }
|
||
]
|
||
|
||
// 权限列表(树形结构)
|
||
const permissionList = ref<PermissionTreeNode[]>([])
|
||
// 原始权限树数据(用于搜索过滤)
|
||
const originalPermissionTree = ref<PermissionTreeNode[]>([])
|
||
|
||
const tableRef = ref()
|
||
const dialogVisible = ref(false)
|
||
const dialogType = ref('add')
|
||
const currentRow = ref<PermissionTreeNode | null>(null)
|
||
const currentPermissionId = ref<number>(0)
|
||
const submitLoading = ref(false)
|
||
|
||
// 表单引用和数据
|
||
const formRef = ref<FormInstance>()
|
||
const form = reactive<CreatePermissionParams & { status?: number }>({
|
||
perm_name: '',
|
||
perm_code: '',
|
||
perm_type: PermissionType.MENU,
|
||
parent_id: undefined,
|
||
url: '',
|
||
platform: 'all',
|
||
sort: 0,
|
||
status: CommonStatus.ENABLED
|
||
})
|
||
|
||
// 表单验证规则
|
||
const rules = reactive<FormRules>({
|
||
perm_name: [{ required: true, message: '请输入权限名称', trigger: 'blur' }],
|
||
perm_code: [
|
||
{ required: true, message: '请输入权限标识', trigger: 'blur' },
|
||
{
|
||
pattern: /^[a-zA-Z0-9:_-]+$/,
|
||
message: '权限标识只能包含字母、数字、冒号、下划线和连字符',
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
perm_type: [{ required: true, message: '请选择权限类型', trigger: 'change' }]
|
||
})
|
||
|
||
// 权限树选项(用于选择父级权限)
|
||
const permissionTreeOptions = ref<any[]>([])
|
||
|
||
// 动态列配置
|
||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||
{
|
||
prop: 'perm_name',
|
||
label: '权限名称',
|
||
width: 240
|
||
},
|
||
{
|
||
prop: 'perm_code',
|
||
label: '权限标识',
|
||
width: 240
|
||
},
|
||
{
|
||
prop: 'perm_type',
|
||
label: '权限类型',
|
||
width: 120,
|
||
formatter: (row: PermissionTreeNode) => {
|
||
return h(ElTag, { type: getPermissionTypeTag(row.perm_type) }, () =>
|
||
getPermissionTypeText(row.perm_type)
|
||
)
|
||
}
|
||
},
|
||
{
|
||
prop: 'url',
|
||
label: '菜单路径'
|
||
},
|
||
{
|
||
prop: 'platform',
|
||
label: '适用端口',
|
||
width: 120,
|
||
formatter: (row: PermissionTreeNode) => {
|
||
const platformMap: Record<string, string> = {
|
||
all: '全部',
|
||
web: 'Web后台',
|
||
h5: 'H5端'
|
||
}
|
||
return platformMap[row.platform || 'all'] || row.platform
|
||
}
|
||
},
|
||
// {
|
||
// prop: 'available_for_role_types',
|
||
// label: '可用角色类型',
|
||
// width: 160,
|
||
// formatter: (row: PermissionTreeNode) => {
|
||
// if (!row.available_for_role_types) {
|
||
// return '-'
|
||
// }
|
||
// const roleTypeMap: Record<string, string> = {
|
||
// '1': '平台角色',
|
||
// '2': '客户角色'
|
||
// }
|
||
// const types = row.available_for_role_types
|
||
// .split(',')
|
||
// .map((type) => roleTypeMap[type.trim()] || type)
|
||
// return types.join(', ')
|
||
// }
|
||
// },
|
||
{
|
||
prop: 'status',
|
||
label: '状态',
|
||
width: 80,
|
||
formatter: (row: PermissionTreeNode) => {
|
||
const status = row.status ?? CommonStatus.ENABLED
|
||
return h(
|
||
ElTag,
|
||
{
|
||
type: status === CommonStatus.ENABLED ? 'success' : 'info'
|
||
},
|
||
() => getStatusText(status)
|
||
)
|
||
}
|
||
},
|
||
{
|
||
prop: 'sort',
|
||
label: '排序',
|
||
width: 80
|
||
},
|
||
{
|
||
prop: 'operation',
|
||
label: '操作',
|
||
width: 120,
|
||
fixed: 'right',
|
||
formatter: (row: PermissionTreeNode) => {
|
||
const buttons = []
|
||
|
||
if (hasAuth('permission:edit')) {
|
||
buttons.push(
|
||
h(ArtButtonTable, {
|
||
type: 'edit',
|
||
onClick: () => showDialog('edit', row)
|
||
})
|
||
)
|
||
}
|
||
|
||
if (hasAuth('permission:delete')) {
|
||
buttons.push(
|
||
h(ArtButtonTable, {
|
||
type: 'delete',
|
||
onClick: () => deletePermission(row)
|
||
})
|
||
)
|
||
}
|
||
|
||
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
||
}
|
||
}
|
||
])
|
||
|
||
// 过滤权限树(根据搜索条件)
|
||
const filterPermissionTree = (
|
||
tree: PermissionTreeNode[],
|
||
filters: typeof searchForm
|
||
): PermissionTreeNode[] => {
|
||
return tree.reduce<PermissionTreeNode[]>((acc, node) => {
|
||
// 克隆节点避免修改原始数据
|
||
const newNode = { ...node }
|
||
|
||
// 检查当前节点是否匹配搜索条件
|
||
let matches = true
|
||
|
||
if (filters.perm_name && !newNode.perm_name.includes(filters.perm_name)) {
|
||
matches = false
|
||
}
|
||
if (filters.perm_code && !newNode.perm_code.includes(filters.perm_code)) {
|
||
matches = false
|
||
}
|
||
if (filters.perm_type !== undefined && newNode.perm_type !== filters.perm_type) {
|
||
matches = false
|
||
}
|
||
|
||
// 递归过滤子节点
|
||
if (newNode.children && newNode.children.length > 0) {
|
||
newNode.children = filterPermissionTree(newNode.children, filters)
|
||
}
|
||
|
||
// 如果当前节点匹配或有匹配的子节点,则保留
|
||
if (matches || (newNode.children && newNode.children.length > 0)) {
|
||
acc.push(newNode)
|
||
}
|
||
|
||
return acc
|
||
}, [])
|
||
}
|
||
|
||
// 获取权限树
|
||
const getPermissionList = async () => {
|
||
try {
|
||
const response = await PermissionService.getPermissionTree()
|
||
if (response.code === 0) {
|
||
originalPermissionTree.value = response.data || []
|
||
|
||
// 应用搜索过滤
|
||
const hasFilters =
|
||
searchForm.perm_name || searchForm.perm_code || searchForm.perm_type !== undefined
|
||
|
||
if (hasFilters) {
|
||
permissionList.value = filterPermissionTree(originalPermissionTree.value, searchForm)
|
||
} else {
|
||
permissionList.value = originalPermissionTree.value
|
||
}
|
||
|
||
// 构建权限树选项
|
||
buildPermissionTreeOptions()
|
||
}
|
||
} catch (error) {
|
||
console.error(error)
|
||
}
|
||
}
|
||
|
||
// 构建权限树选项
|
||
const buildPermissionTreeOptions = () => {
|
||
const buildTree = (list: PermissionTreeNode[]): any[] => {
|
||
return list.map((item) => ({
|
||
value: item.id,
|
||
label: item.perm_name,
|
||
children: item.children && item.children.length > 0 ? buildTree(item.children) : undefined
|
||
}))
|
||
}
|
||
permissionTreeOptions.value = buildTree(originalPermissionTree.value)
|
||
}
|
||
|
||
// 搜索
|
||
const handleSearch = () => {
|
||
getPermissionList()
|
||
}
|
||
|
||
// 重置
|
||
const handleReset = () => {
|
||
Object.assign(searchForm, { ...initialSearchState })
|
||
getPermissionList()
|
||
}
|
||
|
||
// 刷新表格
|
||
const handleRefresh = () => {
|
||
getPermissionList()
|
||
}
|
||
|
||
// 显示对话框
|
||
const showDialog = async (type: string, row?: PermissionTreeNode) => {
|
||
dialogVisible.value = true
|
||
dialogType.value = type
|
||
|
||
if (formRef.value) {
|
||
formRef.value.resetFields()
|
||
}
|
||
|
||
if (type === 'edit' && row) {
|
||
currentRow.value = row
|
||
currentPermissionId.value = row.id
|
||
// 从API获取完整的权限数据(包含parent_id)
|
||
try {
|
||
const response = await PermissionService.getPermission(row.id)
|
||
if (response.code === 0 && response.data) {
|
||
Object.assign(form, {
|
||
perm_name: response.data.perm_name,
|
||
perm_code: response.data.perm_code,
|
||
perm_type: response.data.perm_type,
|
||
parent_id: response.data.parent_id || undefined,
|
||
url: response.data.url || '',
|
||
platform: response.data.platform || 'all',
|
||
sort: response.data.sort || 0,
|
||
status: response.data.status ?? CommonStatus.ENABLED
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('获取权限详情失败:', error)
|
||
// 如果API获取失败,使用树节点数据作为备选
|
||
Object.assign(form, {
|
||
perm_name: row.perm_name,
|
||
perm_code: row.perm_code,
|
||
perm_type: row.perm_type,
|
||
parent_id: undefined,
|
||
url: row.url || '',
|
||
platform: row.platform || 'all',
|
||
sort: row.sort || 0,
|
||
status: row.status ?? CommonStatus.ENABLED
|
||
})
|
||
}
|
||
} else {
|
||
currentPermissionId.value = 0
|
||
resetForm()
|
||
}
|
||
}
|
||
|
||
// 删除权限
|
||
const deletePermission = (row: PermissionTreeNode) => {
|
||
// 检查是否有子权限
|
||
if (row.children && row.children.length > 0) {
|
||
ElMessage.warning('该权限下存在子权限,请先删除子权限')
|
||
return
|
||
}
|
||
|
||
ElMessageBox.confirm(`确定删除权限 ${row.perm_name} 吗?`, '删除确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'error'
|
||
})
|
||
.then(async () => {
|
||
try {
|
||
await PermissionService.deletePermission(row.id)
|
||
ElMessage.success('删除成功')
|
||
await getPermissionList()
|
||
} catch (error) {
|
||
console.error(error)
|
||
}
|
||
})
|
||
.catch(() => {
|
||
// 用户取消删除
|
||
})
|
||
}
|
||
|
||
// 提交表单
|
||
const handleSubmit = async () => {
|
||
if (!formRef.value) return
|
||
|
||
await formRef.value.validate(async (valid) => {
|
||
if (valid) {
|
||
submitLoading.value = true
|
||
try {
|
||
if (dialogType.value === 'add') {
|
||
await PermissionService.createPermission(form)
|
||
ElMessage.success('新增成功')
|
||
} else {
|
||
await PermissionService.updatePermission(currentPermissionId.value, form)
|
||
ElMessage.success('修改成功')
|
||
}
|
||
dialogVisible.value = false
|
||
if (formRef.value) {
|
||
formRef.value.resetFields()
|
||
}
|
||
await getPermissionList()
|
||
} catch (error) {
|
||
console.error(error)
|
||
} finally {
|
||
submitLoading.value = false
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 重置表单
|
||
const resetForm = () => {
|
||
Object.assign(form, {
|
||
perm_name: '',
|
||
perm_code: '',
|
||
perm_type: PermissionType.MENU,
|
||
parent_id: undefined,
|
||
url: '',
|
||
platform: 'all',
|
||
sort: 0,
|
||
status: CommonStatus.ENABLED
|
||
})
|
||
}
|
||
|
||
// 页面加载时获取权限列表
|
||
onMounted(() => {
|
||
getPermissionList()
|
||
})
|
||
</script>
|