♻️ refactor(web): migrate React modules from .js to .jsx and align entrypoint

- Rename React components/pages/utilities that contain JSX to `.jsx` across `web/src`
- Update import paths and re-exports to match new `.jsx` extensions
- Fix Vite entry by switching `web/index.html` from `/src/index.js` to `/src/index.jsx`
- Verified remaining `.js` files are plain JS (hooks/helpers/constants) and do not require JSX
- No runtime behavior changes; extension and reference alignment only

Context: Resolves the Vite pre-transform error caused by the stale `/src/index.js` entry after migrating to `.jsx`.
This commit is contained in:
t0ng7u
2025-08-18 04:14:35 +08:00
parent cfc6bc8e5e
commit adc7fbd424
173 changed files with 299 additions and 280 deletions

View File

@@ -0,0 +1,237 @@
/*
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 <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
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 (
<Spin spinning={loading}>
<Form
initValues={inputs}
onValueChange={handleFormChange}
getFormApi={(api) => (formApiRef.current = api)}
>
<Form.Section text={t('支付设置')}>
<Text>
{t('(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)')}
</Text>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='PayAddress'
label={t('支付地址')}
placeholder={t('例如https://yourdomain.com')}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='EpayId'
label={t('易支付商户ID')}
placeholder={t('例如0001')}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='EpayKey'
label={t('易支付商户密钥')}
placeholder={t('敏感信息不会发送到前端显示')}
type='password'
/>
</Col>
</Row>
<Row
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
style={{ marginTop: 16 }}
>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Input
field='CustomCallbackAddress'
label={t('回调地址')}
placeholder={t('例如https://yourdomain.com')}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field='Price'
precision={2}
label={t('充值价格x元/美金)')}
placeholder={t('例如7就是7元/美金')}
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.InputNumber
field='MinTopUp'
label={t('最低充值美元数量')}
placeholder={t('例如2就是最低充值2$')}
/>
</Col>
</Row>
<Form.TextArea
field='TopupGroupRatio'
label={t('充值分组倍率')}
placeholder={t('为一个 JSON 文本,键为组名称,值为倍率')}
autosize
/>
<Form.TextArea
field='PayMethods'
label={t('充值方式设置')}
placeholder={t('为一个 JSON 文本')}
autosize
/>
<Button onClick={submitPayAddress}>{t('更新支付设置')}</Button>
</Form.Section>
</Form>
</Spin>
);
}