|
- import React, { useEffect, useState, useRef } from 'react';
- import { useTranslation } from 'react-i18next';
- import { userRateLimitApi, showError, showSuccess } from '../../../../helpers';
- import {
- Button,
- Table,
- Modal,
- Form,
- Avatar,
- Typography,
- Popconfirm,
- } from '@douyinfe/semi-ui';
- import { IconDelete, IconPlus, IconClock } from '@douyinfe/semi-icons';
-
- const { Text } = Typography;
-
- const UserModelRateLimitSection = ({ userId }) => {
- const { t } = useTranslation();
- const [limits, setLimits] = useState([]);
- const [loading, setLoading] = useState(false);
- const [addModalVisible, setAddModalVisible] = useState(false);
- const formApiRef = useRef(null);
-
- const loadLimits = async () => {
- if (!userId) return;
- setLoading(true);
- try {
- const res = await userRateLimitApi.get(userId);
- const { success, data, message } = res.data;
- if (success) {
- setLimits(data || []);
- } else {
- showError(message);
- }
- } catch (e) {
- showError(e.message);
- }
- setLoading(false);
- };
-
- useEffect(() => {
- loadLimits();
- }, [userId]);
-
- const saveLimits = async (newLimits) => {
- try {
- const res = await userRateLimitApi.set(userId, newLimits);
- const { success, message } = res.data;
- if (success) {
- showSuccess(t('保存成功'));
- return true;
- } else {
- showError(message);
- return false;
- }
- } catch (e) {
- showError(e.message);
- return false;
- }
- };
-
- const handleAdd = async (values) => {
- const rpm = parseInt(values.rpm) || 0;
- const newItem = { model: values.model, rpm };
- const filtered = limits.filter((item) => item.model !== values.model);
- const newLimits = [...filtered, newItem];
- const ok = await saveLimits(newLimits);
- if (ok) {
- setLimits(newLimits);
- setAddModalVisible(false);
- formApiRef.current?.reset();
- }
- };
-
- const handleDelete = async (model) => {
- const newLimits = limits.filter((item) => item.model !== model);
- const ok = await saveLimits(newLimits);
- if (ok) {
- setLimits(newLimits);
- }
- };
-
- const columns = [
- {
- title: t('模型'),
- dataIndex: 'model',
- key: 'model',
- },
- {
- title: t('RPM 限制'),
- dataIndex: 'rpm',
- key: 'rpm',
- render: (rpm) =>
- rpm === 0 ? (
- <Text type='tertiary'>{t('不限制')}</Text>
- ) : (
- <Text>{rpm}</Text>
- ),
- },
- {
- title: t('操作'),
- key: 'action',
- render: (_, record) => (
- <Popconfirm
- title={t('确认删除该限制?')}
- onConfirm={() => handleDelete(record.model)}
- >
- <Button
- type='danger'
- size='small'
- icon={<IconDelete />}
- >
- {t('删除')}
- </Button>
- </Popconfirm>
- ),
- },
- ];
-
- return (
- <>
- <div className='flex items-center justify-between mb-2'>
- <div className='flex items-center'>
- <Avatar size='small' color='cyan' className='mr-2 shadow-md'>
- <IconClock size={16} />
- </Avatar>
- <div>
- <Text className='text-lg font-medium'>
- {t('模型速率限制(RPM)')}
- </Text>
- <div className='text-xs text-gray-600'>
- {t('为该用户配置各模型每分钟请求数上限,0 表示不限制')}
- </div>
- </div>
- </div>
- <Button
- size='small'
- icon={<IconPlus />}
- onClick={() => setAddModalVisible(true)}
- >
- {t('添加限制')}
- </Button>
- </div>
-
- <Table
- columns={columns}
- dataSource={limits}
- loading={loading}
- rowKey='model'
- size='small'
- pagination={false}
- empty={t('暂无速率限制')}
- />
-
- <Modal
- title={t('添加模型速率限制')}
- visible={addModalVisible}
- onOk={() => formApiRef.current?.submitForm()}
- onCancel={() => {
- setAddModalVisible(false);
- formApiRef.current?.reset();
- }}
- >
- <Form
- getFormApi={(api) => (formApiRef.current = api)}
- onSubmit={handleAdd}
- >
- <Form.Input
- field='model'
- label={t('模型名称')}
- placeholder={t('请输入精确的模型名,如 gpt-4o')}
- rules={[{ required: true, message: t('请输入模型名称') }]}
- style={{ width: '100%' }}
- />
- <Form.InputNumber
- field='rpm'
- label={t('RPM 限制')}
- placeholder='60'
- initValue={60}
- min={0}
- step={10}
- extraText={t('0 表示不限制')}
- rules={[{ required: true, message: t('请输入 RPM 限制') }]}
- style={{ width: '100%' }}
- />
- </Form>
- </Modal>
- </>
- );
- };
-
- export default UserModelRateLimitSection;
|