index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import React, { useContext, useEffect, useState } from 'react';
  2. import { Button, Typography, Tag } from '@douyinfe/semi-ui';
  3. import { API, showError, isMobile } from '../../helpers';
  4. import { StatusContext } from '../../context/Status';
  5. import { marked } from 'marked';
  6. import { useTranslation } from 'react-i18next';
  7. import { IconGithubLogo } from '@douyinfe/semi-icons';
  8. import exampleImage from '/example.png';
  9. import { Link } from 'react-router-dom';
  10. import NoticeModal from '../../components/layout/NoticeModal';
  11. import { Moonshot, OpenAI, XAI, Zhipu, Volcengine, Cohere, Claude, Gemini, Suno, Minimax, Wenxin, Spark, Qingyan, DeepSeek, Qwen, Midjourney, Grok, AzureAI, Hunyuan, Xinference } from '@lobehub/icons';
  12. const { Text } = Typography;
  13. const Home = () => {
  14. const { t, i18n } = useTranslation();
  15. const [statusState] = useContext(StatusContext);
  16. const [homePageContentLoaded, setHomePageContentLoaded] = useState(false);
  17. const [homePageContent, setHomePageContent] = useState('');
  18. const [noticeVisible, setNoticeVisible] = useState(false);
  19. const isDemoSiteMode = statusState?.status?.demo_site_enabled || false;
  20. useEffect(() => {
  21. const lastCloseDate = localStorage.getItem('notice_close_date');
  22. const today = new Date().toDateString();
  23. if (lastCloseDate !== today) {
  24. setNoticeVisible(true);
  25. }
  26. }, []);
  27. const displayHomePageContent = async () => {
  28. setHomePageContent(localStorage.getItem('home_page_content') || '');
  29. const res = await API.get('/api/home_page_content');
  30. const { success, message, data } = res.data;
  31. if (success) {
  32. let content = data;
  33. if (!data.startsWith('https://')) {
  34. content = marked.parse(data);
  35. }
  36. setHomePageContent(content);
  37. localStorage.setItem('home_page_content', content);
  38. // 如果内容是 URL,则发送主题模式
  39. if (data.startsWith('https://')) {
  40. const iframe = document.querySelector('iframe');
  41. if (iframe) {
  42. const theme = localStorage.getItem('theme-mode') || 'light';
  43. iframe.onload = () => {
  44. iframe.contentWindow.postMessage({ themeMode: theme }, '*');
  45. iframe.contentWindow.postMessage({ lang: i18n.language }, '*');
  46. };
  47. }
  48. }
  49. } else {
  50. showError(message);
  51. setHomePageContent('加载首页内容失败...');
  52. }
  53. setHomePageContentLoaded(true);
  54. };
  55. useEffect(() => {
  56. displayHomePageContent().then();
  57. }, []);
  58. return (
  59. <div className="w-full overflow-x-hidden">
  60. <NoticeModal
  61. visible={noticeVisible}
  62. onClose={() => setNoticeVisible(false)}
  63. isMobile={isMobile()}
  64. />
  65. {homePageContentLoaded && homePageContent === '' ? (
  66. <div className="w-full overflow-x-hidden">
  67. {/* Banner 部分 */}
  68. <div className="w-full border-b border-semi-color-border min-h-[500px] md:h-[650px] lg:h-[750px] relative overflow-x-hidden">
  69. <div className="flex flex-col md:flex-row items-center justify-center h-full px-4 py-8 md:py-0">
  70. {/* 左侧内容区 */}
  71. <div className="flex-shrink-0 w-full md:w-[480px] md:mr-[60px] lg:mr-[120px] mb-8 md:mb-0">
  72. <div className="flex items-center gap-2 justify-center md:justify-start">
  73. <h1 className="text-3xl md:text-4xl lg:text-5xl font-semibold text-semi-color-text-0 w-auto leading-normal md:leading-[67px]">
  74. {statusState?.status?.system_name}
  75. </h1>
  76. {statusState?.status?.version && (
  77. <Tag color='light-blue' size='large' shape='circle' className="ml-1">
  78. {statusState.status.version}
  79. </Tag>
  80. )}
  81. </div>
  82. <p className="text-base md:text-lg text-semi-color-text-0 mt-4 md:mt-8 w-full md:w-[480px] leading-7 md:leading-8 text-center md:text-left">
  83. {t('新一代大模型网关与AI资产管理系统,一键接入主流大模型,轻松管理您的AI资产')}
  84. </p>
  85. {/* 操作按钮 */}
  86. <div className="mt-6 md:mt-10 flex flex-wrap gap-4 justify-center md:justify-start">
  87. <Link to="/console">
  88. <Button theme="solid" type="primary" size="large" className="!rounded-3xl">
  89. {t('开始使用')}
  90. </Button>
  91. </Link>
  92. {isDemoSiteMode && (
  93. <Button
  94. size="large"
  95. className="flex items-center !rounded-3xl"
  96. icon={<IconGithubLogo />}
  97. onClick={() => window.open('https://github.com/QuantumNous/new-api', '_blank')}
  98. >
  99. GitHub
  100. </Button>
  101. )}
  102. </div>
  103. {/* 框架兼容性图标 */}
  104. <div className="mt-8 md:mt-16">
  105. <div className="flex items-center mb-3 justify-center md:justify-start">
  106. <Text type="tertiary" className="text-lg md:text-xl font-light">
  107. {t('支持众多的大模型供应商')}
  108. </Text>
  109. </div>
  110. <div className="flex flex-wrap items-center relative mt-6 md:mt-8 gap-6 md:gap-8 justify-center md:justify-start">
  111. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  112. <Moonshot size={40} />
  113. </div>
  114. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  115. <OpenAI size={40} />
  116. </div>
  117. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  118. <XAI size={40} />
  119. </div>
  120. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  121. <Zhipu.Color size={40} />
  122. </div>
  123. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  124. <Volcengine.Color size={40} />
  125. </div>
  126. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  127. <Cohere.Color size={40} />
  128. </div>
  129. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  130. <Claude.Color size={40} />
  131. </div>
  132. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  133. <Gemini.Color size={40} />
  134. </div>
  135. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  136. <Suno size={40} />
  137. </div>
  138. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  139. <Minimax.Color size={40} />
  140. </div>
  141. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  142. <Wenxin.Color size={40} />
  143. </div>
  144. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  145. <Spark.Color size={40} />
  146. </div>
  147. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  148. <Qingyan.Color size={40} />
  149. </div>
  150. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  151. <DeepSeek.Color size={40} />
  152. </div>
  153. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  154. <Qwen.Color size={40} />
  155. </div>
  156. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  157. <Midjourney size={40} />
  158. </div>
  159. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  160. <Grok size={40} />
  161. </div>
  162. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  163. <AzureAI.Color size={40} />
  164. </div>
  165. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  166. <Hunyuan.Color size={40} />
  167. </div>
  168. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  169. <Xinference.Color size={40} />
  170. </div>
  171. <div className="relative w-8 md:w-10 h-8 md:h-10 flex items-center justify-center">
  172. <Typography.Text className="!text-2xl font-bold">30+</Typography.Text>
  173. </div>
  174. </div>
  175. </div>
  176. </div>
  177. {/* 右侧图片区域 - 在小屏幕上隐藏或调整位置 */}
  178. <div className="flex-shrink-0 relative md:mr-[-200px] lg:mr-[-400px] hidden md:block lg:min-w-[1100px]">
  179. <div className="absolute w-[320px] md:w-[500px] lg:w-[640px] h-[320px] md:h-[500px] lg:h-[640px] left-[-25px] md:left-[-40px] lg:left-[-50px] top-[-10px] md:top-[-15px] lg:top-[-20px] opacity-60"
  180. style={{ filter: 'blur(120px)' }}>
  181. <div className="absolute w-[320px] md:w-[400px] lg:w-[474px] h-[320px] md:h-[400px] lg:h-[474px] top-[80px] md:top-[100px] lg:top-[132px] bg-semi-color-primary rounded-full opacity-30"></div>
  182. <div className="absolute w-[320px] md:w-[400px] lg:w-[474px] h-[320px] md:h-[400px] lg:h-[474px] left-[80px] md:left-[120px] lg:left-[166px] bg-semi-color-tertiary rounded-full opacity-30"></div>
  183. </div>
  184. <img
  185. src={exampleImage}
  186. alt="application demo"
  187. className="relative h-[400px] md:h-[600px] lg:h-[721px] ml-[-15px] md:ml-[-20px] lg:ml-[-30px] mt-[-15px] md:mt-[-20px] lg:mt-[-30px]"
  188. />
  189. </div>
  190. </div>
  191. </div>
  192. </div>
  193. ) : (
  194. <div className="overflow-x-hidden w-full">
  195. {homePageContent.startsWith('https://') ? (
  196. <iframe
  197. src={homePageContent}
  198. className="w-full h-screen border-none"
  199. />
  200. ) : (
  201. <div
  202. className="text-base md:text-lg p-4 md:p-6 overflow-x-hidden"
  203. dangerouslySetInnerHTML={{ __html: homePageContent }}
  204. ></div>
  205. )}
  206. </div>
  207. )}
  208. </div>
  209. );
  210. };
  211. export default Home;