| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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; |
|
|