2 次代碼提交 6da891e177 ... f8d12122ea

作者 SHA1 備註 提交日期
  apple f8d12122ea Merge branch 'master' of http://47.107.29.61:3000/Yezb/ss_new_ui_skin_and_page 4 天之前
  apple 49e94eacd5 feat:修改全局左侧菜单样式、修改组件sssubtab组件样式 4 天之前

+ 494 - 0
SsSubTab组件改造方案.md

@@ -0,0 +1,494 @@
+# SsSubTab 组件改造方案
+
+## 文件位置
+`/js/vue/ss-components.js` 第 5715-5895 行
+
+## 改造目标
+
+### 1. UI 模式改造
+- **去掉**:顶部图片 `headerImage`
+- **新增两种模式**:
+  - `collapse`(默认):只显示图标,鼠标悬停时浮动显示文字
+  - `fixed`:只显示图标,不展开浮动
+
+### 2. 性能优化
+- **现状**:iframe 用 `v-show`,页面打开时全部加载
+- **改为**:懒加载,点击哪个才加载哪个
+
+---
+
+## 具体修改方案
+
+### 一、Props 修改
+
+```js
+// 删除
+headerImage: { type: String, default: "" },
+
+// 新增
+initialMode: {
+  type: String,
+  default: 'collapse',  // 'collapse' | 'fixed'
+  validator: (v) => ['collapse', 'fixed'].includes(v)
+},
+```
+
+### 二、Setup 新增逻辑
+
+```js
+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,
+  };
+}
+```
+
+### 三、Template 修改
+
+```html
+<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>
+```
+
+---
+
+## CSS 样式修改(base.css:4784-4968)
+
+### 现有样式分析
+```css
+/* 现有:固定 180px 宽度 */
+.project-edit-container .left-side {
+  width: 180px !important;  /* 需要改为动态 */
+}
+.project-edit-container>div.content-area {
+  width: calc(100% - 180px);  /* 需要改为动态 */
+}
+```
+
+### 需要修改的样式
+
+#### 1. 左侧容器宽度(行 4798-4802)
+```css
+/* 原来 */
+.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;
+}
+```
+
+#### 2. 右侧内容区宽度(行 4964-4967)
+```css
+/* 原来 */
+.project-edit-container>div.content-area {
+  width: calc(100% - 180px);
+  overflow-y: auto;
+}
+
+/* 改为 */
+.project-edit-container>div.content-area {
+  flex: 1;  /* 自动填充剩余空间 */
+  overflow-y: auto;
+}
+```
+
+#### 3. 菜单项样式(行 4824-4832)
+```css
+/* 原来 */
+.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;
+}
+```
+
+#### 4. 删除头部图片相关样式(行 4808-4815)
+```css
+/* 删除或注释掉 */
+/* .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;
+}
+```
+
+#### 5. 箭头位置(行 4834-4837)
+```css
+/* 原来:固定位置 */
+.project-edit-container .menu-item .arrow {
+  position: absolute;
+  left: 180px;
+}
+
+/* 改为:相对定位,放在右侧 */
+.project-edit-container .menu-item .arrow {
+  margin-left: auto;
+}
+```
+
+### 需要新增的样式
+
+```css
+/* ===== 新增:图标样式 ===== */
+.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;
+}
+```
+
+### CSS 修改汇总表
+
+| 行号 | 原值 | 新值 | 说明 |
+|-----|------|------|-----|
+| 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` 字段:
+
+```js
+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 字段 |
+
+---
+
+## 兼容性注意
+
+1. 需要确保 `menuList` 数据有 `icon` 字段,否则使用默认图标
+2. 懒加载后,切换回已加载的菜单不会重新加载(保持 iframe 状态)
+3. 底部按钮功能保持不变

+ 108 - 48
js/vue/ss-components.js

@@ -5711,20 +5711,19 @@ import { EVEN_VAR } from "./EventBus.js";
    * @property {Object} [activeMenu] - 当前选中的菜单项,不传则自动选择第一个可选菜单
    * @property {Array} footerButtons - 底部按钮配置列表
 
+   */
+  /**
+   * SsSubTab 左侧菜单+iframe内容组件
+   * v3.0 改造:去掉顶部图片,改为图标+悬浮模式,iframe懒加载 by xu 20251216
    */
   const SsSubTab = {
     name: "SsSubTab",
     props: {
-      headerImage: {
-        type: String,
-        default: "",
-      },
       menuList: {
         type: Array,
         required: true,
       },
       activeMenu: {
-        // 只需要传入标题
         type: String,
         default: "",
       },
@@ -5736,9 +5735,59 @@ import { EVEN_VAR } from "./EventBus.js";
         type: Boolean,
         default: true,
       },
+      // v3.0 新增:菜单模式 collapse(悬浮展开) / fixed(始终收起) by xu 20251216
+      initialMode: {
+        type: String,
+        default: 'collapse',
+      },
     },
     emits: ["menu-change", "footer-click"],
     setup(props, { emit }) {
+      // v3.0 新增:默认图标映射,使用icon-biz图标 by xu 20251216
+      const defaultIcons = [
+        'icon-obj-ry',       // 人员
+        'icon-obj-dw',       // 单位
+        'icon-obj-gw',       // 岗位
+        'icon-biz-rc',       // 人才
+        'icon-biz-xc',       // 巡查
+        'icon-biz-cl',       // 材料
+        'icon-biz-men',      // 门
+        'icon-obj-xy'        // 协议
+      ];
+      const getMenuIcon = (item, index) => {
+        if (item.icon) return item.icon;
+        // 返回完整的 class:menu-icon (字体族) + 具体图标类
+        return 'menu-icon ' + defaultIcons[index % defaultIcons.length];
+      };
+
+      // v3.0 新增:菜单模式管理 by xu 20251216
+      const menuMode = ref(props.initialMode);
+      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;
+      });
+
+      // v3.0 新增:iframe 懒加载,点击才加载 by xu 20251216
+      const loadedMenus = ref(new Set());
+
+      const isMenuLoaded = (menuName) => {
+        return loadedMenus.value.has(menuName);
+      };
+
       // 根据标题找到对应的菜单项
       const findMenuByTitle = (title) => {
         for (const item of props.menuList) {
@@ -5758,10 +5807,8 @@ import { EVEN_VAR } from "./EventBus.js";
           const menu = findMenuByTitle(props.activeMenu);
           if (menu) return menu;
         }
-
         const firstItem = props.menuList[0];
         if (!firstItem) return null;
-
         return firstItem.children?.length > 0
             ? firstItem.children[0]
             : firstItem;
@@ -5782,9 +5829,20 @@ import { EVEN_VAR } from "./EventBus.js";
           }
       );
 
+      // 初始化:默认选中项加入已加载集合
+      watch(currentMenu, (menu) => {
+        if (menu?.name) {
+          loadedMenus.value.add(menu.name);
+        }
+      }, { immediate: true });
+
       // 选择菜单项时触发 menu-change 钩子
       const selectItem = (item) => {
         currentMenu.value = item;
+        // 标记为已加载
+        if (item.name) {
+          loadedMenus.value.add(item.name);
+        }
         emit("menu-change", item);
       };
 
@@ -5797,18 +5855,24 @@ import { EVEN_VAR } from "./EventBus.js";
         currentMenu,
         selectItem,
         handleFooterClick,
+        menuMode,
+        isHovering,
+        isExpanded,
+        toggleMenuMode,
+        onMouseEnter,
+        onMouseLeave,
+        isMenuLoaded,
+        getMenuIcon,
       };
     },
     template: `
           <div class="project-edit-container">
-              <div class="left-side" v-if="leftDisplay">
-              
-                  <!-- 头部图片 -->
-                  <div v-if="headerImage" class="menu-header">
-                      <div class="thumb">
-                          <img :src="headerImage" style="object-fit: cover;width: 100%;height: 100%;" />
-                      </div>
-                  </div>
+              <div class="left-side"
+                   v-if="leftDisplay"
+                   :data-mode="menuMode"
+                   :class="{ 'is-expanded': isExpanded }"
+                   @mouseenter="onMouseEnter"
+                   @mouseleave="onMouseLeave">
 
                   <!-- 菜单内容 -->
                   <div class="menu-content">
@@ -5817,70 +5881,66 @@ import { EVEN_VAR } from "./EventBus.js";
                               <!-- 分组菜单 -->
                               <div v-if="menuItem.children?.length > 0" class="group">
                                   <div class="menu-item" @click="menuItem.open = !menuItem.open">
-                                      <span>{{ menuItem.title }}</span>
-                                      
+                                      <ss-icon :class="getMenuIcon(menuItem, i)" />
+                                      <span class="menu-label">{{ menuItem.title }}</span>
                                       <span class="arrow">
-                                          <ss-icon :name="menuItem.open ? 'arrow-up' : 'arrow-down'" size="20px" />
+                                          <ss-icon :class="menuItem.open ? 'menu-icon icon-jiantou-shang' : 'menu-icon icon-jiantou-xia'" />
                                       </span>
+                                      <div class="menu-tooltip">{{ menuItem.title }}</div>
                                   </div>
                                   <div v-show="menuItem.open" class="group-detail">
-                                      <div v-for="(item, j) in menuItem.children" 
-                                          :key="j" 
+                                      <div v-for="(item, j) in menuItem.children"
+                                          :key="j"
                                           class="menu-item"
                                           :class="{ active: item.name === currentMenu?.name }"
-                                          @click="selectItem(item)"
-                                      >
-                                          {{ item.title }}
-                                           
-                                           &nbsp;
+                                          @click="selectItem(item)">
+                                          <ss-icon :class="getMenuIcon(item, j)" />
+                                          <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 
+                              <div v-else
                                   class="menu-item"
                                   :class="{ active: menuItem.name === currentMenu?.name }"
-                                  @click="selectItem(menuItem)"
-                              >
-                                    {{ menuItem.title }}
-                                   &nbsp;
+                                  @click="selectItem(menuItem)">
+                                  <ss-icon :class="getMenuIcon(menuItem, i)" />
+                                  <span class="menu-label">{{ menuItem.title }}</span>
                                   <span class="menu-item-point" v-if="menuItem.cgxList || menuItem.objectList"></span>
+                                  <div class="menu-tooltip">{{ menuItem.title }}</div>
                               </div>
                           </template>
                       </div>
                   </div>
-                  
+
+                  <!-- v3.0 模式切换按钮,只显示图标 by xu 20251216 -->
+                  <div class="menu-mode-toggle" @click="toggleMenuMode">
+                      <ss-icon class="menu-base-icon icon-qiehuan" />
+                  </div>
+
                   <!-- 底部按钮 -->
-                  <div v-if="footerButtons.length > 0" 
+                  <div v-if="footerButtons.length > 0"
                       class="sub-tab-menu-footer"
-                      @click="footerButtons[0].onclick"
-                  >
+                      @click="footerButtons[0].onclick">
                       <div>{{ footerButtons[0].text }}</div>
-                      <ss-icon 
-                          v-if="footerButtons.length > 1" 
-                          name="arrow-up" 
-                          size="24px" 
-                      />
-                      
-                      <!-- 悬浮按钮 -->
+                      <ss-icon v-if="footerButtons.length > 1" name="arrow-up" size="24px" />
                       <div v-if="footerButtons.length > 1" class="sub-tab-menu-popup">
-                          <div v-for="(button, index) in footerButtons.slice(1)" 
+                          <div v-for="(button, index) in footerButtons.slice(1)"
                               :key="index"
-                              @click.stop="button.onclick"
-                          >
+                              @click.stop="button.onclick">
                               {{ button.text }}
                           </div>
                       </div>
                   </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"

+ 5 - 5
js/vue/ss-index-components.js

@@ -197,8 +197,8 @@ export const GlobalMenu = {
                 name: item.desc,
                 component: item.url,
                 js: item.js,
-                // v3.0 优先使用 item.icon,没有才用默认图标 by xu 20251215
-                class: item.icon || (item.type == 1 ? 'icon-folder-close' : 'icon-shouye'),
+                // v3.0 优先使用 item.icon,没有才用 menu-base-icon + 默认图标 by xu 20251215
+                class: item.icon || (item.type == 1 ? 'menu-base-icon icon-folder-close' : 'menu-base-icon icon-shouye'),
                 itemType: item.type,
                 children: []
             }));
@@ -355,7 +355,7 @@ export const GlobalMenu = {
                     }, [
                         Vue.h('div', { class: 'menu-item-content' }, [
                             // v3.0 使用 ss-icon + 双 class (图标类 + menu-icon 组类) by xu 20251215
-                            Vue.h(SsIcon, { class: (menuItemsNew.value[0]?.class || 'icon-shouye') + ' menu-icon' }),
+                            Vue.h(SsIcon, { class: 'menu-base-icon icon-shouye'}),
                             Vue.h('div', { class: 'menu-item-label' }, menuItemsNew.value[0]?.name || '首页')
                         ])
                     ]) : null
@@ -393,7 +393,7 @@ export const GlobalMenu = {
                                 Vue.h(SsIcon, {
                                     class: (expandedMenus.value.has(icon.name)
                                         ? 'icon-folder-open'
-                                        : (icon.class || 'icon-folder-close')) + ' menu-icon'
+                                        : (icon.class || 'menu-base-icon icon-folder-close')) + ' menu-icon'
                                 }),
                                 Vue.h('div', { class: 'menu-item-label' }, icon.name || ''),
                                 // 有子菜单时显示小圆点(在一级菜单右上角)
@@ -431,7 +431,7 @@ export const GlobalMenu = {
                         class: 'menu-mode-btn',
                         onClick: toggleMenuMode
                     }, [
-                        Vue.h(SsIcon, { class: 'icon-qiehuan menu-icon' })
+                        Vue.h(SsIcon, { class: 'menu-base-icon icon-qiehuan' })
                     ])
                 ])
             ])

+ 0 - 1
page/home.jsp

@@ -25,7 +25,6 @@
 	<script type="text/javascript" src="/ss/window/dhtmlxcommon.js"></script>
 	<script type="text/javascript" src="/ss/window/dhtmlxwindows.js"></script>
 	<script type="text/javascript" src="/ss/window/dhtmlxcontainer.js"></script>
-	<script type="text/javascript" src="/js/pageC.js"></script> <%-- 对象变动、修改、查看页,左则选项卡宽度 在里面定义 Ben(20251213) --%>
 
 		<%
 		if(pageContext.getSession().getAttribute("ssUser")==null){

+ 217 - 84
skin/easy/css/base.css

@@ -63,9 +63,17 @@
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
 }
-/* hover 不改变图标颜色 by xu 20251215 */
+
 
 /* v3.0 菜单图标类 - 文件夹和首页 by xu 20251215 */
+.menu-base-icon {
+  font-family: "icon-base" !important;
+  font-size: 22px;
+  font-style: normal;
+  color: #565d6d;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
 /* 文件夹图标 - 使用旧图标库 iconfont(新库暂无文件夹图标) */
 .icon-folder-open::before {
   content: "\e60c";
@@ -75,11 +83,7 @@
   content: "\e60b";
   font-family: "iconfont" !important;
 }
-/* 首页图标 - 使用基础图标库 icon-base */
-.icon-shouye::before {
-  content: "\e619" !important;
-  font-family: "icon-base" !important;
-}
+
 
 /* 通用icon */
 .common-icon{
@@ -777,9 +781,22 @@ input::placeholder ,textarea::placeholder{
   left: 0;
   width: var(--left-size-width);
   transition: width 0.2s;
-  display: flex;  /* 添加 flex 布局 */
-  flex-direction: column;  /* 垂直排列 */
-  /* 移除容器边框,改为菜单项边框 by xu 20251212 */
+  display: flex;
+  flex-direction: column;
+  /* v3.0 移除容器边框,改为伪元素边框 by xu 20251212 */
+}
+
+/* v3.0 右侧边框线,覆盖整个左侧高度 by xu 20251216 */
+.layout-container .left-side-container::after {
+  content: "";
+  height: 100%;
+  width: 1px;
+  position: absolute;
+  right: 0;
+  top: 0;
+  background: #dadee2;
+  box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.24);
+  z-index: 0;
 }
 
 .layout-container .left-side-container[size="max"] {
@@ -834,15 +851,9 @@ input::placeholder ,textarea::placeholder{
   position: absolute;
   top: 0;
   left: 0;
-  z-index: 100;
+  z-index: 1;
   overflow: hidden;
   background: #fafbfe;
-  
-  /* display: flex;
-  flex-direction: column;
-  justify-content: flex-start;
-  align-items: center; */
-
 }
 
 .layout-container .left-side-content:hover {
@@ -850,17 +861,6 @@ input::placeholder ,textarea::placeholder{
   overflow-y: auto;
 }
 
-.layout-container .left-side-content:after {
-  content: "";
-  height: 100%;
-  width: 1px;
-  position: absolute;
-  left: 100%;
-  top: 0;
-  background: #dadee2;
-  box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.24);
-}
-
 /* ===== 新菜单样式 =====*/
 /* 三种模式控制 */
 .layout-container .left-side-container[data-mode="expand"] {
@@ -888,9 +888,10 @@ input::placeholder ,textarea::placeholder{
 /* 固定区域:顶部(首页) */
 .layout-container .fixed-top {
   flex-shrink: 0;
-  border-bottom: none;  /* 移除直接的 border */
+  border-bottom: none;
   background: #fafbfe;
   position: relative;
+  z-index: 1;
 }
 
 .layout-container .fixed-top::after {
@@ -909,7 +910,9 @@ input::placeholder ,textarea::placeholder{
   flex: 1;
   overflow-y: auto;
   overflow-x: hidden;
-  background: #fafbfe;
+  /* background: #fafbfe; */
+  position: relative;
+  z-index: 1;
 }
 
 /* 滚动条样式 */
@@ -929,9 +932,11 @@ input::placeholder ,textarea::placeholder{
 /* 固定区域:底部(模式按钮) */
 .layout-container .fixed-bottom {
   flex-shrink: 0;
-  border-top: none;  /* 移除上边框 */
-  background: #edf1f5;  /* 背景色 */
+  border-top: none;
+  background: #edf1f5;
   height: 40px;
+  position: relative;
+  z-index: 1;
 }
 
 /* 一级菜单分隔线(使用:after伪类,90%宽度居中) */
@@ -1006,6 +1011,7 @@ input::placeholder ,textarea::placeholder{
   color: #666;
   transition: all 0.3s;
   min-height: 40px;
+  border-right: 2px solid #dadee2;
 }
 
 .layout-container .menu-mode-btn:hover {
@@ -1348,6 +1354,9 @@ input::placeholder ,textarea::placeholder{
   align-items: center;
   flex-shrink: 0;
 }
+.left-side-container .icon-container.icon-qiehuan{
+  padding: 0 !important;
+}
 /* 选中菜单项隐藏右边框 by xu 20251212 */
 .left-side-container .menu-item.active .menu-item-content{
   background: #edf1f5;
@@ -4774,6 +4783,7 @@ input::placeholder ,textarea::placeholder{
 /* 跨对象弹窗结束 */
 
 /* 项目基本信息开始 */
+/* v3.0 SsSubTab组件改造:去掉顶部图片,改为图标+悬浮模式 by xu 20251216 */
 .project-edit-container {
   width: 100%;
   height: 100%;
@@ -4788,23 +4798,52 @@ input::placeholder ,textarea::placeholder{
   height: 100%;
 }
 
+/* v3.0 左侧菜单默认60px收起 by xu 20251216 */
 .project-edit-container .left-side {
-  width: 180px !important;
-  border-right: 1px solid #e2e4ec;
-  background-color: #edf1f5;
+  width: 60px;
+  background-color: #fafbfe;
+  transition: width 0.2s ease;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  flex-shrink: 0;
+  position: relative;
 }
 
-.project-edit-container .left-side>div {
-  width: 100%;
+/* v3.0 右侧边框线,覆盖整个左侧高度 by xu 20251216 */
+.project-edit-container .left-side::after {
+  content: "";
+  height: 100%;
+  width: 1px;
+  position: absolute;
+  right: 0;
+  top: 0;
+  background: #dadee2;
+  box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.24);
+  z-index: 0;
 }
 
-.project-edit-container .menu-header {
-  height: 120px;
-  border-bottom: 1px solid #d8dae3;
+/* v3.0 展开状态200px by xu 20251216 */
+.project-edit-container .left-side.is-expanded {
+  width: 200px;
+}
+
+/* v3.0 固定模式:始终收起 by xu 20251216 */
+.project-edit-container .left-side[data-mode="fixed"] {
+  width: 60px !important;
+}
+
+.project-edit-container .left-side>div {
+  width: 100%;
+  position: relative;
+  z-index: 1;
 }
 
+/* v3.0 菜单内容区 by xu 20251216 */
 .project-edit-container .menu-content {
-  height: calc(100% - 60px);
+  flex: 1;
+  overflow: hidden;
+  position: relative;
 }
 
 .project-edit-container .menu-content .scroll-view {
@@ -4814,19 +4853,115 @@ input::placeholder ,textarea::placeholder{
   font-size: 18px;
 }
 
+/* v3.0 菜单项样式,参考GlobalMenu by xu 20251216 */
 .project-edit-container .menu-item,
 .project-edit-container .group .menu-item {
-  padding: 20px 12px 20px 30px;
+  height: 60px;
+  width: 100%;
+  box-sizing: border-box;
   cursor: pointer;
   position: relative;
-  color: #333333;
+  color: #565d6d;
   display: flex;
   align-items: center;
+  justify-content: center;
+  gap: 5px;
+  padding: 0 8px;
+  border-right: 2px solid #dadee2;
+}
+
+/* 展开时左对齐 */
+.project-edit-container .left-side.is-expanded .menu-item {
+  justify-content: flex-start;
+}
+
+/* hover效果 */
+.project-edit-container .menu-item:hover {
+  background: #edf1f5;
 }
 
-.project-edit-container .menu-item .arrow{
+/* active效果,隐藏右边框 */
+.project-edit-container .menu-item.active {
+  background: #edf1f5;
+  border-right-color: #edf1f5;
+}
+
+.project-edit-container .menu-item .arrow {
+  margin-left: auto;
+  display: none;
+}
+
+/* v3.0 展开时显示箭头 by xu 20251216 */
+.project-edit-container .left-side.is-expanded .menu-item .arrow {
+  display: block;
+}
+
+/* v3.0 图标容器样式,参考GlobalMenu by xu 20251216 */
+.project-edit-container .icon-container {
+  position: relative;
+  padding: 10px;
+  box-sizing: content-box;
+  width: 24px;
+  height: 24px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-shrink: 0;
+}
+
+/* v3.0 新增:文字标签,收起时隐藏 by xu 20251216 */
+.project-edit-container .menu-label {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  font-size: 18px;
+  color: #565d6d;
+  display: none;
+}
+
+/* v3.0 展开时显示文字 by xu 20251216 */
+.project-edit-container .left-side.is-expanded .menu-label {
+  display: block;
+}
+
+/* v3.0 新增:悬浮提示(收起时显示) by xu 20251216 */
+.project-edit-container .menu-tooltip {
   position: absolute;
-  left: 180px;
+  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;
+}
+
+/* v3.0 tooltip小三角 by xu 20251216 */
+.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;
+}
+
+/* v3.0 悬浮显示tooltip by xu 20251216 */
+.project-edit-container .left-side:not(.is-expanded) .menu-item:hover .menu-tooltip {
+  opacity: 1;
+}
+
+/* v3.0 展开时隐藏tooltip by xu 20251216 */
+.project-edit-container .left-side.is-expanded .menu-tooltip {
+  display: none;
 }
 
 .menu-item-point{
@@ -4835,60 +4970,57 @@ input::placeholder ,textarea::placeholder{
   display: block;
   border-radius: 50%;
   background: #666;
-  
-}
-
-.project-edit-container .menu-item>.icon-container,
-.project-edit-container .group .menu-item>.icon-container {
-  margin-left: 10px;
 }
 
+/* 删除旧的底部分割线样式 */
 .project-edit-container .menu-item::after,
 .project-edit-container .group .menu-item::after {
-  content: "";
-  width: calc(100% - 10px * 2);
-  height: 0;
-  display: block;
-  position: absolute;
-  bottom: -1px;
-  left: 10px;
-  border-bottom: 1px solid #d8dae3;
+  display: none;
 }
 
-.project-edit-container .menu-item.active,
-.project-edit-container .group .menu-item.active {
-  background: #fff;
+.project-edit-container .group {
+  width: 100%;
 }
 
-.project-edit-container .menu-item.active::after,
-.project-edit-container .group .menu-item.active::after {
-  border-bottom-color: transparent;
+.project-edit-container .group-detail {
+  background: rgba(0, 0, 0, 0.03);
 }
 
-.project-edit-container .group {
-  width: 100%;
+/* v3.0 子菜单样式 by xu 20251216 */
+.project-edit-container .group-detail .menu-item {
+  padding-left: 20px;
 }
 
-/* .project-edit-container  .group-detail {
-  padding-left: 10px;
-} */
+.project-edit-container .left-side.is-expanded .group-detail .menu-item {
+  padding-left: 50px;
+}
 
-.project-edit-container  .group-detail .menu-item {
-  padding-left: 44px;
+/* v3.0 收起时隐藏子菜单 by xu 20251216 */
+.project-edit-container .left-side:not(.is-expanded) .group-detail {
+  display: none;
 }
 
-/* .project-edit-container .group-detail .menu-item::before {
-  content: "";
-  height: 6px;
-  width: 6px;
-  border-radius: 50%;
-  position: absolute;
-  top: 50%;
-  left: 30px;
-  background: #666;
-  transform: translateY(-50%);
-  position: relative;
-} */
+/* v3.0 新增:模式切换按钮,参考GlobalMenu by xu 20251216 */
+.project-edit-container .menu-mode-toggle {
+  min-height: 40px;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 12px 8px;
+  cursor: pointer;
+  border-top: 1px solid #d8dae3;
+  border-right: 2px solid #dadee2;
+  flex-shrink: 0;
+}
+
+.project-edit-container .menu-mode-toggle:hover {
+  background: rgba(0, 0, 0, 0.05);
+}
+
+.project-edit-container .menu-mode-toggle .icon-container {
+  padding: 0 !important;
+}
 
 .sub-tab-menu-footer {
   height: 60px;
@@ -4954,8 +5086,9 @@ input::placeholder ,textarea::placeholder{
   display: block;
 }
 
+/* v3.0 右侧内容区改为flex:1自适应 by xu 20251216 */
 .project-edit-container>div.content-area {
-  width: calc(100% - 180px);
+  flex: 1;
   overflow-y: auto;
 }
 

+ 3 - 3
skin/easy/css/icon-base/iconfont.css

@@ -1,12 +1,12 @@
 @font-face {
-  font-family: "icon-biz";
+  font-family: "icon-base";
   src: url('../../fonts/icon-base/iconfont.woff2') format('woff2'),
        url('../../fonts/icon-base/iconfont.woff') format('woff'),
        url('../../fonts/icon-base/iconfont.ttf') format('truetype');
 }
 
-.iconfont {
-  font-family: "iconfont" !important;
+.icon-base {
+  font-family: "icon-base" !important;
   font-size: 16px;
   font-style: normal;
   -webkit-font-smoothing: antialiased;

+ 3 - 2
skin/easy/css/icon-biz/iconfont.css

@@ -6,8 +6,9 @@
 }
 
 
-.iconfont {
-  font-family: "iconfont" !important;
+
+.icon-biz {
+  font-family: "icon-biz" !important;
   font-size: 16px;
   font-style: normal;
   -webkit-font-smoothing: antialiased;

+ 6 - 6
skin/easy/css/iconfont.css

@@ -14,13 +14,13 @@
   content: "\e6f2";
 }
 
-.icon-qiehuan_xi:before {
+/* .icon-qiehuan_xi:before {
   content: "\e6f1";
-}
+} */
 
-.icon-qiehuan:before {
+/* .icon-qiehuan:before {
   content: "\e6f0";
-}
+} */
 
 .icon-xunchajilu:before {
   content: "\e6ef";
@@ -354,9 +354,9 @@
   content: "\e68f";
 }
 
-.icon-shouye:before {
+/* .icon-shouye:before {
   content: "\e690";
-}
+} */
 
 .icon-xiazai:before {
   content: "\e691";

二進制
skin/easy/fonts/icon-base/iconfont.ttf


二進制
skin/easy/fonts/icon-base/iconfont.woff


二進制
skin/easy/fonts/icon-base/iconfont.woff2


二進制
skin/easy/fonts/icon-biz/iconfont.ttf


二進制
skin/easy/fonts/icon-biz/iconfont.woff


二進制
skin/easy/fonts/icon-biz/iconfont.woff2