-
+
);
};
diff --git a/web/src/components/settings/personal/cards/AccountManagement.js b/web/src/components/settings/personal/cards/AccountManagement.js
new file mode 100644
index 00000000..b2c9017e
--- /dev/null
+++ b/web/src/components/settings/personal/cards/AccountManagement.js
@@ -0,0 +1,411 @@
+/*
+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 .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React from 'react';
+import {
+ Button,
+ Card,
+ Input,
+ Space,
+ Typography,
+ Avatar,
+ Tabs,
+ TabPane
+} from '@douyinfe/semi-ui';
+import {
+ IconMail,
+ IconShield,
+ IconGithubLogo,
+ IconKey,
+ IconLock,
+ IconDelete
+} from '@douyinfe/semi-icons';
+import { SiTelegram, SiWechat, SiLinux } from 'react-icons/si';
+import { UserPlus, ShieldCheck } from 'lucide-react';
+import TelegramLoginButton from 'react-telegram-login';
+import {
+ onGitHubOAuthClicked,
+ onOIDCClicked,
+ onLinuxDOOAuthClicked
+} from '../../../../helpers';
+import TwoFASetting from '../components/TwoFASetting';
+
+const AccountManagement = ({
+ t,
+ userState,
+ status,
+ systemToken,
+ setShowEmailBindModal,
+ setShowWeChatBindModal,
+ generateAccessToken,
+ handleSystemTokenClick,
+ setShowChangePasswordModal,
+ setShowAccountDeleteModal
+}) => {
+ return (
+
+ {/* 卡片头部 */}
+
+
+
+
+ );
+};
+
+export default AccountManagement;
diff --git a/web/src/components/settings/personal/cards/ModelsList.js b/web/src/components/settings/personal/cards/ModelsList.js
new file mode 100644
index 00000000..f69c605f
--- /dev/null
+++ b/web/src/components/settings/personal/cards/ModelsList.js
@@ -0,0 +1,240 @@
+/*
+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 .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React, { useState, useEffect } from 'react';
+import {
+ Empty,
+ Skeleton,
+ Space,
+ Tag,
+ Collapsible,
+ Tabs,
+ TabPane,
+ Typography,
+ Avatar
+} from '@douyinfe/semi-ui';
+import { IllustrationNoContent, IllustrationNoContentDark } from '@douyinfe/semi-illustrations';
+import {
+ IconChevronDown,
+ IconChevronUp
+} from '@douyinfe/semi-icons';
+import { Settings } from 'lucide-react';
+import { renderModelTag, getModelCategories } from '../../../../helpers';
+
+const ModelsList = ({ t, models, modelsLoading, copyText }) => {
+ const [isModelsExpanded, setIsModelsExpanded] = useState(() => {
+ // Initialize from localStorage if available
+ const savedState = localStorage.getItem('modelsExpanded');
+ return savedState ? JSON.parse(savedState) : false;
+ });
+ const [activeModelCategory, setActiveModelCategory] = useState('all');
+ const MODELS_DISPLAY_COUNT = 25; // 默认显示的模型数量
+
+ // Save models expanded state to localStorage whenever it changes
+ useEffect(() => {
+ localStorage.setItem('modelsExpanded', JSON.stringify(isModelsExpanded));
+ }, [isModelsExpanded]);
+
+ return (
+
+ );
+};
+
+export default ModelsList;
diff --git a/web/src/components/settings/personal/cards/NotificationSettings.js b/web/src/components/settings/personal/cards/NotificationSettings.js
new file mode 100644
index 00000000..dfa37e1b
--- /dev/null
+++ b/web/src/components/settings/personal/cards/NotificationSettings.js
@@ -0,0 +1,289 @@
+/*
+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 .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React, { useRef, useEffect } from 'react';
+import {
+ Button,
+ Typography,
+ Card,
+ Avatar,
+ Form,
+ Radio,
+ Toast,
+ Tabs,
+ TabPane
+} from '@douyinfe/semi-ui';
+import {
+ IconMail,
+ IconKey,
+ IconBell,
+ IconLink
+} from '@douyinfe/semi-icons';
+import { ShieldCheck, Bell, DollarSign } from 'lucide-react';
+import { renderQuotaWithPrompt } from '../../../../helpers';
+import CodeViewer from '../../../playground/CodeViewer';
+
+const NotificationSettings = ({
+ t,
+ notificationSettings,
+ handleNotificationSettingChange,
+ saveNotificationSettings
+}) => {
+ const formApiRef = useRef(null);
+
+ // 初始化表单值
+ useEffect(() => {
+ if (formApiRef.current && notificationSettings) {
+ formApiRef.current.setValues(notificationSettings);
+ }
+ }, [notificationSettings]);
+
+ // 处理表单字段变化
+ const handleFormChange = (field, value) => {
+ handleNotificationSettingChange(field, value);
+ };
+
+ // 表单提交
+ const handleSubmit = () => {
+ if (formApiRef.current) {
+ formApiRef.current.validate()
+ .then(() => {
+ saveNotificationSettings();
+ })
+ .catch((errors) => {
+ console.log('表单验证失败:', errors);
+ Toast.error(t('请检查表单填写是否正确'));
+ });
+ } else {
+ saveNotificationSettings();
+ }
+ };
+
+ return (
+
+
+
+ }
+ >
+ {/* 卡片头部 */}
+
+
+
+
+
+ {t('其他设置')}
+
{t('通知、价格和隐私相关设置')}
+
+
+
+
+
+ );
+};
+
+export default NotificationSettings;
diff --git a/web/src/components/settings/TwoFASetting.js b/web/src/components/settings/personal/components/TwoFASetting.js
similarity index 99%
rename from web/src/components/settings/TwoFASetting.js
rename to web/src/components/settings/personal/components/TwoFASetting.js
index 15a56780..6b1f96bd 100644
--- a/web/src/components/settings/TwoFASetting.js
+++ b/web/src/components/settings/personal/components/TwoFASetting.js
@@ -16,7 +16,7 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
-import { API, showError, showSuccess, showWarning } from '../../helpers';
+import { API, showError, showSuccess, showWarning } from '../../../../helpers';
import { Banner, Button, Card, Checkbox, Divider, Input, Modal, Tag, Typography, Steps, Space, Badge } from '@douyinfe/semi-ui';
import {
IconShield,
@@ -353,11 +353,7 @@ const TwoFASetting = ({ t }) => {
return (
<>
-
+
diff --git a/web/src/components/settings/personal/components/UserInfoHeader.js b/web/src/components/settings/personal/components/UserInfoHeader.js
new file mode 100644
index 00000000..e1843d00
--- /dev/null
+++ b/web/src/components/settings/personal/components/UserInfoHeader.js
@@ -0,0 +1,192 @@
+/*
+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 .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React from 'react';
+import { Avatar, Card, Tag, Divider, Typography } from '@douyinfe/semi-ui';
+import { isRoot, isAdmin, renderQuota } from '../../../../helpers';
+import { useTheme } from '../../../../context/Theme';
+import { Coins, BarChart2, Users } from 'lucide-react';
+
+const UserInfoHeader = ({ t, userState }) => {
+ const theme = useTheme();
+
+ const getUsername = () => {
+ if (userState.user) {
+ return userState.user.username;
+ } else {
+ return 'null';
+ }
+ };
+
+ const getAvatarText = () => {
+ const username = getUsername();
+ if (username && username.length > 0) {
+ // 获取前两个字符,支持中文和英文
+ return username.slice(0, 2).toUpperCase();
+ }
+ return 'NA';
+ };
+
+ return (
+
+ {/* 装饰性背景元素 */}
+
+
+ );
+};
+
+export default UserInfoHeader;
diff --git a/web/src/components/settings/personal/modals/AccountDeleteModal.js b/web/src/components/settings/personal/modals/AccountDeleteModal.js
new file mode 100644
index 00000000..22e785e7
--- /dev/null
+++ b/web/src/components/settings/personal/modals/AccountDeleteModal.js
@@ -0,0 +1,92 @@
+/*
+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 .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React from 'react';
+import { Banner, Input, Modal, Typography } from '@douyinfe/semi-ui';
+import { IconDelete, IconUser } from '@douyinfe/semi-icons';
+import Turnstile from 'react-turnstile';
+
+const AccountDeleteModal = ({
+ t,
+ showAccountDeleteModal,
+ setShowAccountDeleteModal,
+ inputs,
+ handleInputChange,
+ deleteAccount,
+ userState,
+ turnstileEnabled,
+ turnstileSiteKey,
+ setTurnstileToken
+}) => {
+ return (
+
+
+ {t('删除账户确认')}
+
+
+ );
+};
+
+export default AccountDeleteModal;
diff --git a/web/src/components/settings/personal/modals/ChangePasswordModal.js b/web/src/components/settings/personal/modals/ChangePasswordModal.js
new file mode 100644
index 00000000..09d570cc
--- /dev/null
+++ b/web/src/components/settings/personal/modals/ChangePasswordModal.js
@@ -0,0 +1,115 @@
+/*
+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 .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React from 'react';
+import { Input, Modal, Typography } from '@douyinfe/semi-ui';
+import { IconLock } from '@douyinfe/semi-icons';
+import Turnstile from 'react-turnstile';
+
+const ChangePasswordModal = ({
+ t,
+ showChangePasswordModal,
+ setShowChangePasswordModal,
+ inputs,
+ handleInputChange,
+ changePassword,
+ turnstileEnabled,
+ turnstileSiteKey,
+ setTurnstileToken
+}) => {
+ return (
+
+
+ {t('修改密码')}
+
+
+ );
+};
+
+export default ChangePasswordModal;
diff --git a/web/src/components/settings/personal/modals/EmailBindModal.js b/web/src/components/settings/personal/modals/EmailBindModal.js
new file mode 100644
index 00000000..fe3b485c
--- /dev/null
+++ b/web/src/components/settings/personal/modals/EmailBindModal.js
@@ -0,0 +1,106 @@
+/*
+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 .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React from 'react';
+import { Button, Input, Modal } from '@douyinfe/semi-ui';
+import { IconMail, IconKey } from '@douyinfe/semi-icons';
+import Turnstile from 'react-turnstile';
+
+const EmailBindModal = ({
+ t,
+ showEmailBindModal,
+ setShowEmailBindModal,
+ inputs,
+ handleInputChange,
+ sendVerificationCode,
+ bindEmail,
+ disableButton,
+ loading,
+ countdown,
+ turnstileEnabled,
+ turnstileSiteKey,
+ setTurnstileToken
+}) => {
+ return (
+
+
+ {t('绑定邮箱地址')}
+
+
+ );
+};
+
+export default EmailBindModal;
diff --git a/web/src/components/settings/personal/modals/WeChatBindModal.js b/web/src/components/settings/personal/modals/WeChatBindModal.js
new file mode 100644
index 00000000..a276aeeb
--- /dev/null
+++ b/web/src/components/settings/personal/modals/WeChatBindModal.js
@@ -0,0 +1,80 @@
+/*
+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 .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React from 'react';
+import { Button, Input, Modal, Image } from '@douyinfe/semi-ui';
+import { IconKey } from '@douyinfe/semi-icons';
+import { SiWechat } from 'react-icons/si';
+
+const WeChatBindModal = ({
+ t,
+ showWeChatBindModal,
+ setShowWeChatBindModal,
+ inputs,
+ handleInputChange,
+ bindWeChat,
+ status
+}) => {
+ return (
+
+
+ {t('绑定微信账户')}
+
+ }
+ visible={showWeChatBindModal}
+ onCancel={() => setShowWeChatBindModal(false)}
+ footer={null}
+ size={'small'}
+ centered={true}
+ className="modern-modal"
+ >
+
+
+ );
+};
+
+export default WeChatBindModal;
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 14870045..1d8f144f 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -1691,7 +1691,7 @@
"暂无监控数据": "No monitoring data",
"IP记录": "IP Record",
"记录请求与错误日志 IP": "Record request and error log IP",
- "开启后,仅“消费”和“错误”日志将记录您的客户端 IP 地址": "After enabling, only \"consumption\" and \"error\" logs will record your client IP address",
+ "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "After enabling, only \"consumption\" and \"error\" logs will record your client IP address",
"只有当用户设置开启IP记录时,才会进行请求和错误类型日志的IP记录": "Only when the user sets IP recording, the IP recording of request and error type logs will be performed",
"设置保存成功": "Settings saved successfully",
"设置保存失败": "Settings save failed",
@@ -1973,5 +1973,19 @@
"禁用2FA失败": "Failed to disable 2FA",
"备用码重新生成成功": "Backup codes regenerated successfully",
"重新生成备用码失败": "Failed to regenerate backup codes",
- "备用码已复制到剪贴板": "Backup codes copied to clipboard"
+ "备用码已复制到剪贴板": "Backup codes copied to clipboard",
+ "账户管理": "Account management",
+ "账户绑定、安全设置和身份验证": "Account binding, security settings and identity verification",
+ "通知、价格和隐私相关设置": "Notification, price and privacy related settings",
+ "通知配置": "Notification configuration",
+ "只支持HTTPS,系统将以POST方式发送通知,请确保地址可以接收POST请求": "Only HTTPS is supported, the system will send notifications via POST, please ensure that the address can receive POST requests",
+ "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "The key will be added to the request header as Bearer to verify the legitimacy of the webhook request",
+ "Webhook请求结构说明": "Webhook request structure description",
+ "通知类型 (quota_exceed: 额度预警)": "Notification type (quota_exceed: quota warning)",
+ "通知标题": "Notification title",
+ "通知内容,支持 {{value}} 变量占位符": "Notification content, supports {{value}} variable placeholders",
+ "按顺序替换content中的变量占位符": "Replace variable placeholders in content in order",
+ "Unix时间戳": "Unix timestamp",
+ "隐私设置": "Privacy settings",
+ "记录请求与错误日志IP": "Record request and error log IP"
}
\ No newline at end of file