Footer.jsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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 React, { useEffect, useState, useMemo, useContext } from 'react';
  16. import { useTranslation } from 'react-i18next';
  17. import { Typography } from '@douyinfe/semi-ui';
  18. import { getFooterHTML, getLogo, getSystemName } from '../../helpers';
  19. import { StatusContext } from '../../context/Status';
  20. const FooterBar = () => {
  21. const { t } = useTranslation();
  22. const [footer, setFooter] = useState(getFooterHTML());
  23. const systemName = getSystemName();
  24. const logo = getLogo();
  25. const [statusState] = useContext(StatusContext);
  26. const isDemoSiteMode = statusState?.status?.demo_site_enabled || false;
  27. const loadFooter = () => {
  28. let footer_html = localStorage.getItem('footer_html');
  29. if (footer_html) {
  30. setFooter(footer_html);
  31. }
  32. };
  33. const currentYear = new Date().getFullYear();
  34. const customFooter = useMemo(() => (
  35. <footer className="relative h-auto py-16 px-6 md:px-24 w-full flex flex-col items-center justify-between overflow-hidden">
  36. <div className="absolute hidden md:block top-[204px] left-[-100px] w-[151px] h-[151px] rounded-full bg-[#FFD166]"></div>
  37. <div className="absolute md:hidden bottom-[20px] left-[-50px] w-[80px] h-[80px] rounded-full bg-[#FFD166] opacity-60"></div>
  38. {isDemoSiteMode && (
  39. <div className="flex flex-col md:flex-row justify-between w-full max-w-[1110px] mb-10 gap-8">
  40. <div className="flex-shrink-0">
  41. <img
  42. src={logo}
  43. alt={systemName}
  44. className="w-16 h-16 rounded-full bg-gray-800 p-1.5 object-contain"
  45. />
  46. </div>
  47. <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-8 w-full">
  48. <div className="text-left">
  49. <p className="!text-semi-color-text-0 font-semibold mb-5">{t('关于我们')}</p>
  50. <div className="flex flex-col gap-4">
  51. <a href="https://docs.newapi.pro/wiki/project-introduction/" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">{t('关于项目')}</a>
  52. <a href="https://docs.newapi.pro/support/community-interaction/" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">{t('联系我们')}</a>
  53. <a href="https://docs.newapi.pro/wiki/features-introduction/" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">{t('功能特性')}</a>
  54. </div>
  55. </div>
  56. <div className="text-left">
  57. <p className="!text-semi-color-text-0 font-semibold mb-5">{t('文档')}</p>
  58. <div className="flex flex-col gap-4">
  59. <a href="https://docs.newapi.pro/getting-started/" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">{t('快速开始')}</a>
  60. <a href="https://docs.newapi.pro/installation/" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">{t('安装指南')}</a>
  61. <a href="https://docs.newapi.pro/api/" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">{t('API 文档')}</a>
  62. </div>
  63. </div>
  64. <div className="text-left">
  65. <p className="!text-semi-color-text-0 font-semibold mb-5">{t('相关项目')}</p>
  66. <div className="flex flex-col gap-4">
  67. <a href="https://github.com/songquanpeng/one-api" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">One API</a>
  68. <a href="https://github.com/novicezk/midjourney-proxy" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">Midjourney-Proxy</a>
  69. <a href="https://github.com/Deeptrain-Community/chatnio" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">chatnio</a>
  70. <a href="https://github.com/Calcium-Ion/neko-api-key-tool" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">neko-api-key-tool</a>
  71. </div>
  72. </div>
  73. <div className="text-left">
  74. <p className="!text-semi-color-text-0 font-semibold mb-5">{t('基于New API的项目')}</p>
  75. <div className="flex flex-col gap-4">
  76. <a href="https://github.com/Calcium-Ion/new-api-horizon" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">new-api-horizon</a>
  77. {/* <a href="https://github.com/VoAPI/VoAPI" target="_blank" rel="noopener noreferrer" className="!text-semi-color-text-1">VoAPI</a> */}
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. )}
  83. <div className="flex flex-col md:flex-row items-center justify-between w-full max-w-[1110px] gap-6">
  84. <div className="flex flex-wrap items-center gap-2">
  85. <Typography.Text className="text-sm !text-semi-color-text-1">© {currentYear} {systemName}. {t('版权所有')}</Typography.Text>
  86. </div>
  87. <div className="text-sm">
  88. <span className="!text-semi-color-text-1">{t('设计与开发由')} </span>
  89. <a href="https://github.com/QuantumNous/new-api" target="_blank" rel="noopener noreferrer" className="!text-semi-color-primary font-medium">New API</a>
  90. <span className="!text-semi-color-text-1"> & </span>
  91. <a href="https://github.com/songquanpeng/one-api" target="_blank" rel="noopener noreferrer" className="!text-semi-color-primary font-medium">One API</a>
  92. </div>
  93. </div>
  94. </footer>
  95. ), [logo, systemName, t, currentYear, isDemoSiteMode]);
  96. useEffect(() => {
  97. loadFooter();
  98. }, []);
  99. return (
  100. <div className="w-full">
  101. {footer ? (
  102. <div
  103. className="custom-footer"
  104. dangerouslySetInnerHTML={{ __html: footer }}
  105. ></div>
  106. ) : (
  107. customFooter
  108. )}
  109. </div>
  110. );
  111. };
  112. export default FooterBar;