|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useState, useEffect } from 'react'; |
|
|
import { |
|
|
Empty, |
|
|
Skeleton, |
|
|
Space, |
|
|
Tag, |
|
|
Collapsible, |
|
|
Tabs, |
|
|
TabPane, |
|
|
Typography, |
|
|
Avatar, |
|
|
} from '@douyinfe/semi-ui'; |
|
|
import { |
|
|
IllustrationNoContent, |
|
|
IllustrationNoContentDark, |
|
|
} from '@douyinfe/semi-illustrations'; |
|
|
import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons'; |
|
|
import { Settings } from 'lucide-react'; |
|
|
import { renderModelTag, getModelCategories } from '../../../../helpers'; |
|
|
|
|
|
const ModelsList = ({ t, models, modelsLoading, copyText }) => { |
|
|
const [isModelsExpanded, setIsModelsExpanded] = useState(() => { |
|
|
|
|
|
const savedState = localStorage.getItem('modelsExpanded'); |
|
|
return savedState ? JSON.parse(savedState) : false; |
|
|
}); |
|
|
const [activeModelCategory, setActiveModelCategory] = useState('all'); |
|
|
const MODELS_DISPLAY_COUNT = 25; |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
localStorage.setItem('modelsExpanded', JSON.stringify(isModelsExpanded)); |
|
|
}, [isModelsExpanded]); |
|
|
|
|
|
return ( |
|
|
<div className='py-4'> |
|
|
{/* 卡片头部 */} |
|
|
<div className='flex items-center mb-4'> |
|
|
<Avatar size='small' color='green' className='mr-3 shadow-md'> |
|
|
<Settings size={16} /> |
|
|
</Avatar> |
|
|
<div> |
|
|
<Typography.Text className='text-lg font-medium'> |
|
|
{t('可用模型')} |
|
|
</Typography.Text> |
|
|
<div className='text-xs text-gray-600'> |
|
|
{t('查看当前可用的所有模型')} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{/* 可用模型部分 */} |
|
|
<div className='bg-gray-50 dark:bg-gray-800 rounded-xl'> |
|
|
{modelsLoading ? ( |
|
|
// 骨架屏加载状态 - 模拟实际加载后的布局 |
|
|
<div className='space-y-4'> |
|
|
{/* 模拟分类标签 */} |
|
|
<div |
|
|
className='mb-4' |
|
|
style={{ borderBottom: '1px solid var(--semi-color-border)' }} |
|
|
> |
|
|
<div className='flex overflow-x-auto py-2 gap-2'> |
|
|
{Array.from({ length: 8 }).map((_, index) => ( |
|
|
<Skeleton.Button |
|
|
key={`cat-${index}`} |
|
|
style={{ |
|
|
width: index === 0 ? 130 : 100 + Math.random() * 50, |
|
|
height: 36, |
|
|
borderRadius: 8, |
|
|
}} |
|
|
/> |
|
|
))} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{/* 模拟模型标签列表 */} |
|
|
<div className='flex flex-wrap gap-2'> |
|
|
{Array.from({ length: 20 }).map((_, index) => ( |
|
|
<Skeleton.Button |
|
|
key={`model-${index}`} |
|
|
style={{ |
|
|
width: 100 + Math.random() * 100, |
|
|
height: 32, |
|
|
borderRadius: 16, |
|
|
margin: '4px', |
|
|
}} |
|
|
/> |
|
|
))} |
|
|
</div> |
|
|
</div> |
|
|
) : models.length === 0 ? ( |
|
|
<div className='py-8'> |
|
|
<Empty |
|
|
image={ |
|
|
<IllustrationNoContent style={{ width: 150, height: 150 }} /> |
|
|
} |
|
|
darkModeImage={ |
|
|
<IllustrationNoContentDark |
|
|
style={{ width: 150, height: 150 }} |
|
|
/> |
|
|
} |
|
|
description={t('没有可用模型')} |
|
|
style={{ padding: '24px 0' }} |
|
|
/> |
|
|
</div> |
|
|
) : ( |
|
|
<> |
|
|
{/* 模型分类标签页 */} |
|
|
<div className='mb-4'> |
|
|
<Tabs |
|
|
type='card' |
|
|
activeKey={activeModelCategory} |
|
|
onChange={(key) => setActiveModelCategory(key)} |
|
|
className='mt-2' |
|
|
collapsible |
|
|
> |
|
|
{Object.entries(getModelCategories(t)).map( |
|
|
([key, category]) => { |
|
|
// 计算该分类下的模型数量 |
|
|
const modelCount = |
|
|
key === 'all' |
|
|
? models.length |
|
|
: models.filter((model) => |
|
|
category.filter({ model_name: model }), |
|
|
).length; |
|
|
|
|
|
if (modelCount === 0 && key !== 'all') return null; |
|
|
|
|
|
return ( |
|
|
<TabPane |
|
|
tab={ |
|
|
<span className='flex items-center gap-2'> |
|
|
{category.icon && ( |
|
|
<span className='w-4 h-4'>{category.icon}</span> |
|
|
)} |
|
|
{category.label} |
|
|
<Tag |
|
|
color={ |
|
|
activeModelCategory === key ? 'red' : 'grey' |
|
|
} |
|
|
size='small' |
|
|
shape='circle' |
|
|
> |
|
|
{modelCount} |
|
|
</Tag> |
|
|
</span> |
|
|
} |
|
|
itemKey={key} |
|
|
key={key} |
|
|
/> |
|
|
); |
|
|
}, |
|
|
)} |
|
|
</Tabs> |
|
|
</div> |
|
|
|
|
|
<div className='bg-white dark:bg-gray-700 rounded-lg p-3'> |
|
|
{(() => { |
|
|
// 根据当前选中的分类过滤模型 |
|
|
const categories = getModelCategories(t); |
|
|
const filteredModels = |
|
|
activeModelCategory === 'all' |
|
|
? models |
|
|
: models.filter((model) => |
|
|
categories[activeModelCategory].filter({ |
|
|
model_name: model, |
|
|
}), |
|
|
); |
|
|
|
|
|
// 如果过滤后没有模型,显示空状态 |
|
|
if (filteredModels.length === 0) { |
|
|
return ( |
|
|
<Empty |
|
|
image={ |
|
|
<IllustrationNoContent |
|
|
style={{ width: 120, height: 120 }} |
|
|
/> |
|
|
} |
|
|
darkModeImage={ |
|
|
<IllustrationNoContentDark |
|
|
style={{ width: 120, height: 120 }} |
|
|
/> |
|
|
} |
|
|
description={t('该分类下没有可用模型')} |
|
|
style={{ padding: '16px 0' }} |
|
|
/> |
|
|
); |
|
|
} |
|
|
|
|
|
if (filteredModels.length <= MODELS_DISPLAY_COUNT) { |
|
|
return ( |
|
|
<Space wrap> |
|
|
{filteredModels.map((model) => |
|
|
renderModelTag(model, { |
|
|
size: 'small', |
|
|
shape: 'circle', |
|
|
onClick: () => copyText(model), |
|
|
}), |
|
|
)} |
|
|
</Space> |
|
|
); |
|
|
} else { |
|
|
return ( |
|
|
<> |
|
|
<Collapsible isOpen={isModelsExpanded}> |
|
|
<Space wrap> |
|
|
{filteredModels.map((model) => |
|
|
renderModelTag(model, { |
|
|
size: 'small', |
|
|
shape: 'circle', |
|
|
onClick: () => copyText(model), |
|
|
}), |
|
|
)} |
|
|
<Tag |
|
|
color='grey' |
|
|
type='light' |
|
|
className='cursor-pointer !rounded-lg' |
|
|
onClick={() => setIsModelsExpanded(false)} |
|
|
icon={<IconChevronUp />} |
|
|
> |
|
|
{t('收起')} |
|
|
</Tag> |
|
|
</Space> |
|
|
</Collapsible> |
|
|
{!isModelsExpanded && ( |
|
|
<Space wrap> |
|
|
{filteredModels |
|
|
.slice(0, MODELS_DISPLAY_COUNT) |
|
|
.map((model) => |
|
|
renderModelTag(model, { |
|
|
size: 'small', |
|
|
shape: 'circle', |
|
|
onClick: () => copyText(model), |
|
|
}), |
|
|
)} |
|
|
<Tag |
|
|
color='grey' |
|
|
type='light' |
|
|
className='cursor-pointer !rounded-lg' |
|
|
onClick={() => setIsModelsExpanded(true)} |
|
|
icon={<IconChevronDown />} |
|
|
> |
|
|
{t('更多')}{' '} |
|
|
{filteredModels.length - MODELS_DISPLAY_COUNT}{' '} |
|
|
{t('个模型')} |
|
|
</Tag> |
|
|
</Space> |
|
|
)} |
|
|
</> |
|
|
); |
|
|
} |
|
|
})()} |
|
|
</div> |
|
|
</> |
|
|
)} |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default ModelsList; |
|
|
|