mirror of
https://gitee.com/hhyykk/ipms-sjy-ui.git
synced 2025-02-13 09:05:00 +08:00
feat:菜单列表改为虚拟化树形控件实现
This commit is contained in:
parent
ecfe1b6145
commit
02fa679d62
@ -67,43 +67,64 @@
|
|||||||
|
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table
|
<el-tree-v2
|
||||||
v-if="refreshTable"
|
v-if="refreshTable"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
:data="list"
|
:data="list"
|
||||||
:default-expand-all="isExpandAll"
|
:props="{
|
||||||
row-key="id"
|
label: 'name',
|
||||||
|
children: 'children'
|
||||||
|
}"
|
||||||
|
:default-expanded-keys="isExpandAll ? list.map(item => item.id) : []"
|
||||||
|
:height="600"
|
||||||
|
:item-size="40"
|
||||||
|
:virtual-scroll-horizontal="true"
|
||||||
|
:highlight-current="true"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
>
|
>
|
||||||
<el-table-column :show-overflow-tooltip="true" label="菜单名称" prop="name" width="250" />
|
<template #default="{ data }">
|
||||||
<el-table-column align="center" label="图标" prop="icon" width="100">
|
<div
|
||||||
<template #default="scope">
|
class="custom-tree-node"
|
||||||
<Icon :icon="scope.row.icon" />
|
:class="{ 'menu-item': true }"
|
||||||
</template>
|
>
|
||||||
</el-table-column>
|
<div class="node-content">
|
||||||
<el-table-column label="排序" prop="sort" width="60" />
|
<span class="label">{{ data.name }}</span>
|
||||||
<el-table-column :show-overflow-tooltip="true" label="权限标识" prop="permission" />
|
<div v-if="currentNode === data" class="menu-info">
|
||||||
<el-table-column :show-overflow-tooltip="true" label="组件路径" prop="component" />
|
<span class="info-item" v-if="data.icon">
|
||||||
<el-table-column :show-overflow-tooltip="true" label="组件名称" prop="componentName" />
|
<span class="info-label">图标:</span>
|
||||||
<el-table-column label="状态" prop="status">
|
<span class="icon-preview">
|
||||||
<template #default="scope">
|
<Icon :icon="data.icon" />
|
||||||
<el-switch
|
<span class="icon-name">{{ data.icon }}</span>
|
||||||
class="ml-4px"
|
</span>
|
||||||
v-model="scope.row.status"
|
</span>
|
||||||
v-hasPermi="['system:menu:update']"
|
<span class="info-item">
|
||||||
:active-value="CommonStatusEnum.ENABLE"
|
<span class="info-label">排序:</span>
|
||||||
:inactive-value="CommonStatusEnum.DISABLE"
|
<span class="info-value">{{ data.sort }}</span>
|
||||||
:loading="menuStatusUpdating[scope.row.id]"
|
</span>
|
||||||
@change="(val) => handleStatusChanged(scope.row, val as number)"
|
<span class="info-item" v-if="data.permission">
|
||||||
/>
|
<span class="info-label">权限标识:</span>
|
||||||
</template>
|
<span class="info-value">{{ data.permission }}</span>
|
||||||
</el-table-column>
|
</span>
|
||||||
<el-table-column align="center" label="操作">
|
<span class="info-item" v-if="data.path">
|
||||||
<template #default="scope">
|
<span class="info-label">路由地址:</span>
|
||||||
|
<span class="info-value">{{ data.path }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="info-item" v-if="data.component">
|
||||||
|
<span class="info-label">组件路径:</span>
|
||||||
|
<span class="info-value">{{ data.component }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="info-item" v-if="data.componentName">
|
||||||
|
<span class="info-label">组件名称:</span>
|
||||||
|
<span class="info-value">{{ data.componentName }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-show="currentNode === data" class="operations">
|
||||||
<el-button
|
<el-button
|
||||||
v-hasPermi="['system:menu:update']"
|
v-hasPermi="['system:menu:update']"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="openForm('update', scope.row.id)"
|
@click.stop="openForm('update', data.id)"
|
||||||
>
|
>
|
||||||
修改
|
修改
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -111,7 +132,7 @@
|
|||||||
v-hasPermi="['system:menu:create']"
|
v-hasPermi="['system:menu:create']"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="openForm('create', undefined, scope.row.id)"
|
@click.stop="openForm('create', undefined, data.id)"
|
||||||
>
|
>
|
||||||
新增
|
新增
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -119,13 +140,14 @@
|
|||||||
v-hasPermi="['system:menu:delete']"
|
v-hasPermi="['system:menu:delete']"
|
||||||
link
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
@click="handleDelete(scope.row.id)"
|
@click.stop="handleDelete(data.id)"
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-tree-v2>
|
||||||
</el-table>
|
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
@ -155,13 +177,26 @@ const queryParams = reactive({
|
|||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
const isExpandAll = ref(false) // 是否展开,默认全部折叠
|
const isExpandAll = ref(false) // 是否展开,默认全部折叠
|
||||||
const refreshTable = ref(true) // 重新渲染表格状态
|
const refreshTable = ref(true) // 重新渲染表格状态
|
||||||
|
const currentNode = ref<any>(null) // 当前选中节点
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await MenuApi.getMenuList(queryParams)
|
const data = await MenuApi.getMenuList(queryParams)
|
||||||
list.value = handleTree(data)
|
// 为每个节点添加 showInfo 属性和样式对象
|
||||||
|
const addProps = (items: any[]) => {
|
||||||
|
items.forEach(item => {
|
||||||
|
item.showInfo = false
|
||||||
|
item.popupStyle = {}
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
addProps(item.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const processedData = handleTree(data)
|
||||||
|
addProps(processedData)
|
||||||
|
list.value = processedData
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@ -233,8 +268,136 @@ const handleStatusChanged = async (menu: MenuVO, val: number) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (data: any) => {
|
||||||
|
currentNode.value = data
|
||||||
|
// 关闭所有信息面板
|
||||||
|
list.value.forEach((item: any) => {
|
||||||
|
item.showInfo = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加点击外部关闭弹出层的处理
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('click', (event: MouseEvent) => {
|
||||||
|
const target = event.target as HTMLElement
|
||||||
|
if (!target.closest('.menu-info-popup') && !target.closest('.info-button')) {
|
||||||
|
list.value.forEach((item: any) => {
|
||||||
|
item.showInfo = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getList()
|
getList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.el-tree-node.is-current > .el-tree-node__content) {
|
||||||
|
background-color: var(--el-color-primary-light-7) !important;
|
||||||
|
|
||||||
|
.custom-tree-node {
|
||||||
|
background-color: var(--el-color-primary-light-7);
|
||||||
|
|
||||||
|
.operations {
|
||||||
|
background-color: var(--el-color-primary-light-7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tree-node {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 40px;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||||
|
min-width: 800px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
|
||||||
|
.node-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
overflow-x: auto;
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--el-border-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-preview {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
|
background-color: var(--el-bg-color);
|
||||||
|
|
||||||
|
.icon-name {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.operations {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: sticky;
|
||||||
|
right: 8px;
|
||||||
|
padding-left: 8px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user