树结构
This commit is contained in:
@@ -8,57 +8,52 @@
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="deptList"
|
||||
style="width: 100%"
|
||||
<div class="tree-container">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
row-key="deptID"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
:data="deptList"
|
||||
:props="treeProps"
|
||||
node-key="deptID"
|
||||
:default-expand-all="true"
|
||||
:expand-on-click-node="false"
|
||||
:check-on-click-node="false"
|
||||
draggable
|
||||
:allow-drop="allowDrop"
|
||||
@node-drop="handleNodeDrop"
|
||||
class="dept-tree"
|
||||
>
|
||||
<el-table-column prop="name" label="部门名称" min-width="200" />
|
||||
<el-table-column prop="deptID" label="部门ID" min-width="150" />
|
||||
<el-table-column prop="description" label="部门描述" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="creatorName" label="创建人" width="120" />
|
||||
<el-table-column prop="createTime" label="创建时间" width="180" />
|
||||
<el-table-column label="操作" width="280" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleAddChild(row)"
|
||||
link
|
||||
>
|
||||
新增子部门
|
||||
<template #default="{ data }">
|
||||
<div class="custom-tree-node">
|
||||
<div class="node-label">
|
||||
<el-icon class="node-icon">
|
||||
<OfficeBuilding />
|
||||
</el-icon>
|
||||
<span class="node-name">{{ data.name }}</span>
|
||||
</div>
|
||||
<div class="node-info">
|
||||
<span class="info-item">ID: {{ data.deptID }}</span>
|
||||
<span class="info-item" v-if="data.description">{{ data.description }}</span>
|
||||
<span class="info-item" v-if="data.creatorName">创建人: {{ data.creatorName }}</span>
|
||||
</div>
|
||||
<div class="node-actions">
|
||||
<el-button size="small" type="primary" @click.stop="handleAddChild(data)">
|
||||
子部门
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleEdit(row)"
|
||||
link
|
||||
>
|
||||
<el-button size="small" type="success" @click.stop="handleEdit(data)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleBindRole(row)"
|
||||
link
|
||||
>
|
||||
绑定角色
|
||||
<el-button size="small" type="warning" @click.stop="handleBindRole(data)">
|
||||
角色
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleDelete(row)"
|
||||
link
|
||||
>
|
||||
<el-button size="small" type="danger" @click.stop="handleDelete(data)">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tree>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog
|
||||
@@ -176,12 +171,13 @@ import { roleApi } from '@/apis/system/role';
|
||||
import { SysDept, SysRole } from '@/types';
|
||||
import { ref, onMounted, reactive, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox, FormInstance, FormRules } from 'element-plus';
|
||||
import { Plus } from '@element-plus/icons-vue';
|
||||
import { Plus, OfficeBuilding } from '@element-plus/icons-vue';
|
||||
|
||||
// 数据状态
|
||||
const deptList = ref<SysDept[]>([]);
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const treeRef = ref();
|
||||
|
||||
// 角色绑定相关数据
|
||||
const roleList = ref<SysRole[]>([]);
|
||||
@@ -217,12 +213,18 @@ const formRules: FormRules = {
|
||||
|
||||
// 级联选择器配置
|
||||
const cascaderProps = {
|
||||
value: 'deptID',
|
||||
label: 'name',
|
||||
value: 'value',
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
checkStrictly: true
|
||||
};
|
||||
|
||||
// 树形组件配置
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
};
|
||||
|
||||
// 父部门选项
|
||||
const parentDeptOptions = computed(() => {
|
||||
return buildParentDeptOptions(deptList.value);
|
||||
@@ -230,18 +232,35 @@ const parentDeptOptions = computed(() => {
|
||||
|
||||
// 构建父部门选项
|
||||
function buildParentDeptOptions(depts: SysDept[]): any[] {
|
||||
return depts.map(dept => ({
|
||||
deptID: dept.deptID,
|
||||
name: dept.name,
|
||||
children: dept.children ? buildParentDeptOptions(dept.children) : undefined
|
||||
}));
|
||||
const result: any[] = [];
|
||||
|
||||
function processNode(dept: SysDept): any {
|
||||
const option = {
|
||||
value: dept.deptID,
|
||||
label: dept.name,
|
||||
children: dept.children && dept.children.length > 0
|
||||
? dept.children.map(processNode)
|
||||
: undefined
|
||||
};
|
||||
return option;
|
||||
}
|
||||
|
||||
depts.forEach(dept => {
|
||||
result.push(processNode(dept));
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 加载部门列表
|
||||
async function loadDeptList() {
|
||||
try {
|
||||
loading.value = true;
|
||||
deptList.value = await deptApi.getAllDepts();
|
||||
const rawData = await deptApi.getAllDepts();
|
||||
console.log('原始部门数据:', rawData);
|
||||
// 将扁平数据转换为树形结构
|
||||
deptList.value = buildTree(rawData);
|
||||
console.log('转换后的树形数据:', deptList.value);
|
||||
} catch (error) {
|
||||
console.error('加载部门列表失败:', error);
|
||||
ElMessage.error('加载部门列表失败');
|
||||
@@ -250,6 +269,50 @@ async function loadDeptList() {
|
||||
}
|
||||
}
|
||||
|
||||
// 将扁平数据转换为树形结构
|
||||
function buildTree(flatData: SysDept[]): SysDept[] {
|
||||
const tree: SysDept[] = [];
|
||||
const map: Record<string, SysDept> = {};
|
||||
|
||||
// 创建映射表
|
||||
flatData.forEach(item => {
|
||||
if (item.deptID) {
|
||||
map[item.deptID] = { ...item, children: [] };
|
||||
}
|
||||
});
|
||||
|
||||
// 构建树形结构
|
||||
flatData.forEach(item => {
|
||||
if (item.deptID) {
|
||||
const node = map[item.deptID];
|
||||
if (item.parentID && map[item.parentID]) {
|
||||
// 有父节点,添加到父节点的children中
|
||||
if (!map[item.parentID].children) {
|
||||
map[item.parentID].children = [];
|
||||
}
|
||||
map[item.parentID].children!.push(node);
|
||||
} else {
|
||||
// 没有父节点或父节点不存在,作为根节点
|
||||
tree.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 清理空的children数组
|
||||
function cleanEmptyChildren(nodes: SysDept[]) {
|
||||
nodes.forEach(node => {
|
||||
if (node.children && node.children.length === 0) {
|
||||
delete node.children;
|
||||
} else if (node.children && node.children.length > 0) {
|
||||
cleanEmptyChildren(node.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanEmptyChildren(tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
// 新增部门
|
||||
function handleAdd() {
|
||||
isEdit.value = false;
|
||||
@@ -451,6 +514,48 @@ async function saveRoleBinding() {
|
||||
onMounted(() => {
|
||||
loadDeptList();
|
||||
});
|
||||
|
||||
// 节点拖拽逻辑
|
||||
function allowDrop(draggingNode: any, dropNode: any) {
|
||||
// 禁止拖拽到自己或子节点
|
||||
return !isDescendant(draggingNode.data, dropNode.data);
|
||||
}
|
||||
|
||||
// 判断是否是后代节点
|
||||
function isDescendant(ancestor: SysDept, node: SysDept): boolean {
|
||||
if (ancestor.deptID === node.deptID) return true;
|
||||
if (node.children) {
|
||||
return node.children.some(child => isDescendant(ancestor, child));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 处理节点拖拽
|
||||
async function handleNodeDrop(draggingNode: any, dropNode: any, dropType: string) {
|
||||
try {
|
||||
const dragData = draggingNode.data as SysDept;
|
||||
const dropData = dropNode.data as SysDept;
|
||||
|
||||
// 更新被拖拽节点的父ID
|
||||
if (dropType === 'inner') {
|
||||
dragData.parentID = dropData.deptID;
|
||||
} else {
|
||||
dragData.parentID = dropData.parentID;
|
||||
}
|
||||
|
||||
// 更新到后端
|
||||
await deptApi.updateDept(dragData);
|
||||
ElMessage.success('部门移动成功');
|
||||
|
||||
// 重新加载部门列表以确保数据一致性
|
||||
await loadDeptList();
|
||||
} catch (error) {
|
||||
console.error('移动部门失败:', error);
|
||||
ElMessage.error('移动部门失败');
|
||||
// 重新加载部门列表以恢复原始状态
|
||||
await loadDeptList();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -471,6 +576,100 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.tree-container {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
|
||||
.dept-tree {
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.node-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
|
||||
.node-icon {
|
||||
margin-right: 8px;
|
||||
color: #67C23A;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.node-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.node-info {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
margin: 0 20px;
|
||||
|
||||
.info-item {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
background: #f4f4f5;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.node-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
|
||||
.el-button {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
|
||||
.node-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Element Plus Tree 组件样式覆盖
|
||||
:deep(.el-tree-node__content) {
|
||||
height: auto !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__expand-icon) {
|
||||
padding: 6px;
|
||||
color: #67C23A;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node) {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__children) {
|
||||
padding-left: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table {
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -8,73 +8,59 @@
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="menuList"
|
||||
style="width: 100%"
|
||||
<div class="tree-container">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
row-key="menuID"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
:data="menuList"
|
||||
:props="treeProps"
|
||||
node-key="menuID"
|
||||
:default-expand-all="true"
|
||||
:expand-on-click-node="false"
|
||||
:check-on-click-node="false"
|
||||
draggable
|
||||
:allow-drop="allowDrop"
|
||||
@node-drop="handleNodeDrop"
|
||||
class="menu-tree"
|
||||
>
|
||||
<el-table-column prop="name" label="菜单名称" min-width="200" />
|
||||
<el-table-column prop="menuID" label="菜单ID" min-width="150" />
|
||||
<el-table-column prop="url" label="菜单路径" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="component" label="菜单组件" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="icon" label="菜单图标" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-icon v-if="row.icon">
|
||||
<component :is="row.icon" />
|
||||
<template #default="{ data }">
|
||||
<div class="custom-tree-node">
|
||||
<div class="node-label">
|
||||
<el-icon v-if="data.icon" class="node-icon">
|
||||
<component :is="data.icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="orderNum" label="排序" width="80" />
|
||||
<el-table-column prop="type" label="菜单类型" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getMenuTypeTagType(row.type)" size="small">
|
||||
{{ getMenuTypeText(row.type) }}
|
||||
<span class="node-name">{{ data.name }}</span>
|
||||
<el-tag
|
||||
:type="getMenuTypeTagType(data.type)"
|
||||
size="small"
|
||||
>
|
||||
{{ getMenuTypeText(data.type) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creatorName" label="创建人" width="120" />
|
||||
<el-table-column prop="createTime" label="创建时间" width="180" />
|
||||
<el-table-column label="操作" width="280" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleAddChild(row)"
|
||||
link
|
||||
>
|
||||
新增子菜单
|
||||
</div>
|
||||
<div class="node-info">
|
||||
<span class="info-item">ID: {{ data.menuID }}</span>
|
||||
<span class="info-item" v-if="data.url">{{ data.url }}</span>
|
||||
<span class="info-item" v-if="data.component">{{ data.component }}</span>
|
||||
<span class="info-item">排序: {{ data.orderNum || 0 }}</span>
|
||||
</div>
|
||||
<div class="node-actions">
|
||||
<el-button size="small" type="primary" @click.stop="handleAddChild(data)">
|
||||
子菜单
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleEdit(row)"
|
||||
link
|
||||
>
|
||||
<el-button size="small" type="success" @click.stop="handleEdit(data)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleBindPermission(row)"
|
||||
link
|
||||
>
|
||||
绑定权限
|
||||
<el-button size="small" type="warning" @click.stop="handleBindPermission(data)">
|
||||
权限
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleDelete(row)"
|
||||
link
|
||||
>
|
||||
<el-button size="small" type="danger" @click.stop="handleDelete(data)">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tree>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog
|
||||
@@ -236,6 +222,7 @@ import { Plus } from '@element-plus/icons-vue';
|
||||
const menuList = ref<SysMenu[]>([]);
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const treeRef = ref();
|
||||
|
||||
// 权限绑定相关数据
|
||||
const permissionList = ref<SysPermission[]>([]);
|
||||
@@ -282,12 +269,18 @@ const formRules: FormRules = {
|
||||
|
||||
// 级联选择器配置
|
||||
const cascaderProps = {
|
||||
value: 'menuID',
|
||||
label: 'name',
|
||||
value: 'value',
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
checkStrictly: true
|
||||
};
|
||||
|
||||
// 树形组件配置
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
};
|
||||
|
||||
// 父菜单选项
|
||||
const parentMenuOptions = computed(() => {
|
||||
return buildParentMenuOptions(menuList.value);
|
||||
@@ -295,11 +288,24 @@ const parentMenuOptions = computed(() => {
|
||||
|
||||
// 构建父菜单选项
|
||||
function buildParentMenuOptions(menus: SysMenu[]): any[] {
|
||||
return menus.map(menu => ({
|
||||
menuID: menu.menuID,
|
||||
name: menu.name,
|
||||
children: menu.children ? buildParentMenuOptions(menu.children) : undefined
|
||||
}));
|
||||
const result: any[] = [];
|
||||
|
||||
function processNode(menu: SysMenu): any {
|
||||
const option = {
|
||||
value: menu.menuID,
|
||||
label: menu.name,
|
||||
children: menu.children && menu.children.length > 0
|
||||
? menu.children.map(processNode)
|
||||
: undefined
|
||||
};
|
||||
return option;
|
||||
}
|
||||
|
||||
menus.forEach(menu => {
|
||||
result.push(processNode(menu));
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 获取菜单类型标签类型
|
||||
@@ -326,7 +332,11 @@ function getMenuTypeText(type: number | undefined): string {
|
||||
async function loadMenuList() {
|
||||
try {
|
||||
loading.value = true;
|
||||
menuList.value = await menuApi.getAllMenuList();
|
||||
const rawData = await menuApi.getAllMenuList();
|
||||
console.log('原始菜单数据:', rawData);
|
||||
// 将扁平数据转换为树形结构
|
||||
menuList.value = buildTree(rawData);
|
||||
console.log('转换后的树形数据:', menuList.value);
|
||||
} catch (error) {
|
||||
console.error('加载菜单列表失败:', error);
|
||||
ElMessage.error('加载菜单列表失败');
|
||||
@@ -335,6 +345,50 @@ async function loadMenuList() {
|
||||
}
|
||||
}
|
||||
|
||||
// 将扁平数据转换为树形结构
|
||||
function buildTree(flatData: SysMenu[]): SysMenu[] {
|
||||
const tree: SysMenu[] = [];
|
||||
const map: Record<string, SysMenu> = {};
|
||||
|
||||
// 创建映射表
|
||||
flatData.forEach(item => {
|
||||
if (item.menuID) {
|
||||
map[item.menuID] = { ...item, children: [] };
|
||||
}
|
||||
});
|
||||
|
||||
// 构建树形结构
|
||||
flatData.forEach(item => {
|
||||
if (item.menuID) {
|
||||
const node = map[item.menuID];
|
||||
if (item.parentID && map[item.parentID]) {
|
||||
// 有父节点,添加到父节点的children中
|
||||
if (!map[item.parentID].children) {
|
||||
map[item.parentID].children = [];
|
||||
}
|
||||
map[item.parentID].children!.push(node);
|
||||
} else {
|
||||
// 没有父节点或父节点不存在,作为根节点
|
||||
tree.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 清理空的children数组
|
||||
function cleanEmptyChildren(nodes: SysMenu[]) {
|
||||
nodes.forEach(node => {
|
||||
if (node.children && node.children.length === 0) {
|
||||
delete node.children;
|
||||
} else if (node.children && node.children.length > 0) {
|
||||
cleanEmptyChildren(node.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanEmptyChildren(tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
// 新增菜单
|
||||
function handleAdd() {
|
||||
isEdit.value = false;
|
||||
@@ -547,6 +601,48 @@ async function savePermissionBinding() {
|
||||
onMounted(() => {
|
||||
loadMenuList();
|
||||
});
|
||||
|
||||
// 节点拖拽逻辑
|
||||
function allowDrop(draggingNode: any, dropNode: any) {
|
||||
// 禁止拖拽到自己或子节点
|
||||
return !isDescendant(draggingNode.data, dropNode.data);
|
||||
}
|
||||
|
||||
// 判断是否是后代节点
|
||||
function isDescendant(ancestor: SysMenu, node: SysMenu): boolean {
|
||||
if (ancestor.menuID === node.menuID) return true;
|
||||
if (node.children) {
|
||||
return node.children.some(child => isDescendant(ancestor, child));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 处理节点拖拽
|
||||
async function handleNodeDrop(draggingNode: any, dropNode: any, dropType: string) {
|
||||
try {
|
||||
const dragData = draggingNode.data as SysMenu;
|
||||
const dropData = dropNode.data as SysMenu;
|
||||
|
||||
// 更新被拖拽节点的父ID
|
||||
if (dropType === 'inner') {
|
||||
dragData.parentID = dropData.menuID;
|
||||
} else {
|
||||
dragData.parentID = dropData.parentID;
|
||||
}
|
||||
|
||||
// 更新到后端
|
||||
await menuApi.updateMenu(dragData);
|
||||
ElMessage.success('菜单移动成功');
|
||||
|
||||
// 重新加载菜单列表以确保数据一致性
|
||||
await loadMenuList();
|
||||
} catch (error) {
|
||||
console.error('移动菜单失败:', error);
|
||||
ElMessage.error('移动菜单失败');
|
||||
// 重新加载菜单列表以恢复原始状态
|
||||
await loadMenuList();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -567,6 +663,104 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.tree-container {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
|
||||
.menu-tree {
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.node-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
|
||||
.node-icon {
|
||||
margin-right: 8px;
|
||||
color: #409EFF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.node-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.node-info {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
margin: 0 20px;
|
||||
|
||||
.info-item {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
background: #f4f4f5;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.node-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
|
||||
.el-button {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
|
||||
.node-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Element Plus Tree 组件样式覆盖
|
||||
:deep(.el-tree-node__content) {
|
||||
height: auto !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__expand-icon) {
|
||||
padding: 6px;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node) {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__children) {
|
||||
padding-left: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table {
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
|
||||
Reference in New Issue
Block a user