Files
one-pipe-system/src/views/system/role/index.vue
2026-01-23 17:18:24 +08:00

527 lines
15 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="role-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')">新增角色</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="ID"
:loading="loading"
:data="roleList"
:currentPage="pagination.page"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<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="30%"
>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
<ElFormItem label="角色名称" prop="role_name">
<ElInput v-model="form.role_name" placeholder="请输入角色名称" />
</ElFormItem>
<ElFormItem label="角色描述" prop="role_desc">
<ElInput
v-model="form.role_desc"
type="textarea"
:rows="3"
placeholder="请输入角色描述"
/>
</ElFormItem>
<ElFormItem label="角色类型" prop="role_type">
<ElSelect v-model="form.role_type" placeholder="请选择角色类型" style="width: 100%">
<ElOption label="平台角色" :value="1" />
<ElOption label="客户角色" :value="2" />
</ElSelect>
</ElFormItem>
<ElFormItem label="状态">
<ElSwitch
v-model="form.status"
:active-value="CommonStatus.ENABLED"
:inactive-value="CommonStatus.DISABLED"
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit(formRef)" :loading="submitLoading">
提交
</ElButton>
</div>
</template>
</ElDialog>
<!-- 分配权限对话框 -->
<ElDialog v-model="permissionDialogVisible" title="分配权限" width="500px">
<ElCheckboxGroup v-model="selectedPermissions">
<div
v-for="permission in allPermissions"
:key="permission.ID"
style="margin-bottom: 12px"
>
<ElCheckbox :label="permission.ID">
{{ permission.perm_name }}
<ElTag
:type="permission.perm_type === 1 ? 'info' : 'success'"
size="small"
style="margin-left: 8px"
>
{{ permission.perm_type === 1 ? '菜单' : '按钮' }}
</ElTag>
</ElCheckbox>
</div>
</ElCheckboxGroup>
<template #footer>
<div class="dialog-footer">
<ElButton @click="permissionDialogVisible = false">取消</ElButton>
<ElButton
type="primary"
@click="handleAssignPermissions"
:loading="permissionSubmitLoading"
>
提交
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { RoleService, PermissionService } from '@/api/modules'
import {
ElMessage,
ElMessageBox,
ElTag,
ElCheckbox,
ElCheckboxGroup,
ElSwitch
} from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type { PlatformRole, Permission } from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
import { CommonStatus, getStatusText } from '@/config/constants'
defineOptions({ name: 'Role' })
const dialogVisible = ref(false)
const permissionDialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
const permissionSubmitLoading = ref(false)
const tableRef = ref()
const currentRoleId = ref<number>(0)
const selectedPermissions = ref<number[]>([])
const originalPermissions = ref<number[]>([]) // 保存原始权限,用于对比
const allPermissions = ref<Permission[]>([])
// 搜索表单初始值
const initialSearchState = {
role_name: ''
}
// 搜索表单
const searchForm = reactive({ ...initialSearchState })
// 搜索表单配置
const searchFormItems: SearchFormItem[] = [
{
label: '角色名称',
prop: 'role_name',
type: 'input',
config: {
clearable: true,
placeholder: '请输入角色名称'
}
}
]
// 分页
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 列配置
const columnOptions = [
{ label: 'ID', prop: 'ID' },
{ label: '角色名称', prop: 'role_name' },
{ label: '角色描述', prop: 'role_desc' },
{ label: '角色类型', prop: 'role_type' },
{ label: '状态', prop: 'status' },
{ label: '创建时间', prop: 'CreatedAt' },
{ label: '操作', prop: 'operation' }
]
const formRef = ref<FormInstance>()
const rules = reactive<FormRules>({
role_name: [
{ required: true, message: '请输入角色名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
role_desc: [{ max: 200, message: '描述不能超过200个字符', trigger: 'blur' }],
role_type: [{ required: true, message: '请选择角色类型', trigger: 'change' }]
})
const form = reactive<any>({
id: 0,
role_name: '',
role_desc: '',
role_type: 1,
status: CommonStatus.ENABLED
})
const roleList = ref<PlatformRole[]>([])
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'ID',
label: 'ID',
width: 80
},
{
prop: 'role_name',
label: '角色名称',
minWidth: 120
},
{
prop: 'role_desc',
label: '角色描述',
minWidth: 150
},
{
prop: 'role_type',
label: '角色类型',
width: 100,
formatter: (row: any) => {
return h(ElTag, { type: row.role_type === 1 ? 'primary' : 'success' }, () =>
row.role_type === 1 ? '平台角色' : '客户角色'
)
}
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row: any) => {
return h(ElSwitch, {
modelValue: row.status,
activeValue: CommonStatus.ENABLED,
inactiveValue: CommonStatus.DISABLED,
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
'onUpdate:modelValue': (val: string | number | boolean) =>
handleStatusChange(row, val as number)
})
}
},
{
prop: 'CreatedAt',
label: '创建时间',
width: 180,
formatter: (row: any) => formatDateTime(row.CreatedAt)
},
{
prop: 'operation',
label: '操作',
width: 180,
fixed: 'right',
formatter: (row: any) => {
return h('div', { style: 'display: flex; gap: 8px;' }, [
h(ArtButtonTable, {
icon: '&#xe72b;',
onClick: () => showPermissionDialog(row)
}),
h(ArtButtonTable, {
type: 'edit',
onClick: () => showDialog('edit', row)
}),
h(ArtButtonTable, {
type: 'delete',
onClick: () => deleteRole(row)
})
])
}
}
])
onMounted(() => {
getTableData()
loadAllPermissions()
})
// 加载所有权限列表
const loadAllPermissions = async () => {
try {
const res = await PermissionService.getPermissions({ page: 1, page_size: 1000 })
if (res.code === 0) {
allPermissions.value = res.data.items || []
}
} catch (error) {
console.error('获取权限列表失败:', error)
}
}
// 显示分配权限对话框
const showPermissionDialog = async (row: PlatformRole) => {
currentRoleId.value = row.ID
selectedPermissions.value = []
originalPermissions.value = []
try {
// 每次打开对话框时重新加载最新的权限列表
await loadAllPermissions()
// 加载当前角色的权限
const res = await RoleService.getRolePermissions(row.ID)
if (res.code === 0 || Array.isArray(res.data)) {
// 提取权限ID数组
const permissions = res.data || []
if (Array.isArray(permissions) && permissions.length > 0) {
// 如果返回的是权限对象数组提取ID
if (typeof permissions[0] === 'object' && 'ID' in permissions[0]) {
selectedPermissions.value = permissions.map((perm: any) => perm.ID)
} else {
// 如果返回的是ID数组
selectedPermissions.value = permissions
}
}
// 保存原始权限,用于后续对比
originalPermissions.value = [...selectedPermissions.value]
// 数据加载完成后再打开对话框
permissionDialogVisible.value = true
}
} catch (error) {
console.error('获取角色权限失败:', error)
}
}
// 提交分配权限
const handleAssignPermissions = async () => {
permissionSubmitLoading.value = true
try {
// 对比原始权限和当前选中的权限,找出需要新增和移除的权限
const addedPermissions = selectedPermissions.value.filter(
(id) => !originalPermissions.value.includes(id)
)
const removedPermissions = originalPermissions.value.filter(
(id) => !selectedPermissions.value.includes(id)
)
// 使用 Promise.all 并发执行新增和移除操作
const promises: Promise<any>[] = []
// 如果有新增的权限,调用分配接口
if (addedPermissions.length > 0) {
promises.push(RoleService.assignPermissions(currentRoleId.value, addedPermissions))
}
// 如果有移除的权限,调用移除接口
if (removedPermissions.length > 0) {
removedPermissions.forEach((permId) => {
promises.push(RoleService.removePermission(currentRoleId.value, permId))
})
}
// 等待所有操作完成
if (promises.length > 0) {
await Promise.all(promises)
ElMessage.success('权限更新成功')
} else {
ElMessage.info('权限未发生变化')
}
permissionDialogVisible.value = false
} catch (error) {
console.error(error)
} finally {
permissionSubmitLoading.value = false
}
}
// 获取角色列表
const getTableData = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
pageSize: pagination.pageSize,
role_name: searchForm.role_name || undefined
}
const res = await RoleService.getRoles(params)
if (res.code === 0) {
roleList.value = res.data.items || []
pagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
} finally {
loading.value = false
}
}
// 重置搜索
const handleReset = () => {
Object.assign(searchForm, { ...initialSearchState })
pagination.page = 1
getTableData()
}
// 搜索
const handleSearch = () => {
pagination.page = 1
getTableData()
}
// 刷新表格
const handleRefresh = () => {
getTableData()
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getTableData()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.page = newCurrentPage
getTableData()
}
const dialogType = ref('add')
// 显示新增/编辑对话框
const showDialog = (type: string, row?: any) => {
dialogVisible.value = true
dialogType.value = type
if (type === 'edit' && row) {
form.id = row.ID
form.role_name = row.role_name
form.role_desc = row.role_desc
form.role_type = row.role_type
form.status = row.status
} else {
form.id = 0
form.role_name = ''
form.role_desc = ''
form.role_type = 1
form.status = CommonStatus.ENABLED
}
}
// 删除角色
const deleteRole = (row: any) => {
ElMessageBox.confirm(`确定删除角色 ${row.role_name} 吗?`, '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'error'
})
.then(async () => {
try {
await RoleService.deleteRole(row.ID)
ElMessage.success('删除成功')
await getTableData()
} catch (error) {
console.error(error)
}
})
.catch(() => {
// 用户取消删除
})
}
// 提交表单
const handleSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
submitLoading.value = true
try {
const data = {
role_name: form.role_name,
role_desc: form.role_desc,
role_type: form.role_type,
status: form.status
}
if (dialogType.value === 'add') {
await RoleService.createRole(data)
ElMessage.success('新增成功')
} else {
await RoleService.updateRole(form.id, data)
ElMessage.success('修改成功')
}
dialogVisible.value = false
formEl.resetFields()
await getTableData()
} catch (error) {
console.error(error)
} finally {
submitLoading.value = false
}
}
})
}
// 状态切换
const handleStatusChange = async (row: any, newStatus: number) => {
const oldStatus = row.status
// 先更新UI
row.status = newStatus
try {
await RoleService.updateRoleStatus(row.ID, newStatus as 0 | 1)
ElMessage.success('状态切换成功')
} catch (error) {
// 切换失败,恢复原状态
row.status = oldStatus
console.error(error)
}
}
</script>