Files
new-api/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx
2025-10-28 23:25:43 +08:00

269 lines
9.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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, Col, Form, Row, Spin } from '@douyinfe/semi-ui';
import {
compareObjects,
API,
showError,
showSuccess,
showWarning,
verifyJSON,
} from '../../../helpers';
import { useTranslation } from 'react-i18next';
export default function GroupRatioSettings(props) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [inputs, setInputs] = useState({
GroupRatio: '',
UserUsableGroups: '',
GroupGroupRatio: '',
'group_ratio_setting.group_special_usable_group': '',
AutoGroups: '',
DefaultUseAutoGroup: false,
});
const refForm = useRef();
const [inputsRow, setInputsRow] = useState(inputs);
async function onSubmit() {
try {
await refForm.current
.validate()
.then(() => {
const updateArray = compareObjects(inputs, inputsRow);
if (!updateArray.length)
return showWarning(t('你似乎并没有修改什么'));
const requestQueue = updateArray.map((item) => {
const value =
typeof inputs[item.key] === 'boolean'
? String(inputs[item.key])
: inputs[item.key];
return API.put('/api/option/', { key: item.key, value });
});
setLoading(true);
Promise.all(requestQueue)
.then((res) => {
if (res.includes(undefined)) {
return showError(
requestQueue.length > 1
? t('部分保存失败,请重试')
: t('保存失败'),
);
}
for (let i = 0; i < res.length; i++) {
if (!res[i].data.success) {
return showError(res[i].data.message);
}
}
showSuccess(t('保存成功'));
props.refresh();
})
.catch((error) => {
console.error('Unexpected error:', error);
showError(t('保存失败,请重试'));
})
.finally(() => {
setLoading(false);
});
})
.catch(() => {
showError(t('请检查输入'));
});
} catch (error) {
showError(t('请检查输入'));
console.error(error);
}
}
useEffect(() => {
const currentInputs = {};
for (let key in props.options) {
if (Object.keys(inputs).includes(key)) {
currentInputs[key] = props.options[key];
}
}
setInputs(currentInputs);
setInputsRow(structuredClone(currentInputs));
refForm.current.setValues(currentInputs);
}, [props.options]);
return (
<Spin spinning={loading}>
<Form
values={inputs}
getFormApi={(formAPI) => (refForm.current = formAPI)}
style={{ marginBottom: 15 }}
>
<Row gutter={16}>
<Col xs={24} sm={16}>
<Form.TextArea
label={t('分组倍率')}
placeholder={t('为一个 JSON 文本,键为分组名称,值为倍率')}
extraText={t(
'分组倍率设置,可以在此处新增分组或修改现有分组的倍率,格式为 JSON 字符串,例如:{"vip": 0.5, "test": 1},表示 vip 分组的倍率为 0.5test 分组的倍率为 1',
)}
field={'GroupRatio'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
stopValidateWithError
rules={[
{
validator: (rule, value) => verifyJSON(value),
message: t('不是合法的 JSON 字符串'),
},
]}
onChange={(value) => setInputs({ ...inputs, GroupRatio: value })}
/>
</Col>
</Row>
<Row gutter={16}>
<Col xs={24} sm={16}>
<Form.TextArea
label={t('用户可选分组')}
placeholder={t('为一个 JSON 文本,键为分组名称,值为分组描述')}
extraText={t(
'用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{"vip": "VIP 用户", "test": "测试"},表示用户可以选择 vip 分组和 test 分组',
)}
field={'UserUsableGroups'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
stopValidateWithError
rules={[
{
validator: (rule, value) => verifyJSON(value),
message: t('不是合法的 JSON 字符串'),
},
]}
onChange={(value) =>
setInputs({ ...inputs, UserUsableGroups: value })
}
/>
</Col>
</Row>
<Row gutter={16}>
<Col xs={24} sm={16}>
<Form.TextArea
label={t('分组特殊倍率')}
placeholder={t('为一个 JSON 文本')}
extraText={t(
'键为分组名称,值为另一个 JSON 对象,键为分组名称,值为该分组的用户的特殊分组倍率,例如:{"vip": {"default": 0.5, "test": 1}},表示 vip 分组的用户在使用default分组的令牌时倍率为0.5使用test分组时倍率为1',
)}
field={'GroupGroupRatio'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
stopValidateWithError
rules={[
{
validator: (rule, value) => verifyJSON(value),
message: t('不是合法的 JSON 字符串'),
},
]}
onChange={(value) =>
setInputs({ ...inputs, GroupGroupRatio: value })
}
/>
</Col>
</Row>
<Row gutter={16}>
<Col xs={24} sm={16}>
<Form.TextArea
label={t('分组特殊可用分组')}
placeholder={t('为一个 JSON 文本')}
extraText={t(
'键为用户分组名称,值为操作映射对象。内层键以"+:"开头表示添加指定分组(键值为分组名称,值为描述),以"-:"开头表示移除指定分组(键值为分组名称),不带前缀的键直接添加该分组。例如:{"vip": {"+:premium": "高级分组", "special": "特殊分组", "-:default": "默认分组"}},表示 vip 分组的用户可以使用 premium 和 special 分组,同时移除 default 分组的访问权限',
)}
field={'group_ratio_setting.group_special_usable_group'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
stopValidateWithError
rules={[
{
validator: (rule, value) => verifyJSON(value),
message: t('不是合法的 JSON 字符串'),
},
]}
onChange={(value) =>
setInputs({ ...inputs, 'group_ratio_setting.group_special_usable_group': value })
}
/>
</Col>
</Row>
<Row gutter={16}>
<Col xs={24} sm={16}>
<Form.TextArea
label={t('自动分组auto从第一个开始选择')}
placeholder={t('为一个 JSON 文本')}
field={'AutoGroups'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
stopValidateWithError
rules={[
{
validator: (rule, value) => {
if (!value || value.trim() === '') {
return true; // Allow empty values
}
// First check if it's valid JSON
try {
const parsed = JSON.parse(value);
// Check if it's an array
if (!Array.isArray(parsed)) {
return false;
}
// Check if every element is a string
return parsed.every((item) => typeof item === 'string');
} catch (error) {
return false;
}
},
message: t('必须是有效的 JSON 字符串数组,例如:["g1","g2"]'),
},
]}
onChange={(value) => setInputs({ ...inputs, AutoGroups: value })}
/>
</Col>
</Row>
<Row gutter={16}>
<Col span={16}>
<Form.Switch
label={t(
'创建令牌默认选择auto分组初始令牌也将设为auto否则留空为用户默认分组',
)}
field={'DefaultUseAutoGroup'}
onChange={(value) =>
setInputs({ ...inputs, DefaultUseAutoGroup: value })
}
/>
</Col>
</Row>
</Form>
<Button onClick={onSubmit}>{t('保存分组倍率设置')}</Button>
</Spin>
);
}