|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useEffect, useState } from 'react'; |
|
|
import { |
|
|
Modal, |
|
|
Table, |
|
|
Spin, |
|
|
Button, |
|
|
Typography, |
|
|
Empty, |
|
|
Input, |
|
|
} from '@douyinfe/semi-ui'; |
|
|
import { |
|
|
IllustrationNoResult, |
|
|
IllustrationNoResultDark, |
|
|
} from '@douyinfe/semi-illustrations'; |
|
|
import { IconSearch } from '@douyinfe/semi-icons'; |
|
|
import { API, showError } from '../../../../helpers'; |
|
|
import { MODEL_TABLE_PAGE_SIZE } from '../../../../constants'; |
|
|
import { useIsMobile } from '../../../../hooks/common/useIsMobile'; |
|
|
|
|
|
const MissingModelsModal = ({ visible, onClose, onConfigureModel, t }) => { |
|
|
const [loading, setLoading] = useState(false); |
|
|
const [missingModels, setMissingModels] = useState([]); |
|
|
const [searchKeyword, setSearchKeyword] = useState(''); |
|
|
const [currentPage, setCurrentPage] = useState(1); |
|
|
const isMobile = useIsMobile(); |
|
|
|
|
|
const fetchMissing = async () => { |
|
|
setLoading(true); |
|
|
try { |
|
|
const res = await API.get('/api/models/missing'); |
|
|
if (res.data.success) { |
|
|
setMissingModels(res.data.data || []); |
|
|
} else { |
|
|
showError(res.data.message); |
|
|
} |
|
|
} catch (_) { |
|
|
showError(t('获取未配置模型失败')); |
|
|
} |
|
|
setLoading(false); |
|
|
}; |
|
|
|
|
|
useEffect(() => { |
|
|
if (visible) { |
|
|
fetchMissing(); |
|
|
setSearchKeyword(''); |
|
|
setCurrentPage(1); |
|
|
} else { |
|
|
setMissingModels([]); |
|
|
} |
|
|
}, [visible]); |
|
|
|
|
|
|
|
|
const filteredModels = missingModels.filter((model) => |
|
|
model.toLowerCase().includes(searchKeyword.toLowerCase()), |
|
|
); |
|
|
|
|
|
const dataSource = (() => { |
|
|
const start = (currentPage - 1) * MODEL_TABLE_PAGE_SIZE; |
|
|
const end = start + MODEL_TABLE_PAGE_SIZE; |
|
|
return filteredModels.slice(start, end).map((model) => ({ |
|
|
model, |
|
|
key: model, |
|
|
})); |
|
|
})(); |
|
|
|
|
|
const columns = [ |
|
|
{ |
|
|
title: t('模型名称'), |
|
|
dataIndex: 'model', |
|
|
render: (text) => ( |
|
|
<div className='flex items-center'> |
|
|
<Typography.Text strong>{text}</Typography.Text> |
|
|
</div> |
|
|
), |
|
|
}, |
|
|
{ |
|
|
title: '', |
|
|
dataIndex: 'operate', |
|
|
fixed: 'right', |
|
|
width: 120, |
|
|
render: (text, record) => ( |
|
|
<Button |
|
|
type='primary' |
|
|
size='small' |
|
|
onClick={() => onConfigureModel(record.model)} |
|
|
> |
|
|
{t('配置')} |
|
|
</Button> |
|
|
), |
|
|
}, |
|
|
]; |
|
|
|
|
|
return ( |
|
|
<Modal |
|
|
title={ |
|
|
<div className='flex flex-col gap-2 w-full'> |
|
|
<div className='flex items-center gap-2'> |
|
|
<Typography.Text |
|
|
strong |
|
|
className='!text-[var(--semi-color-text-0)] !text-base' |
|
|
> |
|
|
{t('未配置的模型列表')} |
|
|
</Typography.Text> |
|
|
<Typography.Text type='tertiary' size='small'> |
|
|
{t('共')} {missingModels.length} {t('个未配置模型')} |
|
|
</Typography.Text> |
|
|
</div> |
|
|
</div> |
|
|
} |
|
|
visible={visible} |
|
|
onCancel={onClose} |
|
|
footer={null} |
|
|
size={isMobile ? 'full-width' : 'medium'} |
|
|
className='!rounded-lg' |
|
|
> |
|
|
<Spin spinning={loading}> |
|
|
{missingModels.length === 0 && !loading ? ( |
|
|
<Empty |
|
|
image={<IllustrationNoResult style={{ width: 150, height: 150 }} />} |
|
|
darkModeImage={ |
|
|
<IllustrationNoResultDark style={{ width: 150, height: 150 }} /> |
|
|
} |
|
|
description={t('暂无缺失模型')} |
|
|
style={{ padding: 30 }} |
|
|
/> |
|
|
) : ( |
|
|
<div className='missing-models-content'> |
|
|
{/* 搜索框 */} |
|
|
<div className='flex items-center justify-end gap-2 w-full mb-4'> |
|
|
<Input |
|
|
placeholder={t('搜索模型...')} |
|
|
value={searchKeyword} |
|
|
onChange={(v) => { |
|
|
setSearchKeyword(v); |
|
|
setCurrentPage(1); |
|
|
}} |
|
|
className='!w-full' |
|
|
prefix={<IconSearch />} |
|
|
showClear |
|
|
/> |
|
|
</div> |
|
|
|
|
|
{/* 表格 */} |
|
|
{filteredModels.length > 0 ? ( |
|
|
<Table |
|
|
columns={columns} |
|
|
dataSource={dataSource} |
|
|
pagination={{ |
|
|
currentPage: currentPage, |
|
|
pageSize: MODEL_TABLE_PAGE_SIZE, |
|
|
total: filteredModels.length, |
|
|
showSizeChanger: false, |
|
|
onPageChange: (page) => setCurrentPage(page), |
|
|
}} |
|
|
/> |
|
|
) : ( |
|
|
<Empty |
|
|
image={ |
|
|
<IllustrationNoResult style={{ width: 100, height: 100 }} /> |
|
|
} |
|
|
darkModeImage={ |
|
|
<IllustrationNoResultDark |
|
|
style={{ width: 100, height: 100 }} |
|
|
/> |
|
|
} |
|
|
description={ |
|
|
searchKeyword ? t('未找到匹配的模型') : t('暂无缺失模型') |
|
|
} |
|
|
style={{ padding: 20 }} |
|
|
/> |
|
|
)} |
|
|
</div> |
|
|
)} |
|
|
</Spin> |
|
|
</Modal> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default MissingModelsModal; |
|
|
|