huangxw 5 miesięcy temu
rodzic
commit
dcbdaa9f81

+ 5 - 3
src/layout/components/Sidebar/SidebarItem.vue

@@ -5,7 +5,9 @@
                 <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
                     <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
                     <template #title>
-                        <span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span>
+                        <el-badge :value="menuCounts[resolvePath(onlyOneChild.path)]" :offset="[10, 10]" :show-zero="false">
+                            <span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span>
+                        </el-badge>
                     </template>
                 </el-menu-item>
             </app-link>
@@ -16,7 +18,6 @@
                 <svg-icon :icon-class="item.meta ? item.meta.icon : ''" />
                 <span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span>
             </template>
-
             <sidebar-item v-for="(child, index) in item.children" :key="child.path + index" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" />
         </el-sub-menu>
     </div>
@@ -27,7 +28,8 @@ import { isExternal } from '@/utils/validate';
 import AppLink from './Link.vue';
 import { getNormalPath } from '@/utils/ruoyi';
 import { RouteRecordRaw } from 'vue-router';
-
+import { useSideNumStore } from '@/store/modules/sideNum';
+const { menuCounts } = toRefs(useSideNumStore());
 const props = defineProps({
   item: {
     type: Object as PropType<RouteRecordRaw>,

+ 16 - 0
src/layout/components/Sidebar/index.vue

@@ -19,6 +19,7 @@ import useAppStore from '@/store/modules/app';
 import useSettingsStore from '@/store/modules/settings';
 import usePermissionStore from '@/store/modules/permission';
 import { RouteRecordRaw } from 'vue-router';
+import { useSideNumStore } from '@/store/modules/sideNum';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
@@ -51,4 +52,19 @@ const activeMenu = computed(() => {
 // });
 const bgColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground));
 const textColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor));
+onMounted(() => {
+    // 设置侧边栏数字配置
+    useSideNumStore().setMultipleApiConfigs({
+        '/appointment-record/experience': {
+            apiUrl: '/dgtmedicine/bookingInfo/getUnconfirmedCount',
+        },
+        '/appointment-record/appointment-record': {
+            isParent: true,
+            children: ['/appointment-record/experience']
+        },
+    });
+    // 刷新侧边栏数字
+    useSideNumStore().refreshAllCounts();
+    console.log(useSideNumStore().menuCounts);
+});
 </script>

+ 190 - 0
src/store/modules/sideNum.ts

@@ -0,0 +1,190 @@
+import { defineStore } from 'pinia';
+import { ref, computed } from 'vue';
+import request from '@/utils/request';
+
+export interface MenuApiConfig {
+    // 接口URL
+    apiUrl?: string;
+    // 请求方法
+    method?: 'GET' | 'POST';
+    // 请求参数
+    params?: Record<string, any>;
+    // 数据提取路径,从接口响应中提取数量的路径
+    dataPath?: string;
+    // 是否为父级菜单(数字由子菜单汇总而来,不调用接口)
+    isParent?: boolean;
+    // 子菜单路径列表(仅当isParent为true时有效)
+    children?: string[];
+}
+
+export const useSideNumStore = defineStore('sideNum', () => {
+    // 存储每个菜单路径对应的气泡数量
+    const menuCounts = ref<Record<string, number>>({});
+    // 存储每个菜单路径对应的接口配置
+    const menuApiConfigs = ref<Record<string, MenuApiConfig>>({});
+
+    // 获取指定菜单路径的气泡数量
+    const getMenuCount = (menuPath: string): number => {
+        const config = menuApiConfigs.value[menuPath];
+        // 如果是父级菜单,计算子菜单数量总和
+        if (config && config.isParent && config.children) {
+            return config.children.reduce((total, childPath) => {
+                return total + (menuCounts.value[childPath] || 0);
+            }, 0);
+        }
+        // 普通菜单直接返回存储的数量
+        return menuCounts.value[menuPath] || 0;
+    };
+
+    // 获取所有有数量的菜单项
+    const getActiveMenus = computed(() => {
+        const activeMenus: Record<string, number> = {};
+        Object.keys(menuApiConfigs.value).forEach((key) => {
+            const count = getMenuCount(key); // 使用getMenuCount方法,支持父级菜单计算
+            if (count > 0) {
+                activeMenus[key] = count;
+            }
+        });
+        return activeMenus;
+    });
+
+    // 获取总的未处理数量
+    const getTotalCount = computed(() => {
+        // 只计算非父级菜单的数量,避免重复计算
+        return Object.keys(menuApiConfigs.value).reduce((total, menuPath) => {
+            const config = menuApiConfigs.value[menuPath];
+            if (!config.isParent) {
+                return total + (menuCounts.value[menuPath] || 0);
+            }
+            return total;
+        }, 0);
+    });
+    // 设置菜单接口配置
+    const setMenuApiConfig = (menuPath: string, config: MenuApiConfig) => {
+        menuApiConfigs.value[menuPath] = {
+            method: 'GET',
+            dataPath: 'data.count', // 默认数据路径
+            isParent: false, // 默认不是父级菜单
+            ...config
+        };
+    };
+
+    // 设置父级菜单配置(数字由子菜单汇总)
+    const setParentMenuConfig = (menuPath: string, children: string[]) => {
+        menuApiConfigs.value[menuPath] = {
+            apiUrl: '', // 父级菜单不需要接口URL
+            isParent: true,
+            children: children
+        };
+    };
+
+    // 批量设置多个菜单的接口配置
+    const setMultipleApiConfigs = (configs: Record<string, MenuApiConfig>) => {
+        Object.keys(configs).forEach((menuPath) => {
+            setMenuApiConfig(menuPath, configs[menuPath]);
+        });
+    };
+
+    // 更新指定菜单的气泡数量
+    const updateMenuCount = (menuPath: string, count: number) => {
+        menuCounts.value[menuPath] = Math.max(0, count); // 确保数量不为负数
+    };
+
+    // 清空指定菜单的气泡数量
+    const clearMenuCount = (menuPath: string) => {
+        menuCounts.value[menuPath] = 0;
+    };
+
+    // 清空所有菜单的气泡数量
+    const clearAllCounts = () => {
+        menuCounts.value = {};
+    };
+
+    // 从接口响应中提取数量
+    const extractCountFromResponse = (response: any): number => {
+        try {
+            if (response.code === 200) {
+                return response.data;
+            }
+        } catch {
+            return 0;
+        }
+    };
+
+    // 获取指定菜单的数量(调用接口)
+    const fetchMenuCount = async (menuPath: string): Promise<number> => {
+        const config = menuApiConfigs.value[menuPath];
+        if (!config) {
+            console.warn(`菜单路径 ${menuPath} 没有配置接口信息`);
+            return 0;
+        }
+        console.log(menuPath, '========');
+        // 如果是父级菜单,不调用接口,直接返回子菜单数量总和
+        if (config.isParent) {
+            return getMenuCount(menuPath);
+        }
+
+        try {
+            const response = await makeApiRequest(config);
+            const count = extractCountFromResponse(response);
+            updateMenuCount(menuPath, count);
+            return count;
+        } catch (error) {
+            console.error(`获取菜单 ${menuPath} 数量失败:`, error);
+            return 0;
+        }
+    };
+
+    // 发起API请求
+    const makeApiRequest = async (config: MenuApiConfig): Promise<any> => {
+        const { apiUrl, method = 'GET', params } = config;
+
+        if (method === 'GET') {
+            return await request.get(apiUrl, { params });
+        } else {
+            return await request.post(apiUrl, params);
+        }
+    };
+
+    // 刷新指定菜单的数量(立即调用接口)
+    const refreshMenuCount = async (menuPath: string): Promise<number> => {
+        return fetchMenuCount(menuPath);
+    };
+
+    // 刷新所有菜单的数量
+    const refreshAllCounts = async (): Promise<void> => {
+        // 只刷新非父级菜单的数量
+        const nonParentMenus = Object.keys(menuApiConfigs.value).filter((menuPath) => !menuApiConfigs.value[menuPath].isParent);
+        const promises = nonParentMenus.map((menuPath) => fetchMenuCount(menuPath));
+        await Promise.allSettled(promises);
+    };
+
+    // 重置Store状态
+    const resetStore = () => {
+        menuCounts.value = {};
+        menuApiConfigs.value = {};
+    };
+
+    return {
+        // 状态
+        menuCounts,
+        menuApiConfigs,
+        // 计算属性
+        getActiveMenus,
+        getTotalCount,
+        // 方法
+        getMenuCount,
+        setMenuApiConfig,
+        setParentMenuConfig,
+        setMultipleApiConfigs,
+        updateMenuCount,
+        clearMenuCount,
+        clearAllCounts,
+        extractCountFromResponse,
+        fetchMenuCount,
+        makeApiRequest,
+        refreshMenuCount,
+        refreshAllCounts,
+        resetStore
+    };
+});