chore: cleanup file checkbox
This commit is contained in:
@@ -1,45 +1,68 @@
|
||||
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||
import { ExclamationCircleFilled } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Modal, Space, message, type ModalFuncProps } from 'antd';
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Modal,
|
||||
Space,
|
||||
message,
|
||||
type ModalFuncProps
|
||||
} from 'antd';
|
||||
import { FC, forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import Styles from './index.less';
|
||||
|
||||
const DeleteModal = forwardRef((props, ref) => {
|
||||
const CheckboxWrapper = styled.div`
|
||||
margin-top: 20px;
|
||||
margin-left: 30px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
.check-text {
|
||||
font-weight: 700;
|
||||
color: var(--ant-color-warning);
|
||||
}
|
||||
`;
|
||||
|
||||
interface DeleteModalProps {
|
||||
ref: any;
|
||||
}
|
||||
|
||||
interface DataOptions {
|
||||
content: string;
|
||||
selection?: boolean;
|
||||
name?: string;
|
||||
okText?: string;
|
||||
cancelText?: string;
|
||||
title?: string;
|
||||
operation: string;
|
||||
checkConfig?: {
|
||||
checkText: string;
|
||||
defautlChecked: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const DeleteModal: FC<DeleteModalProps> = forwardRef((props, ref) => {
|
||||
const intl = useIntl();
|
||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [config, setConfig] = useState<
|
||||
ModalFuncProps & {
|
||||
content: string;
|
||||
selection?: boolean;
|
||||
name?: string;
|
||||
okText?: string;
|
||||
cancelText?: string;
|
||||
title?: string;
|
||||
operation: string;
|
||||
}
|
||||
>({} as any);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [config, setConfig] = useState<ModalFuncProps & DataOptions>({} as any);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
show: (
|
||||
data: ModalFuncProps & {
|
||||
content: string;
|
||||
selection?: boolean;
|
||||
name?: string;
|
||||
title?: string;
|
||||
cancelText?: string;
|
||||
okText?: string;
|
||||
operation: string;
|
||||
}
|
||||
) => {
|
||||
show: (data: ModalFuncProps & DataOptions) => {
|
||||
saveScrollHeight();
|
||||
setConfig(data);
|
||||
setChecked(data.checkConfig?.defautlChecked || false);
|
||||
setVisible(true);
|
||||
},
|
||||
hide: () => {
|
||||
setVisible(false);
|
||||
restoreScrollHeight();
|
||||
},
|
||||
configuration: {
|
||||
checked: checked
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -111,6 +134,18 @@ const DeleteModal = forwardRef((props, ref) => {
|
||||
)
|
||||
}}
|
||||
></div>
|
||||
{config.checkConfig && (
|
||||
<CheckboxWrapper>
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onChange={(e) => setChecked(e.target.checked)}
|
||||
>
|
||||
<span className="check-text">
|
||||
{intl.formatMessage({ id: config.checkConfig?.checkText })}
|
||||
</span>
|
||||
</Checkbox>
|
||||
</CheckboxWrapper>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@ html {
|
||||
--color-scrollbar-track: var(--ant-color-fill-tertiary);
|
||||
// --color-fill-1: #fff;
|
||||
--ant-color-text: #000;
|
||||
--color-bg-1: #f4f5f4;
|
||||
--color-scroll-bg: #d9d9d9;
|
||||
--color-fill-2: #fff;
|
||||
--color-fill-3: #f3f6fa;
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function useTableFetch<ListItem>(options: {
|
||||
API?: string;
|
||||
watch?: boolean;
|
||||
fetchAPI: (params: any) => Promise<Global.PageResponse<ListItem>>;
|
||||
deleteAPI?: (id: number) => Promise<any>;
|
||||
deleteAPI?: (id: number, params?: any) => Promise<any>;
|
||||
contentForDelete?: string;
|
||||
}) {
|
||||
const { fetchAPI, deleteAPI, contentForDelete, API, watch } = options;
|
||||
@@ -139,27 +139,38 @@ export default function useTableFetch<ListItem>(options: {
|
||||
|
||||
const handleNameChange = debounceUpdateFilter;
|
||||
|
||||
const handleDelete = (row: ListItem & { name: string; id: number }) => {
|
||||
const handleDelete = (
|
||||
row: ListItem & { name: string; id: number },
|
||||
options?: any
|
||||
) => {
|
||||
modalRef.current.show({
|
||||
content: contentForDelete,
|
||||
operation: 'common.delete.single.confirm',
|
||||
name: row.name,
|
||||
...options,
|
||||
async onOk() {
|
||||
console.log('OK');
|
||||
await deleteAPI?.(row.id);
|
||||
await deleteAPI?.(row.id, {
|
||||
...modalRef.current?.configuration
|
||||
});
|
||||
fetchData();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteBatch = () => {
|
||||
const handleDeleteBatch = (options = {}) => {
|
||||
modalRef.current.show({
|
||||
content: contentForDelete,
|
||||
operation: 'common.delete.confirm',
|
||||
selection: true,
|
||||
...options,
|
||||
async onOk() {
|
||||
if (!deleteAPI) return;
|
||||
await handleBatchRequest(rowSelection.selectedRowKeys, deleteAPI);
|
||||
await handleBatchRequest(rowSelection.selectedRowKeys, (id) =>
|
||||
deleteAPI(id, {
|
||||
...modalRef.current?.configuration
|
||||
})
|
||||
);
|
||||
rowSelection.clearSelections();
|
||||
fetchData();
|
||||
}
|
||||
|
||||
@@ -68,5 +68,6 @@ export default {
|
||||
'resources.modelfiles.storagePath.holder':
|
||||
'Waiting for download to complete...',
|
||||
'resources.filter.worker': 'Filter by Worker',
|
||||
'resources.filter.source': 'Filter by Source'
|
||||
'resources.filter.source': 'Filter by Source',
|
||||
'resources.modelfiles.delete.tips': 'Also delete the file from disk!'
|
||||
};
|
||||
|
||||
@@ -13,10 +13,14 @@ export default {
|
||||
'resources.form.enablePartialOffload': 'Разрешить оффлоуд на CPU',
|
||||
'resources.form.placementStrategy': 'Стратегия размещения',
|
||||
'resources.form.workerSelector': 'Селектор воркеров',
|
||||
'resources.form.enableDistributedInferenceAcrossWorkers': 'Разрешить распределённый инференс между воркерами',
|
||||
'resources.form.spread.tips': 'Равномерно распределяет ресурсы между воркерами. Может увеличить фрагментацию ресурсов на отдельных воркерах.',
|
||||
'resources.form.binpack.tips': 'Максимизирует утилизацию ресурсов, уменьшая фрагментацию на воркерах/GPU.',
|
||||
'resources.form.workerSelector.description': 'Система выбирает подходящие GPU/воркеры для развертывания моделей на основе меток.',
|
||||
'resources.form.enableDistributedInferenceAcrossWorkers':
|
||||
'Разрешить распределённый инференс между воркерами',
|
||||
'resources.form.spread.tips':
|
||||
'Равномерно распределяет ресурсы между воркерами. Может увеличить фрагментацию ресурсов на отдельных воркерах.',
|
||||
'resources.form.binpack.tips':
|
||||
'Максимизирует утилизацию ресурсов, уменьшая фрагментацию на воркерах/GPU.',
|
||||
'resources.form.workerSelector.description':
|
||||
'Система выбирает подходящие GPU/воркеры для развертывания моделей на основе меток.',
|
||||
'resources.table.ip': 'IP-адрес',
|
||||
'resources.table.cpu': 'CPU',
|
||||
'resources.table.memory': 'ОЗУ',
|
||||
@@ -39,12 +43,16 @@ export default {
|
||||
'resources.table.unified': 'Объединённая память',
|
||||
'resources.worker.add.step1': 'Получить токен',
|
||||
'resources.worker.add.step2': 'Зарегистрировать воркер',
|
||||
'resources.worker.add.step2.tips': 'Примечание: <span style="color: #000;font-weight: 600">mytoken</span> — токен из первого шага.',
|
||||
'resources.worker.add.step3': 'После успешной регистрации обновите список воркеров.',
|
||||
'resources.worker.add.step2.tips':
|
||||
'Примечание: <span style="color: #000;font-weight: 600">mytoken</span> — токен из первого шага.',
|
||||
'resources.worker.add.step3':
|
||||
'После успешной регистрации обновите список воркеров.',
|
||||
'resources.worker.container.supported': 'Только для Linux.',
|
||||
'resources.worker.current.version': 'Текущая версия: {version}',
|
||||
'resources.worker.driver.install': 'Установите необходимые драйверы и библиотеки перед установкой GPUStack.',
|
||||
'resources.worker.select.command': 'Выберите метку для генерации команды и скопируйте её.',
|
||||
'resources.worker.driver.install':
|
||||
'Установите необходимые драйверы и библиотеки перед установкой GPUStack.',
|
||||
'resources.worker.select.command':
|
||||
'Выберите метку для генерации команды и скопируйте её.',
|
||||
'resources.worker.script.install': 'Установка скриптом',
|
||||
'resources.worker.container.install': 'Установка контейнером (только Linux)',
|
||||
'resources.worker.cann.tips': `Укажите индексы GPU через <span style='color: #000;font-weight: 600'>ASCEND_VISIBLE_DEVICES=0,1,2,3</span> или <span style='color: #000;font-weight: 600'>ASCEND_VISIBLE_DEVICES=0-3</span>.`,
|
||||
@@ -54,13 +62,15 @@ export default {
|
||||
'resources.modelfiles.size': 'Размер',
|
||||
'resources.modelfiles.selecttarget': 'Выбрать назначение',
|
||||
'resources.modelfiles.form.localdir': 'Локальный каталог',
|
||||
'resources.modelfiles.form.localdir.tips': 'Каталог по умолчанию <span class="desc-block>/var/lib/gpustack/cache</span> или каталог, указанный через <span class="desc-block">--data-dir</span>.',
|
||||
'resources.modelfiles.form.localdir.tips':
|
||||
'Каталог по умолчанию <span class="desc-block>/var/lib/gpustack/cache</span> или каталог, указанный через <span class="desc-block">--data-dir</span>.',
|
||||
'resources.modelfiles.retry.download': 'Повторить загрузку',
|
||||
'resources.modelfiles.storagePath.holder': 'Ожидание завершения загрузки...',
|
||||
'resources.filter.worker': 'Фильтровать по узлу',
|
||||
'resources.filter.source': 'Фильтровать по источнику'
|
||||
'resources.filter.source': 'Фильтровать по источнику',
|
||||
'resources.modelfiles.delete.tips': 'Also delete the file from disk!'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
// 1. 'resources.modelfiles.delete.tips'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -66,5 +66,6 @@ export default {
|
||||
'resources.modelfiles.retry.download': '重新下载',
|
||||
'resources.modelfiles.storagePath.holder': '等待下载完成...',
|
||||
'resources.filter.worker': '按 worker 筛选',
|
||||
'resources.filter.source': '按来源筛选'
|
||||
'resources.filter.source': '按来源筛选',
|
||||
'resources.modelfiles.delete.tips': '同时从磁盘删除文件!'
|
||||
};
|
||||
|
||||
@@ -45,10 +45,16 @@ export async function queryModelFilesList(params: Global.SearchParams) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteModelFile(id: string | number) {
|
||||
return request<Global.PageResponse<ModelFile>>(`${MODEL_FILES_API}/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
export async function deleteModelFile(
|
||||
id: string | number,
|
||||
params: { checked: boolean }
|
||||
) {
|
||||
return request<Global.PageResponse<ModelFile>>(
|
||||
`${MODEL_FILES_API}/${id}?cleanup=${params.checked}`,
|
||||
{
|
||||
method: 'DELETE'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateModelFile(id: string | number, data: any) {
|
||||
|
||||
@@ -44,6 +44,7 @@ import dayjs from 'dayjs';
|
||||
import { useAtom } from 'jotai';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
useGenerateFormEditInitialValues,
|
||||
useGenerateModelFileOptions
|
||||
@@ -69,6 +70,26 @@ import {
|
||||
|
||||
const filterPattern = /^(.*?)(?:-\d+-of-\d+)?(\.gguf)?$/;
|
||||
|
||||
const PathWrapper = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
.btn-wrapper {
|
||||
position: absolute;
|
||||
background: var(--color-bg-1);
|
||||
right: 0;
|
||||
display: none;
|
||||
align-items: center;
|
||||
}
|
||||
&:hover {
|
||||
.btn-wrapper {
|
||||
display: flex;
|
||||
background: var(--ant-table-row-hover-bg);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const getWorkerName = (
|
||||
id: number,
|
||||
workersList: Global.BaseOption<number>[]
|
||||
@@ -76,6 +97,7 @@ const getWorkerName = (
|
||||
const worker = workersList.find((item) => item.value === id);
|
||||
return worker?.label || '';
|
||||
};
|
||||
|
||||
const InstanceStatusTag = (props: { data: ListItem }) => {
|
||||
const { data } = props;
|
||||
if (!data.state) {
|
||||
@@ -272,10 +294,18 @@ const ModelFiles = () => {
|
||||
const handleSelect = async (val: any, record: ListItem) => {
|
||||
try {
|
||||
if (val === 'delete') {
|
||||
handleDelete({
|
||||
...record,
|
||||
name: record.local_path
|
||||
});
|
||||
handleDelete(
|
||||
{
|
||||
...record,
|
||||
name: record.local_path
|
||||
},
|
||||
{
|
||||
checkConfig: {
|
||||
checkText: 'resources.modelfiles.delete.tips',
|
||||
defautlChecked: record.source !== modelSourceMap.local_path_value
|
||||
}
|
||||
}
|
||||
);
|
||||
} else if (val === 'retry') {
|
||||
await retryDownloadModelFile(record.id);
|
||||
showSuccess();
|
||||
@@ -357,6 +387,15 @@ const ModelFiles = () => {
|
||||
restoreScrollHeight();
|
||||
};
|
||||
|
||||
const handleDeleteByBatch = () => {
|
||||
handleDeleteBatch({
|
||||
checkConfig: {
|
||||
checkText: 'resources.modelfiles.delete.tips',
|
||||
defautlChecked: false
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleCreateModel = async (data: any) => {
|
||||
try {
|
||||
const result = getSourceRepoConfigValue(openDeployModal.source, data);
|
||||
@@ -430,17 +469,19 @@ const ModelFiles = () => {
|
||||
}
|
||||
return (
|
||||
record.resolved_paths?.length > 0 && (
|
||||
<span className="flex-center">
|
||||
<PathWrapper>
|
||||
<AutoTooltip ghost>
|
||||
<span>{record.resolved_paths?.[0]}</span>
|
||||
</AutoTooltip>
|
||||
<CopyButton
|
||||
text={record.resolved_paths?.[0]}
|
||||
type="link"
|
||||
btnStyle={{ width: 18, paddingInline: 2 }}
|
||||
></CopyButton>
|
||||
{renderParts(record)}
|
||||
</span>
|
||||
<span className="btn-wrapper">
|
||||
<CopyButton
|
||||
text={record.resolved_paths?.[0]}
|
||||
type="link"
|
||||
btnStyle={{ width: 18, paddingInline: 2 }}
|
||||
></CopyButton>
|
||||
{renderParts(record)}
|
||||
</span>
|
||||
</PathWrapper>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -534,7 +575,7 @@ const ModelFiles = () => {
|
||||
<Button
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={handleDeleteBatch}
|
||||
onClick={handleDeleteByBatch}
|
||||
disabled={!rowSelection.selectedRowKeys.length}
|
||||
>
|
||||
<span>
|
||||
|
||||
Reference in New Issue
Block a user