| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import React, { useState, useEffect } from 'react'; |
| | import { |
| | SideSheet, |
| | Button, |
| | Typography, |
| | Space, |
| | Tag, |
| | Popconfirm, |
| | Card, |
| | Avatar, |
| | Spin, |
| | Empty, |
| | } from '@douyinfe/semi-ui'; |
| | import { IconPlus, IconLayers } from '@douyinfe/semi-icons'; |
| | import { |
| | IllustrationNoResult, |
| | IllustrationNoResultDark, |
| | } from '@douyinfe/semi-illustrations'; |
| | import { |
| | API, |
| | showError, |
| | showSuccess, |
| | stringToColor, |
| | } from '../../../../helpers'; |
| | import { useTranslation } from 'react-i18next'; |
| | import { useIsMobile } from '../../../../hooks/common/useIsMobile'; |
| | import CardTable from '../../../common/ui/CardTable'; |
| | import EditPrefillGroupModal from './EditPrefillGroupModal'; |
| | import { |
| | renderLimitedItems, |
| | renderDescription, |
| | } from '../../../common/ui/RenderUtils'; |
| |
|
| | const { Text, Title } = Typography; |
| |
|
| | const PrefillGroupManagement = ({ visible, onClose }) => { |
| | const { t } = useTranslation(); |
| | const isMobile = useIsMobile(); |
| | const [loading, setLoading] = useState(false); |
| | const [groups, setGroups] = useState([]); |
| | const [showEdit, setShowEdit] = useState(false); |
| | const [editingGroup, setEditingGroup] = useState({ id: undefined }); |
| |
|
| | const typeOptions = [ |
| | { label: t('模型组'), value: 'model' }, |
| | { label: t('标签组'), value: 'tag' }, |
| | { label: t('端点组'), value: 'endpoint' }, |
| | ]; |
| |
|
| | |
| | const loadGroups = async () => { |
| | setLoading(true); |
| | try { |
| | const res = await API.get('/api/prefill_group'); |
| | if (res.data.success) { |
| | setGroups(res.data.data || []); |
| | } else { |
| | showError(res.data.message || t('获取组列表失败')); |
| | } |
| | } catch (error) { |
| | showError(t('获取组列表失败')); |
| | } |
| | setLoading(false); |
| | }; |
| |
|
| | |
| | const deleteGroup = async (id) => { |
| | try { |
| | const res = await API.delete(`/api/prefill_group/${id}`); |
| | if (res.data.success) { |
| | showSuccess(t('删除成功')); |
| | loadGroups(); |
| | } else { |
| | showError(res.data.message || t('删除失败')); |
| | } |
| | } catch (error) { |
| | showError(t('删除失败')); |
| | } |
| | }; |
| |
|
| | |
| | const handleEdit = (group = {}) => { |
| | setEditingGroup(group); |
| | setShowEdit(true); |
| | }; |
| |
|
| | |
| | const closeEdit = () => { |
| | setShowEdit(false); |
| | setTimeout(() => { |
| | setEditingGroup({ id: undefined }); |
| | }, 300); |
| | }; |
| |
|
| | |
| | const handleEditSuccess = () => { |
| | closeEdit(); |
| | loadGroups(); |
| | }; |
| |
|
| | |
| | const columns = [ |
| | { |
| | title: t('组名'), |
| | dataIndex: 'name', |
| | key: 'name', |
| | render: (text, record) => ( |
| | <Space> |
| | <Text strong>{text}</Text> |
| | <Tag color='white' shape='circle' size='small'> |
| | {typeOptions.find((opt) => opt.value === record.type)?.label || |
| | record.type} |
| | </Tag> |
| | </Space> |
| | ), |
| | }, |
| | { |
| | title: t('描述'), |
| | dataIndex: 'description', |
| | key: 'description', |
| | render: (text) => renderDescription(text, 150), |
| | }, |
| | { |
| | title: t('项目内容'), |
| | dataIndex: 'items', |
| | key: 'items', |
| | render: (items, record) => { |
| | try { |
| | if (record.type === 'endpoint') { |
| | const obj = |
| | typeof items === 'string' |
| | ? JSON.parse(items || '{}') |
| | : items || {}; |
| | const keys = Object.keys(obj); |
| | if (keys.length === 0) |
| | return <Text type='tertiary'>{t('暂无项目')}</Text>; |
| | return renderLimitedItems({ |
| | items: keys, |
| | renderItem: (key, idx) => ( |
| | <Tag |
| | key={idx} |
| | size='small' |
| | shape='circle' |
| | color={stringToColor(key)} |
| | > |
| | {key} |
| | </Tag> |
| | ), |
| | maxDisplay: 3, |
| | }); |
| | } |
| | const itemsArray = |
| | typeof items === 'string' ? JSON.parse(items) : items; |
| | if (!Array.isArray(itemsArray) || itemsArray.length === 0) { |
| | return <Text type='tertiary'>{t('暂无项目')}</Text>; |
| | } |
| | return renderLimitedItems({ |
| | items: itemsArray, |
| | renderItem: (item, idx) => ( |
| | <Tag |
| | key={idx} |
| | size='small' |
| | shape='circle' |
| | color={stringToColor(item)} |
| | > |
| | {item} |
| | </Tag> |
| | ), |
| | maxDisplay: 3, |
| | }); |
| | } catch { |
| | return <Text type='tertiary'>{t('数据格式错误')}</Text>; |
| | } |
| | }, |
| | }, |
| | { |
| | title: '', |
| | key: 'action', |
| | fixed: 'right', |
| | width: 140, |
| | render: (_, record) => ( |
| | <Space> |
| | <Button size='small' onClick={() => handleEdit(record)}> |
| | {t('编辑')} |
| | </Button> |
| | <Popconfirm |
| | title={t('确定删除此组?')} |
| | onConfirm={() => deleteGroup(record.id)} |
| | > |
| | <Button size='small' type='danger'> |
| | {t('删除')} |
| | </Button> |
| | </Popconfirm> |
| | </Space> |
| | ), |
| | }, |
| | ]; |
| |
|
| | useEffect(() => { |
| | if (visible) { |
| | loadGroups(); |
| | } |
| | }, [visible]); |
| |
|
| | return ( |
| | <> |
| | <SideSheet |
| | placement='left' |
| | title={ |
| | <Space> |
| | <Tag color='blue' shape='circle'> |
| | {t('管理')} |
| | </Tag> |
| | <Title heading={4} className='m-0'> |
| | {t('预填组管理')} |
| | </Title> |
| | </Space> |
| | } |
| | visible={visible} |
| | onCancel={onClose} |
| | width={isMobile ? '100%' : 800} |
| | bodyStyle={{ padding: '0' }} |
| | closeIcon={null} |
| | > |
| | <Spin spinning={loading}> |
| | <div className='p-2'> |
| | <Card className='!rounded-2xl shadow-sm border-0'> |
| | <div className='flex items-center mb-2'> |
| | <Avatar size='small' color='blue' 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> |
| | <div className='flex justify-end mb-4'> |
| | <Button |
| | type='primary' |
| | theme='solid' |
| | size='small' |
| | icon={<IconPlus />} |
| | onClick={() => handleEdit()} |
| | > |
| | {t('新建组')} |
| | </Button> |
| | </div> |
| | {groups.length > 0 ? ( |
| | <CardTable |
| | columns={columns} |
| | dataSource={groups} |
| | rowKey='id' |
| | hidePagination={true} |
| | size='small' |
| | scroll={{ x: 'max-content' }} |
| | /> |
| | ) : ( |
| | <Empty |
| | image={ |
| | <IllustrationNoResult style={{ width: 150, height: 150 }} /> |
| | } |
| | darkModeImage={ |
| | <IllustrationNoResultDark |
| | style={{ width: 150, height: 150 }} |
| | /> |
| | } |
| | description={t('暂无预填组')} |
| | style={{ padding: 30 }} |
| | /> |
| | )} |
| | </Card> |
| | </div> |
| | </Spin> |
| | </SideSheet> |
| | |
| | {/* 编辑组件 */} |
| | <EditPrefillGroupModal |
| | visible={showEdit} |
| | onClose={closeEdit} |
| | editingGroup={editingGroup} |
| | onSuccess={handleEditSuccess} |
| | /> |
| | </> |
| | ); |
| | }; |
| |
|
| | export default PrefillGroupManagement; |
| |
|