2025-08-31 07:07:40 +08:00
|
|
|
|
/*
|
|
|
|
|
|
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 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
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2025-09-29 22:16:25 +08:00
|
|
|
|
import { useState, useEffect, useMemo, useContext, useRef } from 'react';
|
2025-08-31 07:07:40 +08:00
|
|
|
|
import { StatusContext } from '../../context/Status';
|
|
|
|
|
|
import { API } from '../../helpers';
|
|
|
|
|
|
|
2025-09-01 09:52:52 +08:00
|
|
|
|
// 创建一个全局事件系统来同步所有useSidebar实例
|
|
|
|
|
|
const sidebarEventTarget = new EventTarget();
|
|
|
|
|
|
const SIDEBAR_REFRESH_EVENT = 'sidebar-refresh';
|
|
|
|
|
|
|
2025-08-31 07:07:40 +08:00
|
|
|
|
export const useSidebar = () => {
|
|
|
|
|
|
const [statusState] = useContext(StatusContext);
|
|
|
|
|
|
const [userConfig, setUserConfig] = useState(null);
|
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
2025-09-29 22:16:25 +08:00
|
|
|
|
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}`;
|
|
|
|
|
|
}
|
2025-08-31 07:07:40 +08:00
|
|
|
|
|
|
|
|
|
|
// 默认配置
|
|
|
|
|
|
const defaultAdminConfig = {
|
|
|
|
|
|
chat: {
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
playground: true,
|
2025-09-01 23:43:39 +08:00
|
|
|
|
chat: true,
|
2025-08-31 07:07:40 +08:00
|
|
|
|
},
|
|
|
|
|
|
console: {
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
detail: true,
|
|
|
|
|
|
token: true,
|
|
|
|
|
|
log: true,
|
|
|
|
|
|
midjourney: true,
|
2025-09-01 23:43:39 +08:00
|
|
|
|
task: true,
|
2025-08-31 07:07:40 +08:00
|
|
|
|
},
|
|
|
|
|
|
personal: {
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
topup: true,
|
2025-09-01 23:43:39 +08:00
|
|
|
|
personal: true,
|
2025-08-31 07:07:40 +08:00
|
|
|
|
},
|
|
|
|
|
|
admin: {
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
channel: true,
|
|
|
|
|
|
models: true,
|
|
|
|
|
|
redemption: true,
|
|
|
|
|
|
user: true,
|
2025-09-01 23:43:39 +08:00
|
|
|
|
setting: true,
|
|
|
|
|
|
},
|
2025-08-31 07:07:40 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取管理员配置
|
|
|
|
|
|
const adminConfig = useMemo(() => {
|
|
|
|
|
|
if (statusState?.status?.SidebarModulesAdmin) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const config = JSON.parse(statusState.status.SidebarModulesAdmin);
|
|
|
|
|
|
return config;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return defaultAdminConfig;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return defaultAdminConfig;
|
|
|
|
|
|
}, [statusState?.status?.SidebarModulesAdmin]);
|
|
|
|
|
|
|
|
|
|
|
|
// 加载用户配置的通用方法
|
2025-09-29 22:16:25 +08:00
|
|
|
|
const loadUserConfig = async ({ withLoading } = {}) => {
|
|
|
|
|
|
const shouldShowLoader =
|
|
|
|
|
|
typeof withLoading === 'boolean'
|
|
|
|
|
|
? withLoading
|
|
|
|
|
|
: !hasLoadedOnceRef.current;
|
|
|
|
|
|
|
2025-08-31 07:07:40 +08:00
|
|
|
|
try {
|
2025-09-29 22:16:25 +08:00
|
|
|
|
if (shouldShowLoader) {
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-31 07:07:40 +08:00
|
|
|
|
const res = await API.get('/api/user/self');
|
|
|
|
|
|
if (res.data.success && res.data.data.sidebar_modules) {
|
|
|
|
|
|
let config;
|
|
|
|
|
|
// 检查sidebar_modules是字符串还是对象
|
|
|
|
|
|
if (typeof res.data.data.sidebar_modules === 'string') {
|
|
|
|
|
|
config = JSON.parse(res.data.data.sidebar_modules);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
config = res.data.data.sidebar_modules;
|
|
|
|
|
|
}
|
|
|
|
|
|
setUserConfig(config);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 当用户没有配置时,生成一个基于管理员配置的默认用户配置
|
|
|
|
|
|
// 这样可以确保权限控制正确生效
|
|
|
|
|
|
const defaultUserConfig = {};
|
2025-09-01 23:43:39 +08:00
|
|
|
|
Object.keys(adminConfig).forEach((sectionKey) => {
|
2025-08-31 07:07:40 +08:00
|
|
|
|
if (adminConfig[sectionKey]?.enabled) {
|
|
|
|
|
|
defaultUserConfig[sectionKey] = { enabled: true };
|
|
|
|
|
|
// 为每个管理员允许的模块设置默认值为true
|
2025-09-01 23:43:39 +08:00
|
|
|
|
Object.keys(adminConfig[sectionKey]).forEach((moduleKey) => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
moduleKey !== 'enabled' &&
|
|
|
|
|
|
adminConfig[sectionKey][moduleKey]
|
|
|
|
|
|
) {
|
2025-08-31 07:07:40 +08:00
|
|
|
|
defaultUserConfig[sectionKey][moduleKey] = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
setUserConfig(defaultUserConfig);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
// 出错时也生成默认配置,而不是设置为空对象
|
|
|
|
|
|
const defaultUserConfig = {};
|
2025-09-01 23:43:39 +08:00
|
|
|
|
Object.keys(adminConfig).forEach((sectionKey) => {
|
2025-08-31 07:07:40 +08:00
|
|
|
|
if (adminConfig[sectionKey]?.enabled) {
|
|
|
|
|
|
defaultUserConfig[sectionKey] = { enabled: true };
|
2025-09-01 23:43:39 +08:00
|
|
|
|
Object.keys(adminConfig[sectionKey]).forEach((moduleKey) => {
|
2025-08-31 07:07:40 +08:00
|
|
|
|
if (moduleKey !== 'enabled' && adminConfig[sectionKey][moduleKey]) {
|
|
|
|
|
|
defaultUserConfig[sectionKey][moduleKey] = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
setUserConfig(defaultUserConfig);
|
|
|
|
|
|
} finally {
|
2025-09-29 22:16:25 +08:00
|
|
|
|
if (shouldShowLoader) {
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
hasLoadedOnceRef.current = true;
|
2025-08-31 07:07:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 刷新用户配置的方法(供外部调用)
|
|
|
|
|
|
const refreshUserConfig = async () => {
|
2025-09-29 22:16:25 +08:00
|
|
|
|
if (Object.keys(adminConfig).length > 0) {
|
|
|
|
|
|
await loadUserConfig({ withLoading: false });
|
2025-09-01 10:20:15 +08:00
|
|
|
|
}
|
2025-09-01 09:52:52 +08:00
|
|
|
|
|
|
|
|
|
|
// 触发全局刷新事件,通知所有useSidebar实例更新
|
2025-09-29 22:16:25 +08:00
|
|
|
|
sidebarEventTarget.dispatchEvent(
|
|
|
|
|
|
new CustomEvent(SIDEBAR_REFRESH_EVENT, {
|
|
|
|
|
|
detail: { sourceId: instanceIdRef.current, skipLoader: true },
|
|
|
|
|
|
}),
|
|
|
|
|
|
);
|
2025-08-31 07:07:40 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 加载用户配置
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
// 只有当管理员配置加载完成后才加载用户配置
|
|
|
|
|
|
if (Object.keys(adminConfig).length > 0) {
|
|
|
|
|
|
loadUserConfig();
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [adminConfig]);
|
|
|
|
|
|
|
2025-09-01 09:52:52 +08:00
|
|
|
|
// 监听全局刷新事件
|
|
|
|
|
|
useEffect(() => {
|
2025-09-29 22:16:25 +08:00
|
|
|
|
const handleRefresh = (event) => {
|
|
|
|
|
|
if (event?.detail?.sourceId === instanceIdRef.current) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-01 09:52:52 +08:00
|
|
|
|
if (Object.keys(adminConfig).length > 0) {
|
2025-09-29 22:16:25 +08:00
|
|
|
|
loadUserConfig({
|
|
|
|
|
|
withLoading: event?.detail?.skipLoader ? false : undefined,
|
|
|
|
|
|
});
|
2025-09-01 09:52:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
sidebarEventTarget.addEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh);
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
2025-09-27 18:47:53 +08:00
|
|
|
|
sidebarEventTarget.removeEventListener(
|
|
|
|
|
|
SIDEBAR_REFRESH_EVENT,
|
|
|
|
|
|
handleRefresh,
|
|
|
|
|
|
);
|
2025-09-01 09:52:52 +08:00
|
|
|
|
};
|
|
|
|
|
|
}, [adminConfig]);
|
|
|
|
|
|
|
2025-08-31 07:07:40 +08:00
|
|
|
|
// 计算最终的显示配置
|
|
|
|
|
|
const finalConfig = useMemo(() => {
|
|
|
|
|
|
const result = {};
|
|
|
|
|
|
|
|
|
|
|
|
// 确保adminConfig已加载
|
|
|
|
|
|
if (!adminConfig || Object.keys(adminConfig).length === 0) {
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果userConfig未加载,等待加载完成
|
|
|
|
|
|
if (!userConfig) {
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历所有区域
|
2025-09-01 23:43:39 +08:00
|
|
|
|
Object.keys(adminConfig).forEach((sectionKey) => {
|
2025-08-31 07:07:40 +08:00
|
|
|
|
const adminSection = adminConfig[sectionKey];
|
|
|
|
|
|
const userSection = userConfig[sectionKey];
|
|
|
|
|
|
|
|
|
|
|
|
// 如果管理员禁用了整个区域,则该区域不显示
|
|
|
|
|
|
if (!adminSection?.enabled) {
|
|
|
|
|
|
result[sectionKey] = { enabled: false };
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 区域级别:用户可以选择隐藏管理员允许的区域
|
|
|
|
|
|
// 当userSection存在时检查enabled状态,否则默认为true
|
2025-09-01 23:43:39 +08:00
|
|
|
|
const sectionEnabled = userSection ? userSection.enabled !== false : true;
|
2025-08-31 07:07:40 +08:00
|
|
|
|
result[sectionKey] = { enabled: sectionEnabled };
|
|
|
|
|
|
|
|
|
|
|
|
// 功能级别:只有管理员和用户都允许的功能才显示
|
2025-09-01 23:43:39 +08:00
|
|
|
|
Object.keys(adminSection).forEach((moduleKey) => {
|
2025-08-31 07:07:40 +08:00
|
|
|
|
if (moduleKey === 'enabled') return;
|
|
|
|
|
|
|
|
|
|
|
|
const adminAllowed = adminSection[moduleKey];
|
|
|
|
|
|
// 当userSection存在时检查模块状态,否则默认为true
|
2025-09-01 23:43:39 +08:00
|
|
|
|
const userAllowed = userSection
|
|
|
|
|
|
? userSection[moduleKey] !== false
|
|
|
|
|
|
: true;
|
2025-08-31 07:07:40 +08:00
|
|
|
|
|
2025-09-01 23:43:39 +08:00
|
|
|
|
result[sectionKey][moduleKey] =
|
|
|
|
|
|
adminAllowed && userAllowed && sectionEnabled;
|
2025-08-31 07:07:40 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}, [adminConfig, 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;
|
2025-09-01 23:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
return Object.keys(section).some(
|
|
|
|
|
|
(key) => key !== 'enabled' && section[key] === true,
|
2025-08-31 07:07:40 +08:00
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取区域的可见功能列表
|
|
|
|
|
|
const getVisibleModules = (sectionKey) => {
|
|
|
|
|
|
const section = finalConfig[sectionKey];
|
|
|
|
|
|
if (!section?.enabled) return [];
|
2025-09-01 23:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
return Object.keys(section).filter(
|
|
|
|
|
|
(key) => key !== 'enabled' && section[key] === true,
|
|
|
|
|
|
);
|
2025-08-31 07:07:40 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
loading,
|
|
|
|
|
|
adminConfig,
|
|
|
|
|
|
userConfig,
|
|
|
|
|
|
finalConfig,
|
|
|
|
|
|
isModuleVisible,
|
|
|
|
|
|
hasSectionVisibleModules,
|
|
|
|
|
|
getVisibleModules,
|
2025-09-01 23:43:39 +08:00
|
|
|
|
refreshUserConfig,
|
2025-08-31 07:07:40 +08:00
|
|
|
|
};
|
|
|
|
|
|
};
|