339 lines
9.9 KiB
JavaScript
339 lines
9.9 KiB
JavaScript
/*
|
||
Copyright (C) 2025 QuantumNous
|
||
|
||
This program is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU Affero General Public License as
|
||
published by the Free Software Foundation, either version 3 of the
|
||
License, or (at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU Affero General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Affero General Public License
|
||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
||
For commercial licensing, please contact support@quantumnous.com
|
||
*/
|
||
|
||
import { useState, useEffect, useMemo, useContext, useRef } from 'react';
|
||
import { StatusContext } from '../../context/Status';
|
||
import { UserContext } from '../../context/User';
|
||
import { API, mergeSelfResponseIntoLocalUser } from '../../helpers';
|
||
|
||
// 创建一个全局事件系统来同步所有useSidebar实例
|
||
const sidebarEventTarget = new EventTarget();
|
||
const SIDEBAR_REFRESH_EVENT = 'sidebar-refresh';
|
||
|
||
// 默认配置(当没有任何角色配置或用户配置时的兜底)
|
||
export const DEFAULT_SIDEBAR_CONFIG = {
|
||
chat: {
|
||
enabled: true,
|
||
playground: true,
|
||
chat: true,
|
||
},
|
||
console: {
|
||
enabled: true,
|
||
benchmarks: true,
|
||
detail: true,
|
||
token: true,
|
||
log: true,
|
||
midjourney: true,
|
||
task: true,
|
||
'supplierApply': true,
|
||
},
|
||
personal: {
|
||
enabled: true,
|
||
topup: true,
|
||
personal: true,
|
||
supplier: true,
|
||
distributor_center: true,
|
||
'supplier-apply': true,
|
||
'supplier-channel': true,
|
||
'supplier-pricing-settings': true,
|
||
},
|
||
admin: {
|
||
enabled: true,
|
||
channel: true,
|
||
models: true,
|
||
deployment: true,
|
||
'model-heat': true,
|
||
redemption: true,
|
||
user: true,
|
||
'operation-log': true,
|
||
subscription: true,
|
||
setting: true,
|
||
distributor: true,
|
||
'supplier-management': true,
|
||
'supplier-application-approval': true,
|
||
'supplier-dashboard': true,
|
||
},
|
||
};
|
||
|
||
const deepClone = (value) => JSON.parse(JSON.stringify(value));
|
||
|
||
// 将savedConfig合并到DEFAULT_SIDEBAR_CONFIG,确保新增模块有默认值
|
||
export const mergeWithDefaults = (savedConfig) => {
|
||
const merged = deepClone(DEFAULT_SIDEBAR_CONFIG);
|
||
if (!savedConfig || typeof savedConfig !== 'object') return merged;
|
||
|
||
for (const [sectionKey, sectionConfig] of Object.entries(savedConfig)) {
|
||
if (!sectionConfig || typeof sectionConfig !== 'object') continue;
|
||
|
||
if (!merged[sectionKey]) {
|
||
merged[sectionKey] = { ...sectionConfig };
|
||
continue;
|
||
}
|
||
|
||
merged[sectionKey] = { ...merged[sectionKey], ...sectionConfig };
|
||
}
|
||
|
||
return merged;
|
||
};
|
||
|
||
export const useSidebar = () => {
|
||
const [statusState] = useContext(StatusContext);
|
||
const [userState, userDispatch] = useContext(UserContext);
|
||
const [userConfig, setUserConfig] = useState(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const instanceIdRef = useRef(null);
|
||
const hasLoadedOnceRef = useRef(false);
|
||
|
||
if (!instanceIdRef.current) {
|
||
const randomPart = Math.random().toString(16).slice(2);
|
||
instanceIdRef.current = `sidebar-${Date.now()}-${randomPart}`;
|
||
}
|
||
|
||
// 获取角色级模块配置
|
||
const roleModulesConfig = useMemo(() => {
|
||
if (statusState?.status?.SidebarModulesByRole) {
|
||
try {
|
||
return JSON.parse(statusState.status.SidebarModulesByRole);
|
||
} catch (error) {
|
||
return {};
|
||
}
|
||
}
|
||
return {};
|
||
}, [statusState?.status?.SidebarModulesByRole]);
|
||
|
||
// 获取当前用户角色
|
||
const userRole = userState?.user?.role ?? null;
|
||
|
||
// 基于角色获取基础配置(替代原来的adminConfig)
|
||
const roleBaseConfig = useMemo(() => {
|
||
if (userRole != null && roleModulesConfig) {
|
||
const roleStr = String(userRole);
|
||
const config = roleModulesConfig[roleStr];
|
||
if (config && typeof config === 'object') {
|
||
return mergeWithDefaults(config);
|
||
}
|
||
}
|
||
return mergeWithDefaults(null);
|
||
}, [userRole, roleModulesConfig]);
|
||
|
||
// 加载用户配置的通用方法
|
||
const loadUserConfig = async ({ withLoading } = {}) => {
|
||
const shouldShowLoader =
|
||
typeof withLoading === 'boolean'
|
||
? withLoading
|
||
: !hasLoadedOnceRef.current;
|
||
|
||
try {
|
||
if (shouldShowLoader) {
|
||
setLoading(true);
|
||
}
|
||
|
||
const res = await API.get('/api/user/self');
|
||
if (res.data.success && res.data.data) {
|
||
const payload = res.data.data;
|
||
if (payload.id != null) {
|
||
mergeSelfResponseIntoLocalUser(payload, userDispatch);
|
||
}
|
||
|
||
if (payload.sidebar_modules) {
|
||
let config;
|
||
if (typeof payload.sidebar_modules === 'string') {
|
||
config = JSON.parse(payload.sidebar_modules);
|
||
} else {
|
||
config = payload.sidebar_modules;
|
||
}
|
||
setUserConfig(config);
|
||
} else {
|
||
// 当用户没有配置时,基于角色配置生成默认值
|
||
const defaultUserConfig = {};
|
||
Object.keys(roleBaseConfig).forEach((sectionKey) => {
|
||
if (roleBaseConfig[sectionKey]?.enabled) {
|
||
defaultUserConfig[sectionKey] = { enabled: true };
|
||
Object.keys(roleBaseConfig[sectionKey]).forEach((moduleKey) => {
|
||
if (
|
||
moduleKey !== 'enabled' &&
|
||
roleBaseConfig[sectionKey][moduleKey]
|
||
) {
|
||
defaultUserConfig[sectionKey][moduleKey] = true;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
setUserConfig(defaultUserConfig);
|
||
}
|
||
} else {
|
||
const defaultUserConfig = generateDefaultFromRoleConfig(roleBaseConfig);
|
||
setUserConfig(defaultUserConfig);
|
||
}
|
||
} catch (error) {
|
||
const defaultUserConfig = generateDefaultFromRoleConfig(roleBaseConfig);
|
||
setUserConfig(defaultUserConfig);
|
||
} finally {
|
||
if (shouldShowLoader) {
|
||
setLoading(false);
|
||
}
|
||
hasLoadedOnceRef.current = true;
|
||
}
|
||
};
|
||
|
||
// 刷新用户配置的方法(供外部调用)
|
||
const refreshUserConfig = async () => {
|
||
if (Object.keys(roleBaseConfig).length > 0) {
|
||
await loadUserConfig({ withLoading: false });
|
||
}
|
||
|
||
sidebarEventTarget.dispatchEvent(
|
||
new CustomEvent(SIDEBAR_REFRESH_EVENT, {
|
||
detail: { sourceId: instanceIdRef.current, skipLoader: true },
|
||
}),
|
||
);
|
||
};
|
||
|
||
// 加载用户配置
|
||
useEffect(() => {
|
||
if (Object.keys(roleBaseConfig).length > 0) {
|
||
loadUserConfig();
|
||
}
|
||
}, [roleBaseConfig]);
|
||
|
||
// 监听全局刷新事件
|
||
useEffect(() => {
|
||
const handleRefresh = (event) => {
|
||
if (event?.detail?.sourceId === instanceIdRef.current) {
|
||
return;
|
||
}
|
||
|
||
if (Object.keys(roleBaseConfig).length > 0) {
|
||
loadUserConfig({
|
||
withLoading: event?.detail?.skipLoader ? false : undefined,
|
||
});
|
||
}
|
||
};
|
||
|
||
sidebarEventTarget.addEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh);
|
||
|
||
return () => {
|
||
sidebarEventTarget.removeEventListener(
|
||
SIDEBAR_REFRESH_EVENT,
|
||
handleRefresh,
|
||
);
|
||
};
|
||
}, [roleBaseConfig]);
|
||
|
||
// 计算最终的显示配置:角色配置(基础) + 用户个人配置(覆盖)
|
||
const finalConfig = useMemo(() => {
|
||
const result = {};
|
||
|
||
if (!roleBaseConfig || Object.keys(roleBaseConfig).length === 0) {
|
||
return result;
|
||
}
|
||
|
||
if (!userConfig) {
|
||
return result;
|
||
}
|
||
|
||
// 遍历所有区域
|
||
Object.keys(roleBaseConfig).forEach((sectionKey) => {
|
||
const roleSection = roleBaseConfig[sectionKey];
|
||
const userSection = userConfig[sectionKey];
|
||
|
||
// 如果角色配置禁用了整个区域,则该区域不显示
|
||
if (!roleSection?.enabled) {
|
||
result[sectionKey] = { enabled: false };
|
||
return;
|
||
}
|
||
|
||
// 区域级别:用户可以选择隐藏角色允许的区域
|
||
const sectionEnabled = userSection ? userSection.enabled !== false : true;
|
||
result[sectionKey] = { enabled: sectionEnabled };
|
||
|
||
// 功能级别:只有角色和用户都允许的功能才显示
|
||
Object.keys(roleSection).forEach((moduleKey) => {
|
||
if (moduleKey === 'enabled') return;
|
||
|
||
const roleAllowed = roleSection[moduleKey];
|
||
const userAllowed = userSection
|
||
? userSection[moduleKey] !== false
|
||
: true;
|
||
|
||
result[sectionKey][moduleKey] =
|
||
roleAllowed && userAllowed && sectionEnabled;
|
||
});
|
||
});
|
||
|
||
return result;
|
||
}, [roleBaseConfig, userConfig]);
|
||
|
||
// 检查特定功能是否应该显示
|
||
const isModuleVisible = (sectionKey, moduleKey = null) => {
|
||
if (moduleKey) {
|
||
return finalConfig[sectionKey]?.[moduleKey] === true;
|
||
} else {
|
||
return finalConfig[sectionKey]?.enabled === true;
|
||
}
|
||
};
|
||
|
||
// 检查区域是否有任何可见的功能
|
||
const hasSectionVisibleModules = (sectionKey) => {
|
||
const section = finalConfig[sectionKey];
|
||
if (!section?.enabled) return false;
|
||
|
||
return Object.keys(section).some(
|
||
(key) => key !== 'enabled' && section[key] === true,
|
||
);
|
||
};
|
||
|
||
// 获取区域的可见功能列表
|
||
const getVisibleModules = (sectionKey) => {
|
||
const section = finalConfig[sectionKey];
|
||
if (!section?.enabled) return [];
|
||
|
||
return Object.keys(section).filter(
|
||
(key) => key !== 'enabled' && section[key] === true,
|
||
);
|
||
};
|
||
|
||
return {
|
||
loading,
|
||
roleBaseConfig,
|
||
userConfig,
|
||
finalConfig,
|
||
isModuleVisible,
|
||
hasSectionVisibleModules,
|
||
getVisibleModules,
|
||
refreshUserConfig,
|
||
};
|
||
};
|
||
|
||
// 辅助函数:基于角色配置生成默认用户配置
|
||
function generateDefaultFromRoleConfig(roleConfig) {
|
||
const defaultUserConfig = {};
|
||
Object.keys(roleConfig).forEach((sectionKey) => {
|
||
if (roleConfig[sectionKey]?.enabled) {
|
||
defaultUserConfig[sectionKey] = { enabled: true };
|
||
Object.keys(roleConfig[sectionKey]).forEach((moduleKey) => {
|
||
if (moduleKey !== 'enabled' && roleConfig[sectionKey][moduleKey]) {
|
||
defaultUserConfig[sectionKey][moduleKey] = true;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
return defaultUserConfig;
|
||
}
|