|
|
@@ -21,12 +21,26 @@ import {
|
|
|
Spin,
|
|
|
TextArea,
|
|
|
Typography,
|
|
|
+ Card,
|
|
|
+ Tag,
|
|
|
} from '@douyinfe/semi-ui';
|
|
|
-import Title from '@douyinfe/semi-ui/lib/es/typography/title';
|
|
|
-import { Divider } from 'semantic-ui-react';
|
|
|
+import {
|
|
|
+ IconClock,
|
|
|
+ IconCalendar,
|
|
|
+ IconCreditCard,
|
|
|
+ IconLink,
|
|
|
+ IconServer,
|
|
|
+ IconUserGroup,
|
|
|
+ IconSave,
|
|
|
+ IconClose,
|
|
|
+ IconPlusCircle,
|
|
|
+} from '@douyinfe/semi-icons';
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
|
|
+const { Text, Title } = Typography;
|
|
|
+
|
|
|
const EditToken = (props) => {
|
|
|
+ const { t } = useTranslation();
|
|
|
const [isEdit, setIsEdit] = useState(false);
|
|
|
const [loading, setLoading] = useState(isEdit);
|
|
|
const originInputs = {
|
|
|
@@ -50,17 +64,18 @@ const EditToken = (props) => {
|
|
|
allow_ips,
|
|
|
group,
|
|
|
} = inputs;
|
|
|
- // const [visible, setVisible] = useState(false);
|
|
|
const [models, setModels] = useState([]);
|
|
|
const [groups, setGroups] = useState([]);
|
|
|
const navigate = useNavigate();
|
|
|
- const { t } = useTranslation();
|
|
|
+
|
|
|
const handleInputChange = (name, value) => {
|
|
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
|
|
};
|
|
|
+
|
|
|
const handleCancel = () => {
|
|
|
props.handleClose();
|
|
|
};
|
|
|
+
|
|
|
const setExpiredTime = (month, day, hour, minute) => {
|
|
|
let now = new Date();
|
|
|
let timestamp = now.getTime() / 1000;
|
|
|
@@ -128,6 +143,7 @@ const EditToken = (props) => {
|
|
|
}
|
|
|
setLoading(false);
|
|
|
};
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
setIsEdit(props.editingToken.id !== undefined);
|
|
|
}, [props.editingToken.id]);
|
|
|
@@ -241,237 +257,312 @@ const EditToken = (props) => {
|
|
|
};
|
|
|
|
|
|
return (
|
|
|
- <>
|
|
|
- <SideSheet
|
|
|
- placement={isEdit ? 'right' : 'left'}
|
|
|
- title={
|
|
|
- <Title level={3}>
|
|
|
+ <SideSheet
|
|
|
+ placement={isEdit ? 'right' : 'left'}
|
|
|
+ title={
|
|
|
+ <Space>
|
|
|
+ {isEdit ?
|
|
|
+ <Tag color="blue" shape="circle">{t('更新')}</Tag> :
|
|
|
+ <Tag color="green" shape="circle">{t('新建')}</Tag>
|
|
|
+ }
|
|
|
+ <Title heading={4} className="m-0">
|
|
|
{isEdit ? t('更新令牌信息') : t('创建新的令牌')}
|
|
|
</Title>
|
|
|
- }
|
|
|
- headerStyle={{ borderBottom: '1px solid var(--semi-color-border)' }}
|
|
|
- bodyStyle={{ borderBottom: '1px solid var(--semi-color-border)' }}
|
|
|
- visible={props.visiable}
|
|
|
- footer={
|
|
|
- <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
|
|
- <Space>
|
|
|
- <Button theme='solid' size={'large'} onClick={submit}>
|
|
|
- {t('提交')}
|
|
|
- </Button>
|
|
|
- <Button
|
|
|
- theme='solid'
|
|
|
- size={'large'}
|
|
|
- type={'tertiary'}
|
|
|
- onClick={handleCancel}
|
|
|
- >
|
|
|
- {t('取消')}
|
|
|
- </Button>
|
|
|
- </Space>
|
|
|
- </div>
|
|
|
- }
|
|
|
- closeIcon={null}
|
|
|
- onCancel={() => handleCancel()}
|
|
|
- width={isMobile() ? '100%' : 600}
|
|
|
- >
|
|
|
- <Spin spinning={loading}>
|
|
|
- <Input
|
|
|
- style={{ marginTop: 20 }}
|
|
|
- label={t('名称')}
|
|
|
- name='name'
|
|
|
- placeholder={t('请输入名称')}
|
|
|
- onChange={(value) => handleInputChange('name', value)}
|
|
|
- value={name}
|
|
|
- autoComplete='new-password'
|
|
|
- required={!isEdit}
|
|
|
- />
|
|
|
- <Divider />
|
|
|
- <DatePicker
|
|
|
- label={t('过期时间')}
|
|
|
- name='expired_time'
|
|
|
- placeholder={t('请选择过期时间')}
|
|
|
- onChange={(value) => handleInputChange('expired_time', value)}
|
|
|
- value={expired_time}
|
|
|
- autoComplete='new-password'
|
|
|
- type='dateTime'
|
|
|
- />
|
|
|
- <div style={{ marginTop: 20 }}>
|
|
|
- <Space>
|
|
|
- <Button
|
|
|
- type={'tertiary'}
|
|
|
- onClick={() => {
|
|
|
- setExpiredTime(0, 0, 0, 0);
|
|
|
- }}
|
|
|
- >
|
|
|
- {t('永不过期')}
|
|
|
- </Button>
|
|
|
- <Button
|
|
|
- type={'tertiary'}
|
|
|
- onClick={() => {
|
|
|
- setExpiredTime(0, 0, 1, 0);
|
|
|
- }}
|
|
|
- >
|
|
|
- {t('一小时')}
|
|
|
- </Button>
|
|
|
- <Button
|
|
|
- type={'tertiary'}
|
|
|
- onClick={() => {
|
|
|
- setExpiredTime(1, 0, 0, 0);
|
|
|
- }}
|
|
|
- >
|
|
|
- {t('一个月')}
|
|
|
- </Button>
|
|
|
- <Button
|
|
|
- type={'tertiary'}
|
|
|
- onClick={() => {
|
|
|
- setExpiredTime(0, 1, 0, 0);
|
|
|
- }}
|
|
|
- >
|
|
|
- {t('一天')}
|
|
|
- </Button>
|
|
|
- </Space>
|
|
|
- </div>
|
|
|
-
|
|
|
- <Divider />
|
|
|
- <Banner
|
|
|
- type={'warning'}
|
|
|
- description={t(
|
|
|
- '注意,令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制。',
|
|
|
- )}
|
|
|
- ></Banner>
|
|
|
- <div style={{ marginTop: 20 }}>
|
|
|
- <Typography.Text>{`${t('额度')}${renderQuotaWithPrompt(remain_quota)}`}</Typography.Text>
|
|
|
- </div>
|
|
|
- <AutoComplete
|
|
|
- style={{ marginTop: 8 }}
|
|
|
- name='remain_quota'
|
|
|
- placeholder={t('请输入额度')}
|
|
|
- onChange={(value) => handleInputChange('remain_quota', value)}
|
|
|
- value={remain_quota}
|
|
|
- autoComplete='new-password'
|
|
|
- type='number'
|
|
|
- // position={'top'}
|
|
|
- data={[
|
|
|
- { value: 500000, label: '1$' },
|
|
|
- { value: 5000000, label: '10$' },
|
|
|
- { value: 25000000, label: '50$' },
|
|
|
- { value: 50000000, label: '100$' },
|
|
|
- { value: 250000000, label: '500$' },
|
|
|
- { value: 500000000, label: '1000$' },
|
|
|
- ]}
|
|
|
- disabled={unlimited_quota}
|
|
|
- />
|
|
|
-
|
|
|
- {!isEdit && (
|
|
|
- <>
|
|
|
- <div style={{ marginTop: 20 }}>
|
|
|
- <Typography.Text>{t('新建数量')}</Typography.Text>
|
|
|
- </div>
|
|
|
- <AutoComplete
|
|
|
- style={{ marginTop: 8 }}
|
|
|
- label={t('数量')}
|
|
|
- placeholder={t('请选择或输入创建令牌的数量')}
|
|
|
- onChange={(value) => handleTokenCountChange(value)}
|
|
|
- onSelect={(value) => handleTokenCountChange(value)}
|
|
|
- value={tokenCount.toString()}
|
|
|
- autoComplete='off'
|
|
|
- type='number'
|
|
|
- data={[
|
|
|
- { value: 10, label: t('10个') },
|
|
|
- { value: 20, label: t('20个') },
|
|
|
- { value: 30, label: t('30个') },
|
|
|
- { value: 100, label: t('100个') },
|
|
|
- ]}
|
|
|
- disabled={unlimited_quota}
|
|
|
- />
|
|
|
- </>
|
|
|
- )}
|
|
|
-
|
|
|
- <div>
|
|
|
+ </Space>
|
|
|
+ }
|
|
|
+ headerStyle={{
|
|
|
+ borderBottom: '1px solid var(--semi-color-border)',
|
|
|
+ padding: '24px'
|
|
|
+ }}
|
|
|
+ bodyStyle={{
|
|
|
+ backgroundColor: 'var(--semi-color-bg-0)',
|
|
|
+ padding: '0'
|
|
|
+ }}
|
|
|
+ visible={props.visiable}
|
|
|
+ width={isMobile() ? '100%' : 600}
|
|
|
+ footer={
|
|
|
+ <div className="flex justify-end bg-white">
|
|
|
+ <Space>
|
|
|
<Button
|
|
|
- style={{ marginTop: 8 }}
|
|
|
- type={'warning'}
|
|
|
- onClick={() => {
|
|
|
- setUnlimitedQuota();
|
|
|
- }}
|
|
|
+ theme="solid"
|
|
|
+ size="large"
|
|
|
+ className="!rounded-full"
|
|
|
+ onClick={submit}
|
|
|
+ icon={<IconSave />}
|
|
|
+ loading={loading}
|
|
|
>
|
|
|
- {unlimited_quota ? t('取消无限额度') : t('设为无限额度')}
|
|
|
+ {t('提交')}
|
|
|
</Button>
|
|
|
- </div>
|
|
|
- <Divider />
|
|
|
- <div style={{ marginTop: 10 }}>
|
|
|
- <Typography.Text>
|
|
|
- {t('IP白名单(请勿过度信任此功能)')}
|
|
|
- </Typography.Text>
|
|
|
- </div>
|
|
|
- <TextArea
|
|
|
- label={t('IP白名单')}
|
|
|
- name='allow_ips'
|
|
|
- placeholder={t('允许的IP,一行一个,不填写则不限制')}
|
|
|
- onChange={(value) => {
|
|
|
- handleInputChange('allow_ips', value);
|
|
|
- }}
|
|
|
- value={inputs.allow_ips}
|
|
|
- style={{ fontFamily: 'JetBrains Mono, Consolas' }}
|
|
|
- />
|
|
|
- <div style={{ marginTop: 10, display: 'flex' }}>
|
|
|
- <Space>
|
|
|
- <Checkbox
|
|
|
- name='model_limits_enabled'
|
|
|
- checked={model_limits_enabled}
|
|
|
- onChange={(e) =>
|
|
|
- handleInputChange('model_limits_enabled', e.target.checked)
|
|
|
- }
|
|
|
- >
|
|
|
- {t('启用模型限制(非必要,不建议启用)')}
|
|
|
- </Checkbox>
|
|
|
- </Space>
|
|
|
- </div>
|
|
|
-
|
|
|
- <Select
|
|
|
- style={{ marginTop: 8 }}
|
|
|
- placeholder={t('请选择该渠道所支持的模型')}
|
|
|
- name='models'
|
|
|
- required
|
|
|
- multiple
|
|
|
- selection
|
|
|
- onChange={(value) => {
|
|
|
- handleInputChange('model_limits', value);
|
|
|
- }}
|
|
|
- value={inputs.model_limits}
|
|
|
- autoComplete='new-password'
|
|
|
- optionList={models}
|
|
|
- disabled={!model_limits_enabled}
|
|
|
- />
|
|
|
- <div style={{ marginTop: 10 }}>
|
|
|
- <Typography.Text>{t('令牌分组,默认为用户的分组')}</Typography.Text>
|
|
|
- </div>
|
|
|
- {groups.length > 0 ? (
|
|
|
- <Select
|
|
|
- style={{ marginTop: 8 }}
|
|
|
- placeholder={t('令牌分组,默认为用户的分组')}
|
|
|
- name='gruop'
|
|
|
- required
|
|
|
- selection
|
|
|
- onChange={(value) => {
|
|
|
- handleInputChange('group', value);
|
|
|
- }}
|
|
|
- position={'topLeft'}
|
|
|
- renderOptionItem={renderGroupOption}
|
|
|
- value={inputs.group}
|
|
|
- autoComplete='new-password'
|
|
|
- optionList={groups}
|
|
|
- />
|
|
|
- ) : (
|
|
|
- <Select
|
|
|
- style={{ marginTop: 8 }}
|
|
|
- placeholder={t('管理员未设置用户可选分组')}
|
|
|
- name='gruop'
|
|
|
- disabled={true}
|
|
|
+ <Button
|
|
|
+ theme="light"
|
|
|
+ size="large"
|
|
|
+ className="!rounded-full"
|
|
|
+ type="primary"
|
|
|
+ onClick={handleCancel}
|
|
|
+ icon={<IconClose />}
|
|
|
+ >
|
|
|
+ {t('取消')}
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ closeIcon={null}
|
|
|
+ onCancel={() => handleCancel()}
|
|
|
+ >
|
|
|
+ <Spin spinning={loading}>
|
|
|
+ <div className="p-6">
|
|
|
+ <Card className="!rounded-2xl shadow-sm border-0 mb-6">
|
|
|
+ <div className="flex items-center mb-4">
|
|
|
+ <div className="w-10 h-10 rounded-full bg-blue-50 flex items-center justify-center mr-4">
|
|
|
+ <IconPlusCircle size="large" className="text-blue-500" />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <Text className="text-lg font-medium">{t('基本信息')}</Text>
|
|
|
+ <div className="text-gray-500 text-sm">{t('设置令牌的基本信息')}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div>
|
|
|
+ <Text strong className="block mb-2">{t('名称')}</Text>
|
|
|
+ <Input
|
|
|
+ placeholder={t('请输入名称')}
|
|
|
+ onChange={(value) => handleInputChange('name', value)}
|
|
|
+ value={name}
|
|
|
+ autoComplete="new-password"
|
|
|
+ size="large"
|
|
|
+ className="!rounded-lg"
|
|
|
+ showClear
|
|
|
+ required
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <Text strong className="block mb-2">{t('过期时间')}</Text>
|
|
|
+ <div className="mb-2">
|
|
|
+ <DatePicker
|
|
|
+ placeholder={t('请选择过期时间')}
|
|
|
+ onChange={(value) => handleInputChange('expired_time', value)}
|
|
|
+ value={expired_time}
|
|
|
+ autoComplete="new-password"
|
|
|
+ type="dateTime"
|
|
|
+ className="w-full !rounded-lg"
|
|
|
+ size="large"
|
|
|
+ prefix={<IconCalendar />}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="flex flex-wrap gap-2">
|
|
|
+ <Button
|
|
|
+ theme="light"
|
|
|
+ type="primary"
|
|
|
+ onClick={() => setExpiredTime(0, 0, 0, 0)}
|
|
|
+ className="!rounded-full"
|
|
|
+ >
|
|
|
+ {t('永不过期')}
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ theme="light"
|
|
|
+ type="tertiary"
|
|
|
+ onClick={() => setExpiredTime(0, 0, 1, 0)}
|
|
|
+ className="!rounded-full"
|
|
|
+ icon={<IconClock />}
|
|
|
+ >
|
|
|
+ {t('一小时')}
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ theme="light"
|
|
|
+ type="tertiary"
|
|
|
+ onClick={() => setExpiredTime(0, 1, 0, 0)}
|
|
|
+ className="!rounded-full"
|
|
|
+ icon={<IconCalendar />}
|
|
|
+ >
|
|
|
+ {t('一天')}
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ theme="light"
|
|
|
+ type="tertiary"
|
|
|
+ onClick={() => setExpiredTime(1, 0, 0, 0)}
|
|
|
+ className="!rounded-full"
|
|
|
+ icon={<IconCalendar />}
|
|
|
+ >
|
|
|
+ {t('一个月')}
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ <Card className="!rounded-2xl shadow-sm border-0 mb-6">
|
|
|
+ <div className="flex items-center mb-4">
|
|
|
+ <div className="w-10 h-10 rounded-full bg-green-50 flex items-center justify-center mr-4">
|
|
|
+ <IconCreditCard size="large" className="text-green-500" />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <Text className="text-lg font-medium">{t('额度设置')}</Text>
|
|
|
+ <div className="text-gray-500 text-sm">{t('设置令牌可用额度和数量')}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Banner
|
|
|
+ type="warning"
|
|
|
+ description={t('注意,令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制。')}
|
|
|
+ className="mb-4 !rounded-lg"
|
|
|
/>
|
|
|
- )}
|
|
|
- </Spin>
|
|
|
- </SideSheet>
|
|
|
- </>
|
|
|
+
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div>
|
|
|
+ <div className="flex justify-between mb-2">
|
|
|
+ <Text strong>{t('额度')}</Text>
|
|
|
+ <Text type="tertiary">{renderQuotaWithPrompt(remain_quota)}</Text>
|
|
|
+ </div>
|
|
|
+ <AutoComplete
|
|
|
+ placeholder={t('请输入额度')}
|
|
|
+ onChange={(value) => handleInputChange('remain_quota', value)}
|
|
|
+ value={remain_quota}
|
|
|
+ autoComplete="new-password"
|
|
|
+ type="number"
|
|
|
+ size="large"
|
|
|
+ className="w-full !rounded-lg"
|
|
|
+ prefix={<IconCreditCard />}
|
|
|
+ data={[
|
|
|
+ { value: 500000, label: '1$' },
|
|
|
+ { value: 5000000, label: '10$' },
|
|
|
+ { value: 25000000, label: '50$' },
|
|
|
+ { value: 50000000, label: '100$' },
|
|
|
+ { value: 250000000, label: '500$' },
|
|
|
+ { value: 500000000, label: '1000$' },
|
|
|
+ ]}
|
|
|
+ disabled={unlimited_quota}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {!isEdit && (
|
|
|
+ <div>
|
|
|
+ <Text strong className="block mb-2">{t('新建数量')}</Text>
|
|
|
+ <AutoComplete
|
|
|
+ placeholder={t('请选择或输入创建令牌的数量')}
|
|
|
+ onChange={(value) => handleTokenCountChange(value)}
|
|
|
+ onSelect={(value) => handleTokenCountChange(value)}
|
|
|
+ value={tokenCount.toString()}
|
|
|
+ autoComplete="off"
|
|
|
+ type="number"
|
|
|
+ className="w-full !rounded-lg"
|
|
|
+ size="large"
|
|
|
+ prefix={<IconPlusCircle />}
|
|
|
+ data={[
|
|
|
+ { value: 10, label: t('10个') },
|
|
|
+ { value: 20, label: t('20个') },
|
|
|
+ { value: 30, label: t('30个') },
|
|
|
+ { value: 100, label: t('100个') },
|
|
|
+ ]}
|
|
|
+ disabled={unlimited_quota}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <div className="flex justify-end">
|
|
|
+ <Button
|
|
|
+ theme="light"
|
|
|
+ type={unlimited_quota ? "danger" : "warning"}
|
|
|
+ onClick={setUnlimitedQuota}
|
|
|
+ className="!rounded-full"
|
|
|
+ >
|
|
|
+ {unlimited_quota ? t('取消无限额度') : t('设为无限额度')}
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ <Card className="!rounded-2xl shadow-sm border-0 mb-6">
|
|
|
+ <div className="flex items-center mb-4">
|
|
|
+ <div className="w-10 h-10 rounded-full bg-purple-50 flex items-center justify-center mr-4">
|
|
|
+ <IconLink size="large" className="text-purple-500" />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <Text className="text-lg font-medium">{t('访问限制')}</Text>
|
|
|
+ <div className="text-gray-500 text-sm">{t('设置令牌的访问限制')}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div>
|
|
|
+ <Text strong className="block mb-2">{t('IP白名单')}</Text>
|
|
|
+ <TextArea
|
|
|
+ placeholder={t('允许的IP,一行一个,不填写则不限制')}
|
|
|
+ onChange={(value) => handleInputChange('allow_ips', value)}
|
|
|
+ value={inputs.allow_ips}
|
|
|
+ style={{ fontFamily: 'JetBrains Mono, Consolas' }}
|
|
|
+ className="!rounded-lg"
|
|
|
+ rows={4}
|
|
|
+ />
|
|
|
+ <Text type="tertiary" className="mt-1 block text-xs">{t('请勿过度信任此功能,IP可能被伪造')}</Text>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <div className="flex items-center mb-2">
|
|
|
+ <Checkbox
|
|
|
+ checked={model_limits_enabled}
|
|
|
+ onChange={(e) => handleInputChange('model_limits_enabled', e.target.checked)}
|
|
|
+ >
|
|
|
+ <Text strong>{t('模型限制')}</Text>
|
|
|
+ </Checkbox>
|
|
|
+ </div>
|
|
|
+ <Select
|
|
|
+ placeholder={model_limits_enabled ? t('请选择该渠道所支持的模型') : t('勾选启用模型限制后可选择')}
|
|
|
+ onChange={(value) => handleInputChange('model_limits', value)}
|
|
|
+ value={inputs.model_limits}
|
|
|
+ multiple
|
|
|
+ size="large"
|
|
|
+ className="w-full !rounded-lg"
|
|
|
+ prefix={<IconServer />}
|
|
|
+ optionList={models}
|
|
|
+ disabled={!model_limits_enabled}
|
|
|
+ maxTagCount={3}
|
|
|
+ />
|
|
|
+ <Text type="tertiary" className="mt-1 block text-xs">{t('非必要,不建议启用模型限制')}</Text>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+
|
|
|
+ <Card className="!rounded-2xl shadow-sm border-0">
|
|
|
+ <div className="flex items-center mb-4">
|
|
|
+ <div className="w-10 h-10 rounded-full bg-orange-50 flex items-center justify-center mr-4">
|
|
|
+ <IconUserGroup size="large" className="text-orange-500" />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <Text className="text-lg font-medium">{t('分组信息')}</Text>
|
|
|
+ <div className="text-gray-500 text-sm">{t('设置令牌的分组')}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <Text strong className="block mb-2">{t('令牌分组')}</Text>
|
|
|
+ {groups.length > 0 ? (
|
|
|
+ <Select
|
|
|
+ placeholder={t('令牌分组,默认为用户的分组')}
|
|
|
+ onChange={(value) => handleInputChange('group', value)}
|
|
|
+ renderOptionItem={renderGroupOption}
|
|
|
+ value={inputs.group}
|
|
|
+ size="large"
|
|
|
+ className="w-full !rounded-lg"
|
|
|
+ prefix={<IconUserGroup />}
|
|
|
+ optionList={groups}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <Select
|
|
|
+ placeholder={t('管理员未设置用户可选分组')}
|
|
|
+ disabled={true}
|
|
|
+ size="large"
|
|
|
+ className="w-full !rounded-lg"
|
|
|
+ prefix={<IconUserGroup />}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </Card>
|
|
|
+ </div>
|
|
|
+ </Spin>
|
|
|
+ </SideSheet>
|
|
|
);
|
|
|
};
|
|
|
|