Files
shanxia2.0/web-admin/src/views/system/role/index.vue
T
2026-08-21 16:50:47 +08:00

359 lines
11 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.
<!-- 角色管理(系统管理) -->
<!-- eslint-disable vue/no-v-model-argument -->
<template>
<div class="page-container">
<section class="panel">
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" @submit.prevent>
<div class="filter-grid">
<el-form-item label="关键词">
<el-input v-model="searchForm.keyword" placeholder="角色名称" clearable />
</el-form-item>
</div>
<div class="filter-actions-row">
<span class="filter-hint">按角色名称模糊查询。</span>
<div class="filter-actions-end">
<el-button class="btn-ghost" :loading="loading" @click="handleReset"
><el-icon><RefreshLeft /></el-icon>重置</el-button
>
<el-button class="btn-main" type="primary" :loading="loading" @click="handleSearch"
><el-icon><Search /></el-icon>搜索</el-button
>
</div>
</div>
</el-form>
</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="角色名称"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="roleDialogsRef?.openAdd()">
<el-icon><Plus /></el-icon>
新增角色
</el-button>
<el-button
class="btn-ghost"
:disabled="selectedRows.length !== 1"
@click="handleToolbarMenuConfig"
>
<el-icon><Menu /></el-icon>
菜单配置
</el-button>
<el-button
class="btn-ghost"
:disabled="selectedRows.length !== 1"
@click="handleToolbarDataScope"
>
<el-icon><DataAnalysis /></el-icon>
数据权限
</el-button>
<el-button
class="btn-ghost"
:disabled="selectedRows.length !== 1"
@click="handleToolbarEdit"
>
<el-icon><Edit /></el-icon>
编辑
</el-button>
<el-button
class="btn-ghost"
type="danger"
:disabled="selectedRows.length !== 1 || !canDeleteByCreator(selectedRows[0])"
:title="
selectedRows.length === 1 && !canDeleteByCreator(selectedRows[0])
? CREATOR_DELETE_TIP
: ''
"
@click="handleToolbarDelete"
>
<el-icon><Delete /></el-icon>
删除
</el-button>
<el-button
class="btn-ghost"
type="danger"
:disabled="selectedRows.length === 0 || !allRowsDeletableByCreator(selectedRows)"
:title="allRowsDeletableByCreator(selectedRows) ? '' : CREATOR_DELETE_TIP"
@click="handleBatchDelete"
>
<el-icon><Delete /></el-icon>
批量删除
</el-button>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
<div class="list-body list-wrap">
<ListTable
table-key="system-role"
class="list-table"
:data="tableData"
v-loading="loading"
stripe
row-key="id"
@selection-change="handleSelectionChange"
@row-dblclick="handleView"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
header-align="center"
:index="(index: number) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column
prop="roleName"
label="角色名称"
min-width="140"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-link type="primary" @click="handleView(row)">{{ row.roleName }}</el-link>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('dataScope')"
prop="dataScope"
label="数据权限"
min-width="120"
align="center"
header-align="center"
>
<template #default="{ row }">
{{ dataScopeLabel(row.dataScope) }}
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('status')"
prop="status"
label="状态"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag :type="dictStore.getTagType('sys_status', row.status)" size="small">
{{ dictStore.getLabel('sys_status', row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="创建时间"
min-width="180"
align="center"
header-align="center"
/>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<el-pagination
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="
(val: number) => ((pagination.page = 1), (pagination.pageSize = val), loadData())
"
@current-change="(val: number) => ((pagination.page = val), loadData())"
/>
</div>
</div>
<RoleManageDialogs ref="roleDialogsRef" @changed="loadData" />
</section>
</div>
</template>
<script setup lang="ts">
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import RoleManageDialogs from '@/components/business/role-manage-dialogs/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, onMounted } from 'vue'
import {
canDeleteByCreator,
filterDeletableByCreator,
allRowsDeletableByCreator,
CREATOR_DELETE_TIP
} from '@/utils/creator-delete'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
DataAnalysis,
RefreshLeft,
Search,
Menu,
Edit,
Delete
} from '@element-plus/icons-vue'
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
import { useDictStore } from '@/store/modules/dict'
import { fetchRoleList, fetchDeleteRole } from '@/api/admin'
defineOptions({ name: 'SystemRole' })
const listColumnDefs = [
{ prop: 'dataScope', label: '数据权限' },
{ prop: 'status', label: '状态' },
{ prop: 'createTime', label: '创建时间' }
]
const { visibleColumns, checkAll, isIndeterminate, isColumnVisible, resetToDefault } =
usePersistedListColumns('system-role-list-columns', listColumnDefs)
const dictStore = useDictStore()
const dataScopeLabel = (code: string) => dictStore.getLabel('sys_data_scope', code)
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Role.RoleItem[]>([])
const selectedRows = ref<Api.Role.RoleItem[]>([])
const loading = ref(false)
const pagination = reactive({ page: 1, pageSize: 20, total: 0 })
const roleDialogsRef = ref<InstanceType<typeof RoleManageDialogs> | null>(null)
const handleSelectionChange = (rows: Api.Role.RoleItem[]) => {
selectedRows.value = rows
}
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchRoleList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
} finally {
loading.value = false
}
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleView(row: Api.Role.RoleItem) {
roleDialogsRef.value?.openView(row)
}
function handleToolbarMenuConfig() {
if (selectedRows.value.length !== 1) {
ElMessage.warning('请选择一条角色')
return
}
roleDialogsRef.value?.openMenuConfig(selectedRows.value[0])
}
function handleToolbarDataScope() {
if (selectedRows.value.length !== 1) {
ElMessage.warning('请选择一条角色')
return
}
roleDialogsRef.value?.openDataScope(selectedRows.value[0])
}
function handleToolbarEdit() {
if (selectedRows.value.length !== 1) {
ElMessage.warning('请选择一条角色')
return
}
roleDialogsRef.value?.openEdit(selectedRows.value[0])
}
function handleToolbarDelete() {
if (selectedRows.value.length !== 1) {
ElMessage.warning('请选择一条角色')
return
}
roleDialogsRef.value?.openDelete(selectedRows.value[0])
}
async function handleBatchDelete() {
const deletable = filterDeletableByCreator(selectedRows.value)
if (!deletable.length) {
ElMessage.warning(CREATOR_DELETE_TIP)
return
}
if (deletable.length < selectedRows.value.length) {
ElMessage.warning(CREATOR_DELETE_TIP)
return
}
const ids = deletable.map((r) => r.id)
if (!ids.length) {
ElMessage.warning('请至少选择一项')
return
}
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 个角色吗?`, '提示', {
type: 'warning'
})
await withFullscreenLoading(async () => {
for (const id of ids) {
await fetchDeleteRole(id)
}
ElMessage.success('批量删除成功')
selectedRows.value = []
await loadData()
}, fullscreenLoadingText.batchDelete)
}
watchDebounced(
searchForm,
() => {
pagination.page = 1
loadData()
},
{ debounce: 300, deep: true }
)
onMounted(() => {
loadData()
})
</script>