Files
one-pipe-system/src/views/system/permission/index.vue
sexygoat ce1032c7f9
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m29s
完善按钮和详情权限
2026-02-28 11:04:32 +08:00

561 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>