| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| import React, { useState, useRef, useEffect } from 'react';
|
| import JSONEditor from '../../../common/ui/JSONEditor';
|
| import {
|
| SideSheet,
|
| Button,
|
| Form,
|
| Typography,
|
| Space,
|
| Tag,
|
| Row,
|
| Col,
|
| Card,
|
| Avatar,
|
| Spin,
|
| } from '@douyinfe/semi-ui';
|
| import { IconLayers, IconSave, IconClose } from '@douyinfe/semi-icons';
|
| import { API, showError, showSuccess } from '../../../../helpers';
|
| import { useTranslation } from 'react-i18next';
|
| import { useIsMobile } from '../../../../hooks/common/useIsMobile';
|
|
|
| const { Text, Title } = Typography;
|
|
|
|
|
| const ENDPOINT_TEMPLATE = {
|
| openai: { path: '/v1/chat/completions', method: 'POST' },
|
| 'openai-response': { path: '/v1/responses', method: 'POST' },
|
| 'openai-response-compact': { path: '/v1/responses/compact', method: 'POST' },
|
| anthropic: { path: '/v1/messages', method: 'POST' },
|
| gemini: { path: '/v1beta/models/{model}:generateContent', method: 'POST' },
|
| 'jina-rerank': { path: '/v1/rerank', method: 'POST' },
|
| 'image-generation': { path: '/v1/images/generations', method: 'POST' },
|
| };
|
|
|
| const EditPrefillGroupModal = ({
|
| visible,
|
| onClose,
|
| editingGroup,
|
| onSuccess,
|
| }) => {
|
| const { t } = useTranslation();
|
| const isMobile = useIsMobile();
|
| const [loading, setLoading] = useState(false);
|
| const formRef = useRef(null);
|
| const isEdit = editingGroup && editingGroup.id !== undefined;
|
|
|
| const [selectedType, setSelectedType] = useState(editingGroup?.type || 'tag');
|
|
|
|
|
| useEffect(() => {
|
| setSelectedType(editingGroup?.type || 'tag');
|
| }, [editingGroup?.type]);
|
|
|
| const typeOptions = [
|
| { label: t('模型组'), value: 'model' },
|
| { label: t('标签组'), value: 'tag' },
|
| { label: t('端点组'), value: 'endpoint' },
|
| ];
|
|
|
|
|
| const handleSubmit = async (values) => {
|
| setLoading(true);
|
| try {
|
| const submitData = {
|
| ...values,
|
| };
|
| if (values.type === 'endpoint') {
|
| submitData.items = values.items || '';
|
| } else {
|
| submitData.items = Array.isArray(values.items) ? values.items : [];
|
| }
|
|
|
| if (editingGroup.id) {
|
| submitData.id = editingGroup.id;
|
| const res = await API.put('/api/prefill_group', submitData);
|
| if (res.data.success) {
|
| showSuccess(t('更新成功'));
|
| onSuccess();
|
| } else {
|
| showError(res.data.message || t('更新失败'));
|
| }
|
| } else {
|
| const res = await API.post('/api/prefill_group', submitData);
|
| if (res.data.success) {
|
| showSuccess(t('创建成功'));
|
| onSuccess();
|
| } else {
|
| showError(res.data.message || t('创建失败'));
|
| }
|
| }
|
| } catch (error) {
|
| showError(t('操作失败'));
|
| }
|
| setLoading(false);
|
| };
|
|
|
| return (
|
| <SideSheet
|
| placement='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>
|
| </Space>
|
| }
|
| visible={visible}
|
| onCancel={onClose}
|
| width={isMobile ? '100%' : 600}
|
| bodyStyle={{ padding: '0' }}
|
| footer={
|
| <div className='flex justify-end bg-white'>
|
| <Space>
|
| <Button
|
| theme='solid'
|
| className='!rounded-lg'
|
| onClick={() => formRef.current?.submitForm()}
|
| icon={<IconSave />}
|
| loading={loading}
|
| >
|
| {t('提交')}
|
| </Button>
|
| <Button
|
| theme='light'
|
| className='!rounded-lg'
|
| type='primary'
|
| onClick={onClose}
|
| icon={<IconClose />}
|
| >
|
| {t('取消')}
|
| </Button>
|
| </Space>
|
| </div>
|
| }
|
| closeIcon={null}
|
| >
|
| <Spin spinning={loading}>
|
| <Form
|
| getFormApi={(api) => (formRef.current = api)}
|
| initValues={{
|
| name: editingGroup?.name || '',
|
| type: editingGroup?.type || 'tag',
|
| description: editingGroup?.description || '',
|
| items: (() => {
|
| try {
|
| if (editingGroup?.type === 'endpoint') {
|
| // 保持原始字符串
|
| return typeof editingGroup?.items === 'string'
|
| ? editingGroup.items
|
| : JSON.stringify(editingGroup.items || {}, null, 2);
|
| }
|
| return Array.isArray(editingGroup?.items)
|
| ? editingGroup.items
|
| : [];
|
| } catch {
|
| return editingGroup?.type === 'endpoint' ? '' : [];
|
| }
|
| })(),
|
| }}
|
| onSubmit={handleSubmit}
|
| >
|
| <div className='p-2'>
|
| {/* 基本信息 */}
|
| <Card className='!rounded-2xl shadow-sm border-0'>
|
| <div className='flex items-center mb-2'>
|
| <Avatar size='small' color='green' className='mr-2 shadow-md'>
|
| <IconLayers size={16} />
|
| </Avatar>
|
| <div>
|
| <Text className='text-lg font-medium'>{t('基本信息')}</Text>
|
| <div className='text-xs text-gray-600'>
|
| {t('设置预填组的基本信息')}
|
| </div>
|
| </div>
|
| </div>
|
| <Row gutter={12}>
|
| <Col span={24}>
|
| <Form.Input
|
| field='name'
|
| label={t('组名')}
|
| placeholder={t('请输入组名')}
|
| rules={[{ required: true, message: t('请输入组名') }]}
|
| showClear
|
| />
|
| </Col>
|
| <Col span={24}>
|
| <Form.Select
|
| field='type'
|
| label={t('类型')}
|
| placeholder={t('选择组类型')}
|
| optionList={typeOptions}
|
| rules={[{ required: true, message: t('请选择组类型') }]}
|
| style={{ width: '100%' }}
|
| onChange={(val) => setSelectedType(val)}
|
| />
|
| </Col>
|
| <Col span={24}>
|
| <Form.TextArea
|
| field='description'
|
| label={t('描述')}
|
| placeholder={t('请输入组描述')}
|
| rows={3}
|
| showClear
|
| />
|
| </Col>
|
| <Col span={24}>
|
| {selectedType === 'endpoint' ? (
|
| <JSONEditor
|
| field='items'
|
| label={t('端点映射')}
|
| value={
|
| formRef.current?.getValue('items') ??
|
| (typeof editingGroup?.items === 'string'
|
| ? editingGroup.items
|
| : JSON.stringify(editingGroup.items || {}, null, 2))
|
| }
|
| onChange={(val) =>
|
| formRef.current?.setValue('items', val)
|
| }
|
| editorType='object'
|
| placeholder={
|
| '{\n "openai": {"path": "/v1/chat/completions", "method": "POST"}\n}'
|
| }
|
| template={ENDPOINT_TEMPLATE}
|
| templateLabel={t('填入模板')}
|
| extraText={t('键为端点类型,值为路径和方法对象')}
|
| />
|
| ) : (
|
| <Form.TagInput
|
| field='items'
|
| label={t('项目')}
|
| placeholder={t('输入项目名称,按回车添加')}
|
| addOnBlur
|
| showClear
|
| style={{ width: '100%' }}
|
| />
|
| )}
|
| </Col>
|
| </Row>
|
| </Card>
|
| </div>
|
| </Form>
|
| </Spin>
|
| </SideSheet>
|
| );
|
| };
|
|
|
| export default EditPrefillGroupModal;
|
|
|