useSidebar.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import { useState, useEffect, useMemo, useContext } from 'react';
  16. import { StatusContext } from '../../context/Status';
  17. import { API } from '../../helpers';
  18. // 创建一个全局事件系统来同步所有useSidebar实例
  19. const sidebarEventTarget = new EventTarget();
  20. const SIDEBAR_REFRESH_EVENT = 'sidebar-refresh';
  21. export const useSidebar = () => {
  22. const [statusState] = useContext(StatusContext);
  23. const [userConfig, setUserConfig] = useState(null);
  24. const [loading, setLoading] = useState(true);
  25. // 默认配置
  26. const defaultAdminConfig = {
  27. chat: {
  28. enabled: true,
  29. playground: true,
  30. chat: true,
  31. },
  32. console: {
  33. enabled: true,
  34. detail: true,
  35. token: true,
  36. log: true,
  37. midjourney: true,
  38. task: true,
  39. },
  40. personal: {
  41. enabled: true,
  42. topup: true,
  43. personal: true,
  44. },
  45. admin: {
  46. enabled: true,
  47. channel: true,
  48. models: true,
  49. redemption: true,
  50. user: true,
  51. setting: true,
  52. },
  53. };
  54. // 获取管理员配置
  55. const adminConfig = useMemo(() => {
  56. if (statusState?.status?.SidebarModulesAdmin) {
  57. try {
  58. const config = JSON.parse(statusState.status.SidebarModulesAdmin);
  59. return config;
  60. } catch (error) {
  61. return defaultAdminConfig;
  62. }
  63. }
  64. return defaultAdminConfig;
  65. }, [statusState?.status?.SidebarModulesAdmin]);
  66. // 加载用户配置的通用方法
  67. const loadUserConfig = async () => {
  68. try {
  69. setLoading(true);
  70. const res = await API.get('/api/user/self');
  71. if (res.data.success && res.data.data.sidebar_modules) {
  72. let config;
  73. // 检查sidebar_modules是字符串还是对象
  74. if (typeof res.data.data.sidebar_modules === 'string') {
  75. config = JSON.parse(res.data.data.sidebar_modules);
  76. } else {
  77. config = res.data.data.sidebar_modules;
  78. }
  79. setUserConfig(config);
  80. } else {
  81. // 当用户没有配置时,生成一个基于管理员配置的默认用户配置
  82. // 这样可以确保权限控制正确生效
  83. const defaultUserConfig = {};
  84. Object.keys(adminConfig).forEach((sectionKey) => {
  85. if (adminConfig[sectionKey]?.enabled) {
  86. defaultUserConfig[sectionKey] = { enabled: true };
  87. // 为每个管理员允许的模块设置默认值为true
  88. Object.keys(adminConfig[sectionKey]).forEach((moduleKey) => {
  89. if (
  90. moduleKey !== 'enabled' &&
  91. adminConfig[sectionKey][moduleKey]
  92. ) {
  93. defaultUserConfig[sectionKey][moduleKey] = true;
  94. }
  95. });
  96. }
  97. });
  98. setUserConfig(defaultUserConfig);
  99. }
  100. } catch (error) {
  101. // 出错时也生成默认配置,而不是设置为空对象
  102. const defaultUserConfig = {};
  103. Object.keys(adminConfig).forEach((sectionKey) => {
  104. if (adminConfig[sectionKey]?.enabled) {
  105. defaultUserConfig[sectionKey] = { enabled: true };
  106. Object.keys(adminConfig[sectionKey]).forEach((moduleKey) => {
  107. if (moduleKey !== 'enabled' && adminConfig[sectionKey][moduleKey]) {
  108. defaultUserConfig[sectionKey][moduleKey] = true;
  109. }
  110. });
  111. }
  112. });
  113. setUserConfig(defaultUserConfig);
  114. } finally {
  115. setLoading(false);
  116. }
  117. };
  118. // 刷新用户配置的方法(供外部调用)
  119. const refreshUserConfig = async () => {
  120. // 移除adminConfig的条件限制,直接刷新用户配置
  121. await loadUserConfig();
  122. // 触发全局刷新事件,通知所有useSidebar实例更新
  123. sidebarEventTarget.dispatchEvent(new CustomEvent(SIDEBAR_REFRESH_EVENT));
  124. };
  125. // 加载用户配置
  126. useEffect(() => {
  127. // 只有当管理员配置加载完成后才加载用户配置
  128. if (Object.keys(adminConfig).length > 0) {
  129. loadUserConfig();
  130. }
  131. }, [adminConfig]);
  132. // 监听全局刷新事件
  133. useEffect(() => {
  134. const handleRefresh = () => {
  135. if (Object.keys(adminConfig).length > 0) {
  136. loadUserConfig();
  137. }
  138. };
  139. sidebarEventTarget.addEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh);
  140. return () => {
  141. sidebarEventTarget.removeEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh);
  142. };
  143. }, [adminConfig]);
  144. // 计算最终的显示配置
  145. const finalConfig = useMemo(() => {
  146. const result = {};
  147. // 确保adminConfig已加载
  148. if (!adminConfig || Object.keys(adminConfig).length === 0) {
  149. return result;
  150. }
  151. // 如果userConfig未加载,等待加载完成
  152. if (!userConfig) {
  153. return result;
  154. }
  155. // 遍历所有区域
  156. Object.keys(adminConfig).forEach((sectionKey) => {
  157. const adminSection = adminConfig[sectionKey];
  158. const userSection = userConfig[sectionKey];
  159. // 如果管理员禁用了整个区域,则该区域不显示
  160. if (!adminSection?.enabled) {
  161. result[sectionKey] = { enabled: false };
  162. return;
  163. }
  164. // 区域级别:用户可以选择隐藏管理员允许的区域
  165. // 当userSection存在时检查enabled状态,否则默认为true
  166. const sectionEnabled = userSection ? userSection.enabled !== false : true;
  167. result[sectionKey] = { enabled: sectionEnabled };
  168. // 功能级别:只有管理员和用户都允许的功能才显示
  169. Object.keys(adminSection).forEach((moduleKey) => {
  170. if (moduleKey === 'enabled') return;
  171. const adminAllowed = adminSection[moduleKey];
  172. // 当userSection存在时检查模块状态,否则默认为true
  173. const userAllowed = userSection
  174. ? userSection[moduleKey] !== false
  175. : true;
  176. result[sectionKey][moduleKey] =
  177. adminAllowed && userAllowed && sectionEnabled;
  178. });
  179. });
  180. return result;
  181. }, [adminConfig, userConfig]);
  182. // 检查特定功能是否应该显示
  183. const isModuleVisible = (sectionKey, moduleKey = null) => {
  184. if (moduleKey) {
  185. return finalConfig[sectionKey]?.[moduleKey] === true;
  186. } else {
  187. return finalConfig[sectionKey]?.enabled === true;
  188. }
  189. };
  190. // 检查区域是否有任何可见的功能
  191. const hasSectionVisibleModules = (sectionKey) => {
  192. const section = finalConfig[sectionKey];
  193. if (!section?.enabled) return false;
  194. return Object.keys(section).some(
  195. (key) => key !== 'enabled' && section[key] === true,
  196. );
  197. };
  198. // 获取区域的可见功能列表
  199. const getVisibleModules = (sectionKey) => {
  200. const section = finalConfig[sectionKey];
  201. if (!section?.enabled) return [];
  202. return Object.keys(section).filter(
  203. (key) => key !== 'enabled' && section[key] === true,
  204. );
  205. };
  206. return {
  207. loading,
  208. adminConfig,
  209. userConfig,
  210. finalConfig,
  211. isModuleVisible,
  212. hasSectionVisibleModules,
  213. getVisibleModules,
  214. refreshUserConfig,
  215. };
  216. };