From 01ef1fe4e4984802ef344ac1dbed49cf66476a6f Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sat, 21 Jun 2025 04:16:01 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20refactor:=20reorganize=20payment?= =?UTF-8?q?=20settings=20into=20dedicated=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure payment settings into a separate tab for better organization and user experience. The changes include: 1. Create dedicated Payment components in the Setting directory structure 2. Move payment-related settings from SystemSetting to PaymentSetting 3. Add proper i18n support with useTranslation hook 4. Split payment settings into GeneralPayment and PaymentGateway components 5. Fix internationalization issues in placeholder text 6. Update navigation with CreditCard icon for payment tab This refactoring improves code maintainability by following the established project pattern of having specialized setting components in their own directories. --- web/src/components/settings/PaymentSetting.js | 88 +++++++ web/src/components/settings/SystemSetting.js | 150 ------------ web/src/i18n/locales/en.json | 13 +- .../Setting/Payment/SettingsGeneralPayment.js | 74 ++++++ .../Setting/Payment/SettingsPaymentGateway.js | 218 ++++++++++++++++++ web/src/pages/Setting/index.js | 14 +- 6 files changed, 405 insertions(+), 152 deletions(-) create mode 100644 web/src/components/settings/PaymentSetting.js create mode 100644 web/src/pages/Setting/Payment/SettingsGeneralPayment.js create mode 100644 web/src/pages/Setting/Payment/SettingsPaymentGateway.js diff --git a/web/src/components/settings/PaymentSetting.js b/web/src/components/settings/PaymentSetting.js new file mode 100644 index 00000000..91a40a2b --- /dev/null +++ b/web/src/components/settings/PaymentSetting.js @@ -0,0 +1,88 @@ +import React, { useEffect, useState } from 'react'; +import { Card, Spin } from '@douyinfe/semi-ui'; +import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment.js'; +import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway.js'; +import { API, showError } from '../../helpers'; +import { useTranslation } from 'react-i18next'; + +const PaymentSetting = () => { + const { t } = useTranslation(); + let [inputs, setInputs] = useState({ + ServerAddress: '', + PayAddress: '', + EpayId: '', + EpayKey: '', + Price: 7.3, + MinTopUp: 1, + TopupGroupRatio: '', + CustomCallbackAddress: '', + PayMethods: '', + }); + + let [loading, setLoading] = useState(false); + + const getOptions = async () => { + const res = await API.get('/api/option/'); + const { success, message, data } = res.data; + if (success) { + let newInputs = {}; + data.forEach((item) => { + switch (item.key) { + case 'TopupGroupRatio': + try { + newInputs[item.key] = JSON.stringify(JSON.parse(item.value), null, 2); + } catch (error) { + console.error('解析TopupGroupRatio出错:', error); + newInputs[item.key] = item.value; + } + break; + case 'Price': + case 'MinTopUp': + newInputs[item.key] = parseFloat(item.value); + break; + default: + if (item.key.endsWith('Enabled')) { + newInputs[item.key] = item.value === 'true' ? true : false; + } else { + newInputs[item.key] = item.value; + } + break; + } + }); + + setInputs(newInputs); + } else { + showError(t(message)); + } + }; + + async function onRefresh() { + try { + setLoading(true); + await getOptions(); + } catch (error) { + showError(t('刷新失败')); + } finally { + setLoading(false); + } + } + + useEffect(() => { + onRefresh(); + }, []); + + return ( + <> + + + + + + + + + + ); +}; + +export default PaymentSetting; \ No newline at end of file diff --git a/web/src/components/settings/SystemSetting.js b/web/src/components/settings/SystemSetting.js index 1236ef2e..b5829f31 100644 --- a/web/src/components/settings/SystemSetting.js +++ b/web/src/components/settings/SystemSetting.js @@ -17,7 +17,6 @@ import { removeTrailingSlash, showError, showSuccess, - verifyJSON, } from '../../helpers'; import axios from 'axios'; @@ -42,17 +41,9 @@ const SystemSetting = () => { SMTPAccount: '', SMTPFrom: '', SMTPToken: '', - ServerAddress: '', WorkerUrl: '', WorkerValidKey: '', WorkerAllowHttpImageRequestEnabled: '', - EpayId: '', - EpayKey: '', - Price: 7.3, - MinTopUp: 1, - TopupGroupRatio: '', - PayAddress: '', - CustomCallbackAddress: '', Footer: '', WeChatAuthEnabled: '', WeChatServerAddress: '', @@ -73,7 +64,6 @@ const SystemSetting = () => { LinuxDOOAuthEnabled: '', LinuxDOClientId: '', LinuxDOClientSecret: '', - PayMethods: '', }); const [originInputs, setOriginInputs] = useState({}); @@ -200,11 +190,6 @@ const SystemSetting = () => { setInputs(values); }; - const submitServerAddress = async () => { - let ServerAddress = removeTrailingSlash(inputs.ServerAddress); - await updateOptions([{ key: 'ServerAddress', value: ServerAddress }]); - }; - const submitWorker = async () => { let WorkerUrl = removeTrailingSlash(inputs.WorkerUrl); const options = [ @@ -220,56 +205,6 @@ const SystemSetting = () => { await updateOptions(options); }; - const submitPayAddress = async () => { - if (inputs.ServerAddress === '') { - showError('请先填写服务器地址'); - return; - } - if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { - if (!verifyJSON(inputs.TopupGroupRatio)) { - showError('充值分组倍率不是合法的 JSON 字符串'); - return; - } - } - if (originInputs['PayMethods'] !== inputs.PayMethods) { - if (!verifyJSON(inputs.PayMethods)) { - showError('充值方式设置不是合法的 JSON 字符串'); - return; - } - } - - const options = [ - { key: 'PayAddress', value: removeTrailingSlash(inputs.PayAddress) }, - ]; - - if (inputs.EpayId !== '') { - options.push({ key: 'EpayId', value: inputs.EpayId }); - } - if (inputs.EpayKey !== undefined && inputs.EpayKey !== '') { - options.push({ key: 'EpayKey', value: inputs.EpayKey }); - } - if (inputs.Price !== '') { - options.push({ key: 'Price', value: inputs.Price.toString() }); - } - if (inputs.MinTopUp !== '') { - options.push({ key: 'MinTopUp', value: inputs.MinTopUp.toString() }); - } - if (inputs.CustomCallbackAddress !== '') { - options.push({ - key: 'CustomCallbackAddress', - value: inputs.CustomCallbackAddress, - }); - } - if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { - options.push({ key: 'TopupGroupRatio', value: inputs.TopupGroupRatio }); - } - if (originInputs['PayMethods'] !== inputs.PayMethods) { - options.push({ key: 'PayMethods', value: inputs.PayMethods }); - } - - await updateOptions(options); - }; - const submitSMTP = async () => { const options = []; @@ -551,17 +486,6 @@ const SystemSetting = () => { marginTop: '10px', }} > - - - - - - @@ -604,80 +528,6 @@ const SystemSetting = () => { - - - - (当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { + if (props.options && formApiRef.current) { + const currentInputs = { ServerAddress: props.options.ServerAddress || '' }; + setInputs(currentInputs); + formApiRef.current.setValues(currentInputs); + } + }, [props.options]); + + const handleFormChange = (values) => { + setInputs(values); + }; + + const submitServerAddress = async () => { + setLoading(true); + try { + let ServerAddress = removeTrailingSlash(inputs.ServerAddress); + const res = await API.put('/api/option/', { + key: 'ServerAddress', + value: ServerAddress, + }); + if (res.data.success) { + showSuccess(t('更新成功')); + props.refresh && props.refresh(); + } else { + showError(res.data.message); + } + } catch (error) { + showError(t('更新失败')); + } + setLoading(false); + }; + + return ( + +
(formApiRef.current = api)} + > + + + + +
+
+ ); +} \ No newline at end of file diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGateway.js b/web/src/pages/Setting/Payment/SettingsPaymentGateway.js new file mode 100644 index 00000000..0bb63b53 --- /dev/null +++ b/web/src/pages/Setting/Payment/SettingsPaymentGateway.js @@ -0,0 +1,218 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { + Button, + Form, + Row, + Col, + Typography, + Spin, +} from '@douyinfe/semi-ui'; +const { Text } = Typography; +import { + API, + removeTrailingSlash, + showError, + showSuccess, + verifyJSON, +} from '../../../helpers'; +import { useTranslation } from 'react-i18next'; + +export default function SettingsPaymentGateway(props) { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); + const [inputs, setInputs] = useState({ + PayAddress: '', + EpayId: '', + EpayKey: '', + Price: 7.3, + MinTopUp: 1, + TopupGroupRatio: '', + CustomCallbackAddress: '', + PayMethods: '', + }); + const [originInputs, setOriginInputs] = useState({}); + const formApiRef = useRef(null); + + useEffect(() => { + if (props.options && formApiRef.current) { + const currentInputs = { + PayAddress: props.options.PayAddress || '', + EpayId: props.options.EpayId || '', + EpayKey: props.options.EpayKey || '', + Price: props.options.Price !== undefined ? parseFloat(props.options.Price) : 7.3, + MinTopUp: props.options.MinTopUp !== undefined ? parseFloat(props.options.MinTopUp) : 1, + TopupGroupRatio: props.options.TopupGroupRatio || '', + CustomCallbackAddress: props.options.CustomCallbackAddress || '', + PayMethods: props.options.PayMethods || '', + }; + setInputs(currentInputs); + setOriginInputs({ ...currentInputs }); + formApiRef.current.setValues(currentInputs); + } + }, [props.options]); + + const handleFormChange = (values) => { + setInputs(values); + }; + + const submitPayAddress = async () => { + if (props.options.ServerAddress === '') { + showError(t('请先填写服务器地址')); + return; + } + + if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { + if (!verifyJSON(inputs.TopupGroupRatio)) { + showError(t('充值分组倍率不是合法的 JSON 字符串')); + return; + } + } + + if (originInputs['PayMethods'] !== inputs.PayMethods) { + if (!verifyJSON(inputs.PayMethods)) { + showError(t('充值方式设置不是合法的 JSON 字符串')); + return; + } + } + + setLoading(true); + try { + const options = [ + { key: 'PayAddress', value: removeTrailingSlash(inputs.PayAddress) }, + ]; + + if (inputs.EpayId !== '') { + options.push({ key: 'EpayId', value: inputs.EpayId }); + } + if (inputs.EpayKey !== undefined && inputs.EpayKey !== '') { + options.push({ key: 'EpayKey', value: inputs.EpayKey }); + } + if (inputs.Price !== '') { + options.push({ key: 'Price', value: inputs.Price.toString() }); + } + if (inputs.MinTopUp !== '') { + options.push({ key: 'MinTopUp', value: inputs.MinTopUp.toString() }); + } + if (inputs.CustomCallbackAddress !== '') { + options.push({ + key: 'CustomCallbackAddress', + value: inputs.CustomCallbackAddress, + }); + } + if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { + options.push({ key: 'TopupGroupRatio', value: inputs.TopupGroupRatio }); + } + if (originInputs['PayMethods'] !== inputs.PayMethods) { + options.push({ key: 'PayMethods', value: inputs.PayMethods }); + } + + // 发送请求 + const requestQueue = options.map(opt => + API.put('/api/option/', { + key: opt.key, + value: opt.value, + }) + ); + + const results = await Promise.all(requestQueue); + + // 检查所有请求是否成功 + const errorResults = results.filter(res => !res.data.success); + if (errorResults.length > 0) { + errorResults.forEach(res => { + showError(res.data.message); + }); + } else { + showSuccess(t('更新成功')); + // 更新本地存储的原始值 + setOriginInputs({ ...inputs }); + props.refresh && props.refresh(); + } + } catch (error) { + showError(t('更新失败')); + } + setLoading(false); + }; + + return ( + +
(formApiRef.current = api)} + > + + + {t('(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)')} + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ ); +} \ No newline at end of file diff --git a/web/src/pages/Setting/index.js b/web/src/pages/Setting/index.js index 09b9acdf..d0ae763b 100644 --- a/web/src/pages/Setting/index.js +++ b/web/src/pages/Setting/index.js @@ -11,7 +11,8 @@ import { MoreHorizontal, LayoutDashboard, MessageSquare, - Palette + Palette, + CreditCard } from 'lucide-react'; import SystemSetting from '../../components/settings/SystemSetting.js'; @@ -24,6 +25,7 @@ import DashboardSetting from '../../components/settings/DashboardSetting.js'; import RatioSetting from '../../components/settings/RatioSetting.js'; import ChatsSetting from '../../components/settings/ChatsSetting.js'; import DrawingSetting from '../../components/settings/DrawingSetting.js'; +import PaymentSetting from '../../components/settings/PaymentSetting.js'; const Setting = () => { const { t } = useTranslation(); @@ -63,6 +65,16 @@ const Setting = () => { content: , itemKey: 'drawing', }); + panes.push({ + tab: ( + + + {t('支付设置')} + + ), + content: , + itemKey: 'payment', + }); panes.push({ tab: (