/js/vue/ss-components.js 第 5715-5895 行
headerImagecollapse(默认):只显示图标,鼠标悬停时浮动显示文字fixed:只显示图标,不展开浮动v-show,页面打开时全部加载// 删除
headerImage: { type: String, default: "" },
// 新增
initialMode: {
type: String,
default: 'collapse', // 'collapse' | 'fixed'
validator: (v) => ['collapse', 'fixed'].includes(v)
},
setup(props, { emit }) {
// ... 原有逻辑保留 ...
// ===== 新增:菜单模式管理 =====
const menuMode = ref(props.initialMode); // 'collapse' | 'fixed'
const isHovering = ref(false); // 鼠标是否在菜单区域
// 切换模式
const toggleMenuMode = () => {
menuMode.value = menuMode.value === 'collapse' ? 'fixed' : 'collapse';
};
// 鼠标进入/离开
const onMouseEnter = () => {
if (menuMode.value === 'collapse') {
isHovering.value = true;
}
};
const onMouseLeave = () => {
isHovering.value = false;
};
// 是否显示文字(展开状态)
const isExpanded = computed(() => {
return menuMode.value === 'collapse' && isHovering.value;
});
// ===== 新增:iframe 懒加载 =====
const loadedMenus = ref(new Set()); // 已加载过的菜单 name
// 修改 selectItem
const selectItem = (item) => {
currentMenu.value = item;
// 标记为已加载
if (item.name) {
loadedMenus.value.add(item.name);
}
emit("menu-change", item);
};
// 初始化:默认选中项需要加入已加载集合
watch(currentMenu, (menu) => {
if (menu?.name) {
loadedMenus.value.add(menu.name);
}
}, { immediate: true });
// 判断菜单是否已加载
const isMenuLoaded = (menuName) => {
return loadedMenus.value.has(menuName);
};
return {
// 原有
currentMenu,
selectItem,
handleFooterClick,
// 新增
menuMode,
isHovering,
isExpanded,
toggleMenuMode,
onMouseEnter,
onMouseLeave,
isMenuLoaded,
};
}
<div class="project-edit-container">
<!-- 左侧菜单 -->
<div class="left-side"
v-if="leftDisplay"
:data-mode="menuMode"
:class="{ 'is-expanded': isExpanded }"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave">
<!-- 删除:头部图片 -->
<!-- 菜单内容 -->
<div class="menu-content">
<div class="scroll-view">
<template v-for="(menuItem, i) in menuList" :key="i">
<!-- 分组菜单 -->
<div v-if="menuItem.children?.length > 0" class="group">
<div class="menu-item" @click="menuItem.open = !menuItem.open">
<ss-icon :name="menuItem.icon || 'folder'" class="menu-icon" size="20px" />
<span class="menu-label" v-show="isExpanded">{{ menuItem.title }}</span>
<span class="arrow" v-show="isExpanded">
<ss-icon :name="menuItem.open ? 'arrow-up' : 'arrow-down'" size="16px" />
</span>
<!-- 收起时的悬浮提示 -->
<div class="menu-tooltip" v-show="!isExpanded">{{ menuItem.title }}</div>
</div>
<div v-show="menuItem.open && isExpanded" class="group-detail">
<div v-for="(item, j) in menuItem.children"
:key="j"
class="menu-item"
:class="{ active: item.name === currentMenu?.name }"
@click="selectItem(item)">
<ss-icon :name="item.icon || 'file'" class="menu-icon" size="18px" />
<span class="menu-label">{{ item.title }}</span>
<span class="menu-item-point" v-if="item.cgxList || item.objectList"></span>
</div>
</div>
</div>
<!-- 普通菜单项 -->
<div v-else
class="menu-item"
:class="{ active: menuItem.name === currentMenu?.name }"
@click="selectItem(menuItem)">
<ss-icon :name="menuItem.icon || 'file'" class="menu-icon" size="20px" />
<span class="menu-label" v-show="isExpanded">{{ menuItem.title }}</span>
<span class="menu-item-point" v-if="menuItem.cgxList || menuItem.objectList"></span>
<!-- 收起时的悬浮提示 -->
<div class="menu-tooltip" v-show="!isExpanded">{{ menuItem.title }}</div>
</div>
</template>
</div>
</div>
<!-- 底部:模式切换按钮 -->
<div class="menu-mode-toggle" @click="toggleMenuMode">
<ss-icon :name="menuMode === 'collapse' ? 'pin' : 'pin-fill'" size="20px" />
<span class="menu-label" v-show="isExpanded">
{{ menuMode === 'collapse' ? '固定菜单' : '取消固定' }}
</span>
</div>
<!-- 底部按钮(保留原有) -->
<div v-if="footerButtons.length > 0" class="sub-tab-menu-footer" @click="footerButtons[0].onclick">
<!-- ... 保持原有 ... -->
</div>
</div>
<!-- 右侧内容区域 - 懒加载 iframe -->
<div class="content-area fit-height-content"
style="overflow: hidden;"
:style="!leftDisplay ? { width: '100%' } : {}">
<template v-for="(menuItem, i) in menuList" :key="i">
<!-- 只有加载过的才渲染 iframe -->
<iframe
v-if="isMenuLoaded(menuItem.name)"
:src="menuItem.url"
style="height: 100%;width: 100%;"
frameborder="0"
class="sub-tab-iframe"
:id="i === 0 ? 'sub-tab-iframe' : ''"
v-show="currentMenu?.name === menuItem.name"
/>
</template>
</div>
</div>
/* 现有:固定 180px 宽度 */
.project-edit-container .left-side {
width: 180px !important; /* 需要改为动态 */
}
.project-edit-container>div.content-area {
width: calc(100% - 180px); /* 需要改为动态 */
}
/* 原来 */
.project-edit-container .left-side {
width: 180px !important;
border-right: 1px solid #e2e4ec;
background-color: #edf1f5;
}
/* 改为 */
.project-edit-container .left-side {
width: 60px; /* 默认收起宽度 */
border-right: 1px solid #e2e4ec;
background-color: #edf1f5;
transition: width 0.2s ease;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 展开状态 */
.project-edit-container .left-side.is-expanded {
width: 200px;
}
/* 固定模式:始终收起,不响应悬浮 */
.project-edit-container .left-side[data-mode="fixed"] {
width: 60px !important;
}
/* 原来 */
.project-edit-container>div.content-area {
width: calc(100% - 180px);
overflow-y: auto;
}
/* 改为 */
.project-edit-container>div.content-area {
flex: 1; /* 自动填充剩余空间 */
overflow-y: auto;
}
/* 原来 */
.project-edit-container .menu-item,
.project-edit-container .group .menu-item {
padding: 20px 12px 20px 30px;
cursor: pointer;
position: relative;
color: #333333;
display: flex;
align-items: center;
}
/* 改为 */
.project-edit-container .menu-item,
.project-edit-container .group .menu-item {
padding: 15px;
cursor: pointer;
position: relative;
color: #333333;
display: flex;
align-items: center;
justify-content: center; /* 收起时图标居中 */
gap: 10px;
}
/* 展开时左对齐 */
.project-edit-container .left-side.is-expanded .menu-item {
justify-content: flex-start;
padding: 15px 20px;
}
/* 删除或注释掉 */
/* .project-edit-container .menu-header {
height: 120px;
border-bottom: 1px solid #d8dae3;
} */
/* 修改 menu-content 高度 */
.project-edit-container .menu-content {
height: calc(100% - 50px); /* 原来是 calc(100% - 60px),去掉图片后调整 */
flex: 1;
overflow: hidden;
}
/* 原来:固定位置 */
.project-edit-container .menu-item .arrow {
position: absolute;
left: 180px;
}
/* 改为:相对定位,放在右侧 */
.project-edit-container .menu-item .arrow {
margin-left: auto;
}
/* ===== 新增:图标样式 ===== */
.project-edit-container .menu-icon {
flex-shrink: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
/* ===== 新增:文字标签 ===== */
.project-edit-container .menu-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 收起时隐藏文字 */
.project-edit-container .left-side:not(.is-expanded) .menu-label {
display: none;
}
/* ===== 新增:悬浮提示(收起时显示) ===== */
.project-edit-container .menu-tooltip {
position: absolute;
left: calc(100% + 10px);
top: 50%;
transform: translateY(-50%);
background: #393d51;
color: #fff;
padding: 8px 16px;
border-radius: 4px;
white-space: nowrap;
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease;
font-size: 14px;
}
/* 小三角 */
.project-edit-container .menu-tooltip::before {
content: "";
position: absolute;
left: -6px;
top: 50%;
transform: translateY(-50%);
border: 6px solid transparent;
border-right-color: #393d51;
border-left: none;
}
/* 悬浮显示 tooltip */
.project-edit-container .left-side:not(.is-expanded) .menu-item:hover .menu-tooltip {
opacity: 1;
}
/* 展开时隐藏 tooltip */
.project-edit-container .left-side.is-expanded .menu-tooltip {
display: none;
}
/* ===== 新增:模式切换按钮 ===== */
.project-edit-container .menu-mode-toggle {
height: 50px;
display: flex;
align-items: center;
justify-content: center;
padding: 15px;
cursor: pointer;
border-top: 1px solid #d8dae3;
color: #666;
gap: 10px;
flex-shrink: 0;
}
.project-edit-container .menu-mode-toggle:hover {
background: #e0e4ea;
color: #333;
}
/* 展开时左对齐 */
.project-edit-container .left-side.is-expanded .menu-mode-toggle {
justify-content: flex-start;
padding: 15px 20px;
}
/* ===== 新增:子菜单样式调整 ===== */
.project-edit-container .group-detail {
background: rgba(0, 0, 0, 0.03);
}
.project-edit-container .group-detail .menu-item {
padding-left: 25px;
}
.project-edit-container .left-side.is-expanded .group-detail .menu-item {
padding-left: 50px;
}
/* 收起时隐藏子菜单 */
.project-edit-container .left-side:not(.is-expanded) .group-detail {
display: none;
}
| 行号 | 原值 | 新值 | 说明 |
|---|---|---|---|
| 4799 | width: 180px !important |
width: 60px |
默认收起宽度 |
| 4808-4811 | .menu-header {...} |
删除或注释 | 去掉头部图片 |
| 4814 | height: calc(100% - 60px) |
flex: 1 |
菜单内容自适应 |
| 4826 | padding: 20px 12px 20px 30px |
padding: 15px |
菜单项内边距 |
| 4836 | left: 180px |
margin-left: auto |
箭头位置 |
| 4965 | width: calc(100% - 180px) |
flex: 1 |
右侧内容区自适应 |
菜单项需要增加 icon 字段:
menuList: [
{
name: 'basic',
title: '基本信息',
icon: 'info', // 新增:图标名称
url: '/page/xxx.jsp'
},
{
title: '项目设置',
icon: 'setting', // 新增
children: [
{ name: 'config1', title: '配置1', icon: 'config', url: '...' },
{ name: 'config2', title: '配置2', icon: 'config', url: '...' }
]
}
]
| 项目 | 原实现 | 新实现 |
|---|---|---|
| 头部图片 | 有 headerImage | 删除 |
| 菜单宽度 | 固定宽度 | 60px / 200px 切换 |
| 文字显示 | 始终显示 | 收起时隐藏,悬浮/展开时显示 |
| 模式切换 | 无 | collapse(悬浮展开) / fixed(始终收起) |
| iframe 加载 | v-show 全部加载 | v-if 懒加载(点击才加载) |
| 图标 | 无 | 每个菜单项需要 icon 字段 |
menuList 数据有 icon 字段,否则使用默认图标