diff --git a/web/src/App.js b/web/src/App.js index 93b4e496..2d715767 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -25,7 +25,7 @@ import Playground from './pages/Playground/index.js'; import OAuth2Callback from './components/auth/OAuth2Callback.js'; import PersonalSetting from './components/settings/PersonalSetting.js'; import Setup from './pages/Setup/index.js'; -import { useSetupCheck } from './hooks/useSetupCheck.js'; +import SetupCheck from './components/layout/SetupCheck.js'; const Home = lazy(() => import('./pages/Home')); const Detail = lazy(() => import('./pages/Detail')); @@ -35,7 +35,7 @@ function App() { const location = useLocation(); return ( - + } /> - + ); } diff --git a/web/src/components/layout/SetupCheck.js b/web/src/components/layout/SetupCheck.js new file mode 100644 index 00000000..3fbd9012 --- /dev/null +++ b/web/src/components/layout/SetupCheck.js @@ -0,0 +1,18 @@ +import React, { useContext, useEffect } from 'react'; +import { Navigate, useLocation } from 'react-router-dom'; +import { StatusContext } from '../../context/Status'; + +const SetupCheck = ({ children }) => { + const [statusState] = useContext(StatusContext); + const location = useLocation(); + + useEffect(() => { + if (statusState?.status?.setup === false && location.pathname !== '/setup') { + window.location.href = '/setup'; + } + }, [statusState?.status?.setup, location.pathname]); + + return children; +}; + +export default SetupCheck; \ No newline at end of file diff --git a/web/src/hooks/useSetupCheck.js b/web/src/hooks/useSetupCheck.js deleted file mode 100644 index d2233de8..00000000 --- a/web/src/hooks/useSetupCheck.js +++ /dev/null @@ -1,32 +0,0 @@ -import { useContext, useEffect } from 'react'; -import { useLocation } from 'react-router-dom'; -import { StatusContext } from '../context/Status'; - -/** - * 自定义Hook:检查系统setup状态并进行重定向 - * @param {Object} options - 配置选项 - * @param {boolean} options.autoRedirect - 是否自动重定向,默认true - * @param {string} options.setupPath - setup页面路径,默认'/setup' - * @returns {Object} 返回setup状态信息 - */ -export function useSetupCheck(options = {}) { - const { autoRedirect = true, setupPath = '/setup' } = options; - const [statusState] = useContext(StatusContext); - const location = useLocation(); - - const isSetupComplete = statusState?.status?.setup !== false; - const needsSetup = !isSetupComplete && location.pathname !== setupPath; - - useEffect(() => { - if (autoRedirect && needsSetup) { - window.location.href = setupPath; - } - }, [autoRedirect, needsSetup, setupPath]); - - return { - isSetupComplete, - needsSetup, - statusState, - currentPath: location.pathname - }; -} \ No newline at end of file diff --git a/web/src/hooks/useTokenKeys.js b/web/src/hooks/useTokenKeys.js index a6583591..eba69e08 100644 --- a/web/src/hooks/useTokenKeys.js +++ b/web/src/hooks/useTokenKeys.js @@ -13,7 +13,7 @@ export function useTokenKeys(id) { if (fetchedKeys.length === 0) { showError('当前没有可用的启用令牌,请确认是否有令牌处于启用状态!'); setTimeout(() => { - window.location.href = '/token'; + window.location.href = '/console/token'; }, 1500); // 延迟 1.5 秒后跳转 } setKeys(fetchedKeys); diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 3ab3a51f..e0575264 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1367,7 +1367,7 @@ "提示 {{nonCacheInput}} tokens + 缓存 {{cacheInput}} tokens * {{cacheRatio}} / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}": "Prompt {{nonCacheInput}} tokens + cache {{cacheInput}} tokens * {{cacheRatio}} / 1M tokens * ${{price}} + completion {{completion}} tokens / 1M tokens * ${{compPrice}} * group {{ratio}} = ${{total}}", "缓存 Tokens": "Cache Tokens", "系统初始化": "System initialization", - "管理员账号已经初始化过,请继续设置系统参数": "The admin account has already been initialized, please continue to set the system parameters", + "管理员账号已经初始化过,请继续设置其他参数": "The admin account has already been initialized, please continue to set other parameters", "管理员账号": "Admin account", "请输入管理员用户名": "Please enter the admin username", "请输入管理员密码": "Please enter the admin password", @@ -1532,5 +1532,24 @@ "搜索条件": "Search Conditions", "加载中...": "Loading...", "暂无公告": "No Notice", - "操练场": "Playground" + "操练场": "Playground", + "欢迎使用,请完成以下设置以开始使用系统": "Welcome to use, please complete the following settings to start using the system", + "数据库信息": "Database Information", + "您正在使用 MySQL 数据库。MySQL 是一个可靠的关系型数据库管理系统,适合生产环境使用。": "You are using the MySQL database. MySQL is a reliable relational database management system, suitable for production environments.", + "您正在使用 PostgreSQL 数据库。PostgreSQL 是一个功能强大的开源关系型数据库系统,提供了出色的可靠性和数据完整性,适合生产环境使用。": "You are using the PostgreSQL database. PostgreSQL is a powerful open-source relational database system that provides excellent reliability and data integrity, suitable for production environments.", + "设置系统管理员的登录信息": "Set the login information for the system administrator", + "选择适合您使用场景的模式": "Select the mode suitable for your usage scenario", + "使用模式说明": "Usage mode description", + "计费模式": "Billing mode", + "多用户支持": "Multi-user support", + "个人使用": "Personal use", + "功能演示": "Function demonstration", + "体验试用": "Experience trial", + "默认模式": "Default Mode", + "无需计费": "No Charge", + "演示体验": "Demo Experience", + "提供基础功能演示,方便用户了解系统特性。": "Provide basic feature demonstrations to help users understand the system features.", + "适用于为多个用户提供服务的场景": "Suitable for scenarios where multiple users are provided.", + "适用于个人使用的场景,不需要设置模型价格": "Suitable for personal use, no need to set model price.", + "适用于展示系统功能的场景,提供基础功能演示": "Suitable for scenarios where the system functions are displayed, providing basic feature demonstrations." } \ No newline at end of file diff --git a/web/src/pages/Setup/index.js b/web/src/pages/Setup/index.js index 3079676a..54240b37 100644 --- a/web/src/pages/Setup/index.js +++ b/web/src/pages/Setup/index.js @@ -6,14 +6,20 @@ import { Typography, Modal, Banner, + Layout, + Tag, } from '@douyinfe/semi-ui'; import { API, showError, showNotice } from '../../helpers'; import { useTranslation } from 'react-i18next'; import { IconHelpCircle, IconInfoCircle, - IconAlertTriangle, + IconUser, + IconLock, + IconSetting, + IconCheckCircleStroked, } from '@douyinfe/semi-icons'; +import { Shield, Rocket, FlaskConical, Database, Layers } from 'lucide-react'; const Setup = () => { const { t } = useTranslation(); @@ -127,163 +133,443 @@ const Setup = () => { }; return ( - <> -
- - - {t('系统初始化')} - - - {setupStatus.database_type === 'sqlite' && ( - } - closeIcon={null} - title={t('数据库警告')} - description={ -
-

- {t( - '您正在使用 SQLite 数据库。如果您在容器环境中运行,请确保已正确设置数据库文件的持久化映射,否则容器重启后所有数据将丢失!', - )} -

-

- {t( - '建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。', - )} -

-
- } - style={{ marginBottom: '24px' }} - /> - )} - -
{ - formRef.current = formApi; - console.log('Form API set:', formApi); - }} - initValues={formData} - > - {setupStatus.root_init ? ( - } - closeIcon={null} - description={t('管理员账号已经初始化过,请继续设置系统参数')} - style={{ marginBottom: '24px' }} - /> - ) : ( - - - setFormData({ ...formData, username: value }) - } - /> - - setFormData({ ...formData, password: value }) - } - /> - - setFormData({ ...formData, confirmPassword: value }) - } - /> - - )} - - - {t('系统设置')} -
- } - > - - {t('使用模式')} - { - // e.preventDefault(); - // e.stopPropagation(); - setUsageModeInfoVisible(true); - }} - /> +
+ + +
+
+ {/* 主卡片容器 */} + + {/* 顶部装饰性区域 */} + + {/* 装饰性背景元素 */} +
+
+
+
- } - extraText={t('可在初始化后修改')} - initValue='external' - onChange={handleUsageModeChange} - > - {t('对外运营模式')} - {t('自用模式')} - {t('演示站点模式')} - - - -
- +
+
+ +
+
+ + {t('系统初始化')} + + + {t('欢迎使用,请完成以下设置以开始使用系统')} + +
+
+ {/* 数据库警告 */} + {setupStatus.database_type === 'sqlite' && ( +
+ + +
+ } + closeIcon={null} + title={ +
+ {t('数据库警告')} + + SQLite + +
+ } + description={ +
+

+ {t( + '您正在使用 SQLite 数据库。如果您在容器环境中运行,请确保已正确设置数据库文件的持久化映射,否则容器重启后所有数据将丢失!', + )} +

+

+ {t( + '建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。', + )} +

+
+ } + className="!rounded-xl mb-6" + fullMode={false} + bordered + /> +
+ )} + {/* MySQL数据库提示 */} + {setupStatus.database_type === 'mysql' && ( +
+ + +
+ } + closeIcon={null} + title={ +
+ {t('数据库信息')} + + MySQL + +
+ } + description={ +
+

+ {t( + '您正在使用 MySQL 数据库。MySQL 是一个可靠的关系型数据库管理系统,适合生产环境使用。', + )} +

+
+ } + className="!rounded-xl mb-6" + fullMode={false} + bordered + /> +
+ )} + {/* PostgreSQL数据库提示 */} + {setupStatus.database_type === 'postgres' && ( +
+ + +
+ } + closeIcon={null} + title={ +
+ {t('数据库信息')} + + PostgreSQL + +
+ } + description={ +
+

+ {t( + '您正在使用 PostgreSQL 数据库。PostgreSQL 是一个功能强大的开源关系型数据库系统,提供了出色的可靠性和数据完整性,适合生产环境使用。', + )} +

+
+ } + className="!rounded-xl mb-6" + fullMode={false} + bordered + /> +
+ )} + + + {/* 主内容区域 */} +
{ + formRef.current = formApi; + console.log('Form API set:', formApi); + }} + initValues={formData} + > + {/* 管理员账号设置 */} + +
+
+
+
+
+
+ +
+
+ {t('管理员账号')} +
{t('设置系统管理员的登录信息')}
+
+
+ + {setupStatus.root_init ? ( + <> + + +
+ } + closeIcon={null} + description={ +
+ {t('管理员账号已经初始化过,请继续设置其他参数')} +
+ } + className="!rounded-lg" + /> + + ) : ( + <> + } + showClear + size='large' + className="mb-4 !rounded-lg" + noLabel={false} + validateStatus="default" + onChange={(value) => + setFormData({ ...formData, username: value }) + } + /> + } + showClear + size='large' + className="mb-4 !rounded-lg" + noLabel={false} + validateStatus="default" + onChange={(value) => + setFormData({ ...formData, password: value }) + } + /> + } + showClear + size='large' + className="!rounded-lg" + noLabel={false} + validateStatus="default" + onChange={(value) => + setFormData({ ...formData, confirmPassword: value }) + } + /> + + )} + + + {/* 使用模式 */} + +
+
+
+
+
+
+ +
+
+
+ {t('使用模式')} +
+
{t('选择适合您使用场景的模式')}
+
+
+ + +
+ +
+ +
+
+
{t('对外运营模式')}
+
{t('适用于为多个用户提供服务的场景')}
+ + {t('默认模式')} + +
+
+ } + /> + +
+ +
+
+
{t('自用模式')}
+
{t('适用于个人使用的场景,不需要设置模型价格')}
+ + {t('无需计费')} + +
+ + } + /> + +
+ +
+
+
{t('演示站点模式')}
+
{t('适用于展示系统功能的场景,提供基础功能演示')}
+ + {t('演示体验')} + +
+ + } + /> + +
+
+ + +
+ +
+ + - - + + + {/* 使用模式说明模态框 */} + + {t('使用模式说明')} + + } visible={selfUseModeInfoVisible} onOk={() => setUsageModeInfoVisible(false)} onCancel={() => setUsageModeInfoVisible(false)} closeOnEsc={true} - okText={t('确定')} + okText={t('我已了解')} cancelText={null} + centered={true} + size='medium' + className="[&_.semi-modal-body]:!p-6" > -
- {t('对外运营模式')} -

{t('默认模式,适用于为多个用户提供服务的场景。')}

-

- {t( - '此模式下,系统将计算每次调用的用量,您需要对每个模型都设置价格,如果没有设置价格,用户将无法使用该模型。', - )} -

-
-
- {t('自用模式')} -

{t('适用于个人使用的场景。')}

-

- {t('不需要设置模型价格,系统将弱化用量计算,您可专注于使用模型。')} -

-
-
- {t('演示站点模式')} -

{t('适用于展示系统功能的场景。')}

+
+ {/* 对外运营模式 */} +
+
+
+ +
+
+ {t('对外运营模式')} +
+

{t('默认模式,适用于为多个用户提供服务的场景。')}

+

{t('此模式下,系统将计算每次调用的用量,您需要对每个模型都设置价格,如果没有设置价格,用户将无法使用该模型。')}

+
+ {t('计费模式')} + {t('多用户支持')} +
+
+
+
+
+ + {/* 自用模式 */} +
+
+
+ +
+
+ {t('自用模式')} +
+

{t('适用于个人使用的场景。')}

+

{t('不需要设置模型价格,系统将弱化用量计算,您可专注于使用模型。')}

+
+ {t('无需计费')} + {t('个人使用')} +
+
+
+
+
+ + {/* 演示站点模式 */} +
+
+
+ +
+
+ {t('演示站点模式')} +
+

{t('适用于展示系统功能的场景。')}

+

{t('提供基础功能演示,方便用户了解系统特性。')}

+
+ {t('功能演示')} + {t('体验试用')} +
+
+
+
+
- +
); };