zlwu 1 год назад
Родитель
Сommit
b35b31a01e

+ 5 - 0
backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java

@@ -59,6 +59,11 @@ public class RouterVo {
      */
     private String menuType;
 
+    /**
+     * 是否为外链(0是 1否)
+     */
+    private String isFrame;
+
     /**
      * 子路由
      */

+ 1 - 0
backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java

@@ -204,6 +204,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
             router.setComponent(menu.getComponentInfo());
             router.setQuery(menu.getQueryParam());
             router.setMenuType(menu.getMenuType());
+            router.setIsFrame(menu.getIsFrame());
             router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()),
                     menu.getPath()));
             List<SysMenu> cMenus = menu.getChildren();

+ 55 - 7
web/src/layout/components/TopMenu.vue

@@ -9,9 +9,17 @@
       active-text-color="#ffffff"
       @select="handleSelect"
     >
-      <el-menu-item v-for="(item, index) in topMenus" :key="index" :index="item.path">
-        <svg-icon v-if="item.meta && item.meta.icon" :icon-class="item.meta.icon" />
-        <span>{{ item.meta?.title || item.name }}</span>
+      <el-menu-item v-for="(item, index) in topMenus" :key="index" :index="isExternalLink(item) ? `external_${index}` : item.path">
+        <template v-if="isExternalLink(item)">
+          <a :href="getExternalUrl(item)" target="_blank" rel="noopener noreferrer" class="external-link" @click.stop>
+            <svg-icon v-if="item.meta && item.meta.icon" :icon-class="item.meta.icon" />
+            <span>{{ item.meta?.title || item.name }}</span>
+          </a>
+        </template>
+        <template v-else>
+          <svg-icon v-if="item.meta && item.meta.icon" :icon-class="item.meta.icon" />
+          <span>{{ item.meta?.title || item.name }}</span>
+        </template>
       </el-menu-item>
     </el-menu>
   </div>
@@ -63,21 +71,43 @@ const setTopMenus = () => {
   console.log('TopMenu - 顶部菜单列表:', topMenus.value);
 };
 
+// 判断是否为外链
+const isExternalLink = (item: any): boolean => {
+  // isFrame 为 "0" 表示是外链
+  return item.isFrame === '0';
+};
+
+// 获取外链完整URL
+const getExternalUrl = (item: any): string => {
+  // 对于 isFrame='0' 的外链菜单,path 字段就是完整的外链地址
+  // 对于内链(isFrame='1' 且 path 是 http 开头),地址存储在 meta.link 中
+  const url = item.path || item.meta?.link || '';
+  console.log('TopMenu - 外链地址:', url, '(isFrame:', item.isFrame, ')');
+  return url;
+};
+
 // 菜单选择事件
 const handleSelect = (index: string) => {
   console.log('TopMenu - 选择菜单:', index);
 
-  // 清理双斜杠
-  const cleanIndex = index.replace(/\/+/g, '/');
-  console.log('TopMenu - 清理后路径:', cleanIndex);
+  // 如果是外链菜单的 index(格式:external_数字),直接返回
+  if (index.startsWith('external_')) {
+    console.log('TopMenu - 外链菜单,由 <a> 标签处理打开');
+    return;
+  }
 
-  // 查找对应的菜单项(支持双斜杠匹配)
+  // 查找对应的菜单项
   const menuItem = topMenus.value.find((item) => {
     const cleanPath = item.path.replace(/\/+/g, '/');
+    const cleanIndex = index.replace(/\/+/g, '/');
     return cleanPath === cleanIndex || item.path === index;
   });
   console.log('TopMenu - 找到的菜单项:', menuItem);
 
+  // 清理双斜杠
+  const cleanIndex = index.replace(/\/+/g, '/');
+  console.log('TopMenu - 清理后路径:', cleanIndex);
+
   if (menuItem) {
     // 判断是否有子菜单(过滤掉隐藏的和按钮类型的)
     const hasChildren = menuItem.children && menuItem.children.length > 0;
@@ -182,6 +212,24 @@ watch(
     span {
       vertical-align: middle;
     }
+
+    .external-link {
+      display: flex;
+      align-items: center;
+      color: inherit;
+      text-decoration: none;
+      width: 100%;
+      height: 100%;
+
+      &:hover {
+        color: #ffffff;
+      }
+
+      .svg-icon {
+        margin-right: 8px;
+        font-size: 16px;
+      }
+    }
   }
 }
 </style>

+ 1 - 0
web/src/types/router.d.ts

@@ -17,6 +17,7 @@ declare module 'vue-router' {
     alwaysShow?: boolean;
     query?: string;
     parentPath?: string;
+    isFrame?: string;
   }
 
   interface _RouteLocationBase {

+ 5 - 2
web/src/views/crm/pool/index.vue

@@ -81,8 +81,9 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              link
-              type="primary"
+              type="info"
+              plain
+              icon="View"
               :disabled="single"
               @click="() => handleView(customerList[customerList.findIndex((item) => item.id === ids[0])])"
               >查看</el-button
@@ -177,6 +178,7 @@ const customerList = ref<CustomerVO[]>([]);
 const loading = ref(true);
 const total = ref(0);
 const showSearch = ref(false);
+const single = ref(true);
 const multiple = ref(true);
 const ids = ref<number[]>([]);
 
@@ -247,6 +249,7 @@ const handleQuickSearch = () => {
 /** 多选框选中数据 */
 const handleSelectionChange = (selection: CustomerVO[]) => {
   ids.value = selection.map((item) => item.id);
+  single.value = selection.length !== 1;
   multiple.value = !selection.length;
 };
 

+ 3 - 3
web/src/views/crm/product/category/index.vue

@@ -51,17 +51,17 @@
             <el-button type="info" plain icon="Sort" @click="toggleExpandAll">展开/折叠</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button link type="success" :disabled="single" @click="handleUpdate(categoryList.find((item) => item.id === selectedId))"
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate(categoryList.find((item) => item.id === selectedId))"
               >修改</el-button
             >
           </el-col>
           <el-col :span="1.5">
-            <el-button link type="primary" :disabled="single" @click="handleAdd(categoryList.find((item) => item.id === selectedId))"
+            <el-button type="primary" plain icon="Plus" :disabled="single" @click="handleAdd(categoryList.find((item) => item.id === selectedId))"
               >新增子分类</el-button
             >
           </el-col>
           <el-col :span="1.5">
-            <el-button link type="danger" :disabled="single" @click="handleDelete(categoryList.find((item) => item.id === selectedId))"
+            <el-button type="danger" plain icon="Delete" :disabled="single" @click="handleDelete(categoryList.find((item) => item.id === selectedId))"
               >删除</el-button
             >
           </el-col>