fix: ui issues
This commit is contained in:
@@ -29,6 +29,7 @@ const SealAutoComplete: React.FC<
|
|||||||
extra,
|
extra,
|
||||||
style,
|
style,
|
||||||
addAfter,
|
addAfter,
|
||||||
|
suffixIcon,
|
||||||
loading,
|
loading,
|
||||||
allowClear,
|
allowClear,
|
||||||
clearSpaceOnBlur,
|
clearSpaceOnBlur,
|
||||||
@@ -103,7 +104,7 @@ const SealAutoComplete: React.FC<
|
|||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return suffixIcon || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const popupRender = (originNode: React.ReactElement): React.ReactElement => {
|
const popupRender = (originNode: React.ReactElement): React.ReactElement => {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const SealInputNumber: React.FC<InputNumberProps & SealFormItemProps> = (
|
|||||||
let status = '';
|
let status = '';
|
||||||
if (isInFormItems) {
|
if (isInFormItems) {
|
||||||
const statusData = Form?.Item?.useStatus?.();
|
const statusData = Form?.Item?.useStatus?.();
|
||||||
status = statusData?.status || '';
|
status = props.status || statusData?.status || '';
|
||||||
} else {
|
} else {
|
||||||
status = props.status || '';
|
status = props.status || '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ const SealPassword: React.FC<InputProps & SealFormItemProps> = (props) => {
|
|||||||
e.target.value = e.target.value?.trim?.();
|
e.target.value = e.target.value?.trim?.();
|
||||||
if (!inputRef.current?.input?.value) {
|
if (!inputRef.current?.input?.value) {
|
||||||
setIsFocus(false);
|
setIsFocus(false);
|
||||||
props.onBlur?.(e);
|
|
||||||
}
|
}
|
||||||
|
props.onBlur?.(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -106,7 +106,9 @@ const SealCascader: React.FC<
|
|||||||
// the status can be controlled by Form.Item
|
// the status can be controlled by Form.Item
|
||||||
if (isInFormItems) {
|
if (isInFormItems) {
|
||||||
const statusData = Form?.Item?.useStatus?.();
|
const statusData = Form?.Item?.useStatus?.();
|
||||||
status = statusData?.status || '';
|
status = props.status || statusData?.status || '';
|
||||||
|
} else {
|
||||||
|
status = props.status || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const _options = useMemo(() => {
|
const _options = useMemo(() => {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
|
|||||||
let status = '';
|
let status = '';
|
||||||
if (isInFormItems) {
|
if (isInFormItems) {
|
||||||
const statusData = Form?.Item?.useStatus?.();
|
const statusData = Form?.Item?.useStatus?.();
|
||||||
status = statusData?.status || '';
|
status = props.status || statusData?.status || '';
|
||||||
} else {
|
} else {
|
||||||
status = props.status || '';
|
status = props.status || '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,8 +181,11 @@ const SelectWrapper = styled.div`
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.validate-status-error {
|
&.validate-status-error {
|
||||||
&.dropdown-visible {
|
.ant-select-dropdown {
|
||||||
.ant-select-dropdown {
|
border-color: var(--ant-color-error) !important;
|
||||||
|
}
|
||||||
|
.__wrapper__ {
|
||||||
|
.ant-cascader.ant-select.ant-select-outlined {
|
||||||
border-color: var(--ant-color-error) !important;
|
border-color: var(--ant-color-error) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ export default function useTableFetch<T>(
|
|||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
loadendRef.current = false;
|
loadendRef.current = false;
|
||||||
const newQueryParams = { ...queryParams, ...params };
|
const newQueryParams = { ...queryParams, ...params, page: 1 };
|
||||||
setQueryParams(newQueryParams);
|
setQueryParams(newQueryParams);
|
||||||
await fetchData({ query: newQueryParams });
|
await fetchData({ query: newQueryParams });
|
||||||
if (watch && !options?.paginate) {
|
if (watch && !options?.paginate) {
|
||||||
|
|||||||
@@ -80,5 +80,8 @@ export default {
|
|||||||
'benchmark.env.system': 'System',
|
'benchmark.env.system': 'System',
|
||||||
'benchmark.env.runtimeVersion': 'Runtime Version',
|
'benchmark.env.runtimeVersion': 'Runtime Version',
|
||||||
'benchmark.env.driverVersion': 'Driver Version',
|
'benchmark.env.driverVersion': 'Driver Version',
|
||||||
'benchmark.env.cpuCounts': 'CPU Counts'
|
'benchmark.env.cpuCounts': 'CPU Cores',
|
||||||
|
'benchmark.table.export.results': 'Export Results',
|
||||||
|
'benchmark.form.nonLlmModel.tips':
|
||||||
|
'Benchmarking currently only supports LLM models'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,5 +15,11 @@ export default {
|
|||||||
'providers.form.model.test': 'Test',
|
'providers.form.model.test': 'Test',
|
||||||
'providers.form.model.test.tips': 'Test Connection',
|
'providers.form.model.test.tips': 'Test Connection',
|
||||||
'providers.form.target.placeholder': 'provider/models',
|
'providers.form.target.placeholder': 'provider/models',
|
||||||
'providers.form.fallback.token': 'Fallback API Keys'
|
'providers.form.fallback.token': 'Fallback API Keys',
|
||||||
|
'providers.form.custombeckendUrl': 'Custom Base URL',
|
||||||
|
'providers.form.more': '+{count} more',
|
||||||
|
'providers.form.addAll': 'Add All',
|
||||||
|
'providers.form.rules.models': 'Please select at least one model',
|
||||||
|
'providers.form.rules.tokens': 'Please enter a valid API Key',
|
||||||
|
'providers.form.rules.model': 'Please select a model'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,5 +80,8 @@ export default {
|
|||||||
'benchmark.env.system': 'System',
|
'benchmark.env.system': 'System',
|
||||||
'benchmark.env.runtimeVersion': 'Runtime Version',
|
'benchmark.env.runtimeVersion': 'Runtime Version',
|
||||||
'benchmark.env.driverVersion': 'Driver Version',
|
'benchmark.env.driverVersion': 'Driver Version',
|
||||||
'benchmark.env.cpuCounts': 'CPU Counts'
|
'benchmark.env.cpuCounts': 'CPU Cores',
|
||||||
|
'benchmark.table.export.results': 'Export Results',
|
||||||
|
'benchmark.form.nonLlmModel.tips':
|
||||||
|
'Benchmarking currently only supports LLM models'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,5 +15,11 @@ export default {
|
|||||||
'providers.form.model.test': 'Test',
|
'providers.form.model.test': 'Test',
|
||||||
'providers.form.model.test.tips': 'Test Connection',
|
'providers.form.model.test.tips': 'Test Connection',
|
||||||
'providers.form.target.placeholder': 'provider/models',
|
'providers.form.target.placeholder': 'provider/models',
|
||||||
'providers.form.fallback.token': 'Fallback API Keys'
|
'providers.form.fallback.token': 'Fallback API Keys',
|
||||||
|
'providers.form.custombeckendUrl': 'Custom Base URL',
|
||||||
|
'providers.form.more': '+{count} more',
|
||||||
|
'providers.form.addAll': 'Add All',
|
||||||
|
'providers.form.rules.models': 'Please select at least one model',
|
||||||
|
'providers.form.rules.tokens': 'Please enter a valid API Key',
|
||||||
|
'providers.form.rules.model': 'Please select a model'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,5 +80,8 @@ export default {
|
|||||||
'benchmark.env.system': 'System',
|
'benchmark.env.system': 'System',
|
||||||
'benchmark.env.runtimeVersion': 'Runtime Version',
|
'benchmark.env.runtimeVersion': 'Runtime Version',
|
||||||
'benchmark.env.driverVersion': 'Driver Version',
|
'benchmark.env.driverVersion': 'Driver Version',
|
||||||
'benchmark.env.cpuCounts': 'CPU Counts'
|
'benchmark.env.cpuCounts': 'CPU Cores',
|
||||||
|
'benchmark.table.export.results': 'Export Results',
|
||||||
|
'benchmark.form.nonLlmModel.tips':
|
||||||
|
'Benchmarking currently only supports LLM models'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,5 +15,11 @@ export default {
|
|||||||
'providers.form.model.test': 'Test',
|
'providers.form.model.test': 'Test',
|
||||||
'providers.form.model.test.tips': 'Test Connection',
|
'providers.form.model.test.tips': 'Test Connection',
|
||||||
'providers.form.target.placeholder': 'provider/models',
|
'providers.form.target.placeholder': 'provider/models',
|
||||||
'providers.form.fallback.token': 'Fallback API Keys'
|
'providers.form.fallback.token': 'Fallback API Keys',
|
||||||
|
'providers.form.custombeckendUrl': 'Custom Base URL',
|
||||||
|
'providers.form.more': '+{count} more',
|
||||||
|
'providers.form.addAll': 'Add All',
|
||||||
|
'providers.form.rules.models': 'Please select at least one model',
|
||||||
|
'providers.form.rules.tokens': 'Please enter a valid API Key',
|
||||||
|
'providers.form.rules.model': 'Please select a model'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,5 +80,7 @@ export default {
|
|||||||
'benchmark.env.system': '系统',
|
'benchmark.env.system': '系统',
|
||||||
'benchmark.env.runtimeVersion': '运行时版本',
|
'benchmark.env.runtimeVersion': '运行时版本',
|
||||||
'benchmark.env.driverVersion': '驱动版本',
|
'benchmark.env.driverVersion': '驱动版本',
|
||||||
'benchmark.env.cpuCounts': 'CPU 核心数'
|
'benchmark.env.cpuCounts': 'CPU 核心数',
|
||||||
|
'benchmark.table.export.results': '导出结果',
|
||||||
|
'benchmark.form.nonLlmModel.tips': '基准测试目前仅支持 LLM 模型'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,5 +15,11 @@ export default {
|
|||||||
'providers.form.model.test': '测试',
|
'providers.form.model.test': '测试',
|
||||||
'providers.form.model.test.tips': '测试连接',
|
'providers.form.model.test.tips': '测试连接',
|
||||||
'providers.form.target.placeholder': '提供商/模型',
|
'providers.form.target.placeholder': '提供商/模型',
|
||||||
'providers.form.fallback.token': '备用 API Key'
|
'providers.form.fallback.token': '备用 API Key',
|
||||||
|
'providers.form.custombeckendUrl': '自定义 Base URL',
|
||||||
|
'providers.form.more': '+{count} 更多',
|
||||||
|
'providers.form.addAll': '添加全部',
|
||||||
|
'providers.form.rules.models': '请选择至少一个模型',
|
||||||
|
'providers.form.rules.tokens': '请输入有效的 API Key',
|
||||||
|
'providers.form.rules.model': '请选择模型'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const RightActions: React.FC<RightActionsProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const ButtonList = [
|
const ButtonList = [
|
||||||
{
|
{
|
||||||
label: 'common.button.export',
|
label: 'benchmark.table.export.results',
|
||||||
key: 'export',
|
key: 'export',
|
||||||
icon: (
|
icon: (
|
||||||
<IconFont type="icon-export" style={{ lineHeight: 1, fontSize: 16 }} />
|
<IconFont type="icon-export" style={{ lineHeight: 1, fontSize: 16 }} />
|
||||||
|
|||||||
@@ -8,17 +8,6 @@ import { BenchmarkStatusValueMap } from '../config';
|
|||||||
import { BenchmarkListItem as ListItem } from '../config/types';
|
import { BenchmarkListItem as ListItem } from '../config/types';
|
||||||
|
|
||||||
const actionList = [
|
const actionList = [
|
||||||
{
|
|
||||||
key: 'edit',
|
|
||||||
label: 'common.button.edit',
|
|
||||||
icon: icons.EditOutlined
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'common.button.stop',
|
|
||||||
key: 'stop',
|
|
||||||
icon: icons.Stop,
|
|
||||||
status: [BenchmarkStatusValueMap.QUEUED, BenchmarkStatusValueMap.Running]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'common.button.viewlog',
|
label: 'common.button.viewlog',
|
||||||
key: 'viewlog',
|
key: 'viewlog',
|
||||||
@@ -30,9 +19,20 @@ const actionList = [
|
|||||||
],
|
],
|
||||||
icon: <IconFont type="icon-logs" />
|
icon: <IconFont type="icon-logs" />
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'edit',
|
||||||
|
label: 'common.button.edit',
|
||||||
|
icon: icons.EditOutlined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'common.button.stop',
|
||||||
|
key: 'stop',
|
||||||
|
icon: icons.Stop,
|
||||||
|
status: [BenchmarkStatusValueMap.QUEUED, BenchmarkStatusValueMap.Running]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'export',
|
key: 'export',
|
||||||
label: 'common.button.export',
|
label: 'benchmark.table.export.results',
|
||||||
icon: (
|
icon: (
|
||||||
<IconFont type="icon-export" style={{ lineHeight: 1, fontSize: 16 }} />
|
<IconFont type="icon-export" style={{ lineHeight: 1, fontSize: 16 }} />
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,19 +3,22 @@ import { PageAction } from '@/config';
|
|||||||
import useAppUtils from '@/hooks/use-app-utils';
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
import {
|
import {
|
||||||
InstanceStatusMap,
|
InstanceStatusMap,
|
||||||
InstanceStatusMapValue
|
InstanceStatusMapValue,
|
||||||
|
modelCategoriesMap
|
||||||
} from '@/pages/llmodels/config';
|
} from '@/pages/llmodels/config';
|
||||||
import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-benchmark';
|
import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-benchmark';
|
||||||
import { useQueryModelInstancesList } from '@/pages/llmodels/services/use-query-model-instances';
|
import { useQueryModelInstancesList } from '@/pages/llmodels/services/use-query-model-instances';
|
||||||
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
|
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form, Tooltip } from 'antd';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useFormContext } from '../config/form-context';
|
import { useFormContext } from '../config/form-context';
|
||||||
import { FormData } from '../config/types';
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
|
// benchmark.form.nonLlmModel.tips
|
||||||
const InstanceNode = (props: any) => {
|
const InstanceNode = (props: any) => {
|
||||||
const { data: instance } = props;
|
const { data: instance } = props;
|
||||||
|
const intl = useIntl();
|
||||||
return instance.isLeaf ? (
|
return instance.isLeaf ? (
|
||||||
<span className="flex-center">
|
<span className="flex-center">
|
||||||
{instance.label}
|
{instance.label}
|
||||||
@@ -24,7 +27,17 @@ const InstanceNode = (props: any) => {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span>{instance.label}</span>
|
<>
|
||||||
|
{instance.disabled ? (
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({ id: 'benchmark.form.nonLlmModel.tips' })}
|
||||||
|
>
|
||||||
|
<span>{instance.label}</span>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<span>{instance.label}</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -99,6 +112,7 @@ const ModelInstanceForm: React.FC = () => {
|
|||||||
.map((model: any) => ({
|
.map((model: any) => ({
|
||||||
label: model.name,
|
label: model.name,
|
||||||
value: model.name,
|
value: model.name,
|
||||||
|
disabled: modelCategoriesMap.llm !== model.categories?.[0],
|
||||||
id: model.id,
|
id: model.id,
|
||||||
isLeaf: false,
|
isLeaf: false,
|
||||||
children: []
|
children: []
|
||||||
@@ -109,12 +123,17 @@ const ModelInstanceForm: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// preload instances for the first model
|
// preload instances for the first model
|
||||||
const instanceList = await fetchInstanceList({ id: modelOptions[0]?.id });
|
const selectedllmModel = modelOptions.find((model) => !model.disabled);
|
||||||
|
if (!selectedllmModel) {
|
||||||
|
setModelList(modelOptions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const instanceList = await fetchInstanceList({ id: selectedllmModel.id });
|
||||||
const instanceOptions = instanceList.map((instance: any) =>
|
const instanceOptions = instanceList.map((instance: any) =>
|
||||||
renderInstance(instance)
|
renderInstance(instance)
|
||||||
);
|
);
|
||||||
if (modelOptions[0]) {
|
if (selectedllmModel) {
|
||||||
modelOptions[0].children = [...instanceOptions] as never[];
|
selectedllmModel.children = [...instanceOptions] as never[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// init form value for model instance
|
// init form value for model instance
|
||||||
@@ -127,8 +146,8 @@ const ModelInstanceForm: React.FC = () => {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
handleOnChange(
|
handleOnChange(
|
||||||
[modelOptions[0].value, instanceOptions[0]?.value],
|
[selectedllmModel.value, instanceOptions[0]?.value],
|
||||||
[modelOptions[0], instanceOptions[0]]
|
[selectedllmModel, instanceOptions[0]]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ const Benchmark: React.FC = () => {
|
|||||||
} else if (val === 'stop') {
|
} else if (val === 'stop') {
|
||||||
handleStopBenchmark(row.id);
|
handleStopBenchmark(row.id);
|
||||||
} else if (val === 'export') {
|
} else if (val === 'export') {
|
||||||
exportData([row.id]);
|
exportData([row.id], row.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,7 +192,11 @@ const Benchmark: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleExportData = () => {
|
const handleExportData = () => {
|
||||||
exportData(rowSelection.selectedRowKeys);
|
const firstSelectedRow = dataSource.dataList.find(
|
||||||
|
(item) => item.id === rowSelection.selectedRowKeys[0]
|
||||||
|
);
|
||||||
|
const name = firstSelectedRow ? firstSelectedRow.name : 'benchmark';
|
||||||
|
exportData(rowSelection.selectedRowKeys, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { GPUSTACK_API_BASE_URL } from '@/config/settings';
|
import { GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||||
import { downloadFile } from '@/utils/download-stream';
|
import { downloadFile } from '@/utils/download-stream';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
import { EXPORT_BENCHMARK_LIST } from '../apis';
|
import { EXPORT_BENCHMARK_LIST } from '../apis';
|
||||||
|
|
||||||
const matchFilename = (disposition: string | null): string | undefined => {
|
const matchFilename = (disposition: string | null): string | undefined => {
|
||||||
@@ -12,7 +13,9 @@ const matchFilename = (disposition: string | null): string | undefined => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function useExportBenchmark() {
|
export function useExportBenchmark() {
|
||||||
const exportData = async (data: any[]) => {
|
const exportData = async (data: any[], name: string) => {
|
||||||
|
const date = dayjs().format('YYYYMMDD_HHmmss');
|
||||||
|
const fileName = `${name || 'benchmark'}_${date}`;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${GPUSTACK_API_BASE_URL}${EXPORT_BENCHMARK_LIST}`,
|
`${GPUSTACK_API_BASE_URL}${EXPORT_BENCHMARK_LIST}`,
|
||||||
@@ -26,8 +29,7 @@ export function useExportBenchmark() {
|
|||||||
);
|
);
|
||||||
// header
|
// header
|
||||||
const contentDispostion = res.headers.get('content-Disposition');
|
const contentDispostion = res.headers.get('content-Disposition');
|
||||||
const filename =
|
const filename = matchFilename(contentDispostion) || `${fileName}.yml`;
|
||||||
matchFilename(contentDispostion) || `benchmark-export.yml`;
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const blob = await res.blob();
|
const blob = await res.blob();
|
||||||
downloadFile(blob, filename);
|
downloadFile(blob, filename);
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ const CloudOptions: React.FC<{
|
|||||||
const { disabled } = props;
|
const { disabled } = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
const volumes = Form.useWatch(['cloud_options', volumeOptions.name], form);
|
const volumes =
|
||||||
|
Form.useWatch(['cloud_options', volumeOptions.name], form) || [];
|
||||||
const [validateStatusList, setValidateStatusList] = useState<
|
const [validateStatusList, setValidateStatusList] = useState<
|
||||||
{ [key: string]: statusType }[]
|
{ [key: string]: statusType }[]
|
||||||
>([]);
|
>([]);
|
||||||
@@ -45,10 +46,15 @@ const CloudOptions: React.FC<{
|
|||||||
form.setFieldValue(['cloud_options', volumeOptions.name], data);
|
form.setFieldValue(['cloud_options', volumeOptions.name], data);
|
||||||
toggleValidation(true);
|
toggleValidation(true);
|
||||||
};
|
};
|
||||||
console.log('disabled=====', disabled);
|
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={['cloud_options', volumeOptions.name as string]}
|
name={['cloud_options', volumeOptions.name as string]}
|
||||||
|
style={{
|
||||||
|
backgroundColor:
|
||||||
|
disabled && volumes?.length === 0
|
||||||
|
? 'var(--ant-color-bg-container-disabled)'
|
||||||
|
: ''
|
||||||
|
}}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
validator: listMapValidator
|
validator: listMapValidator
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import AutoTooltip from '@/components/auto-tooltip';
|
import AutoTooltip from '@/components/auto-tooltip';
|
||||||
import {
|
import OverlayScroller from '@/components/overlay-scroller';
|
||||||
CheckCircleOutlined,
|
import { CheckCircleOutlined } from '@ant-design/icons';
|
||||||
CloseCircleOutlined,
|
import { useIntl } from '@umijs/max';
|
||||||
WarningOutlined
|
import { Flex, Popover, Tag } from 'antd';
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { Flex, Tag } from 'antd';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { categoryConfig } from '../../_components/model-tag';
|
import { categoryConfig } from '../../_components/model-tag';
|
||||||
@@ -17,47 +15,101 @@ interface ProviderModelProps {
|
|||||||
const ProviderModels: React.FC<ProviderModelProps> = ({ dataList }) => {
|
const ProviderModels: React.FC<ProviderModelProps> = ({ dataList }) => {
|
||||||
const iconsMap = {
|
const iconsMap = {
|
||||||
accessible: <CheckCircleOutlined />,
|
accessible: <CheckCircleOutlined />,
|
||||||
inaccessible: <CloseCircleOutlined />,
|
inaccessible: null,
|
||||||
none: <WarningOutlined />
|
none: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const head12Items = dataList.slice(0, 8);
|
||||||
|
const restItems = dataList.slice(8);
|
||||||
|
|
||||||
|
const renderModels = (dataList: ProviderModel[]) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{dataList.map((model, index) => (
|
||||||
|
<Tag
|
||||||
|
key={index}
|
||||||
|
icon={
|
||||||
|
_.isBoolean(model.accessible)
|
||||||
|
? iconsMap[model.accessible ? 'accessible' : 'inaccessible']
|
||||||
|
: iconsMap['none']
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 4
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
color={
|
||||||
|
model.accessible === true ? 'var(--ant-color-success)' : 'default'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="flex-center">
|
||||||
|
<AutoTooltip ghost maxWidth={'120px'}>
|
||||||
|
{model.name}
|
||||||
|
</AutoTooltip>
|
||||||
|
<span style={{ marginLeft: 8 }}>
|
||||||
|
{categoryConfig[model.category]?.icon}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Flex gap="8px" wrap="wrap">
|
<div>
|
||||||
{dataList.map((model) => (
|
<Flex gap="8px" wrap="wrap">
|
||||||
<Tag
|
{renderModels(head12Items)}
|
||||||
key={model.name}
|
{restItems.length > 0 && (
|
||||||
icon={
|
<Popover
|
||||||
_.isBoolean(model.accessible)
|
placement="right"
|
||||||
? iconsMap[model.accessible ? 'accessible' : 'inaccessible']
|
content={
|
||||||
: iconsMap['none']
|
<OverlayScroller
|
||||||
}
|
maxHeight={420}
|
||||||
variant="outlined"
|
styles={{
|
||||||
styles={{
|
wrapper: {
|
||||||
root: {
|
paddingInlineStart: 0
|
||||||
backgroundColor: 'transparent',
|
}
|
||||||
display: 'flex',
|
}}
|
||||||
alignItems: 'center',
|
>
|
||||||
borderRadius: 4
|
<Flex gap="8px" wrap="wrap">
|
||||||
|
{renderModels(restItems)}
|
||||||
|
</Flex>
|
||||||
|
</OverlayScroller>
|
||||||
}
|
}
|
||||||
}}
|
styles={{
|
||||||
color={
|
root: { maxWidth: '400px' },
|
||||||
model.accessible === true
|
container: {
|
||||||
? 'success'
|
paddingInlineEnd: 4
|
||||||
: model.accessible === false
|
}
|
||||||
? 'error'
|
}}
|
||||||
: 'warning'
|
>
|
||||||
}
|
<Tag
|
||||||
>
|
variant="outlined"
|
||||||
<span className="flex-center">
|
styles={{
|
||||||
<AutoTooltip ghost maxWidth={'120px'}>
|
root: {
|
||||||
{model.name}
|
width: 'fit-content',
|
||||||
</AutoTooltip>
|
backgroundColor: 'transparent',
|
||||||
<span style={{ marginLeft: 8 }}>
|
display: 'flex',
|
||||||
{categoryConfig[model.category]?.icon}
|
alignItems: 'center',
|
||||||
</span>
|
borderRadius: 4
|
||||||
</span>
|
}
|
||||||
</Tag>
|
}}
|
||||||
))}
|
>
|
||||||
</Flex>
|
{intl.formatMessage(
|
||||||
|
{ id: 'providers.form.more' },
|
||||||
|
{ count: restItems.length }
|
||||||
|
)}
|
||||||
|
</Tag>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface FormData {
|
|||||||
api_key: string;
|
api_key: string;
|
||||||
config: {
|
config: {
|
||||||
type: maasProviderType;
|
type: maasProviderType;
|
||||||
|
openaiCustomUrl?: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,7 @@ const AccessToken = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: false,
|
required: false,
|
||||||
message: getRuleMessage(
|
message: getRuleMessage('input', 'providers.form.tokens.title')
|
||||||
'input',
|
|
||||||
intl.formatMessage({ id: 'providers.form.tokens.title' })
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ import useAppUtils from '@/hooks/use-app-utils';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import ProviderLogo from '../components/provider-logo';
|
import ProviderLogo from '../components/provider-logo';
|
||||||
import { maasProviderOptions } from '../config/providers';
|
import { maasProviderOptions, ProviderEnum } from '../config/providers';
|
||||||
import { FormData } from '../config/types';
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
const Basic = () => {
|
const Basic: React.FC<{
|
||||||
|
onAPIKeyBlur?: (e: any) => void;
|
||||||
|
}> = ({ onAPIKeyBlur }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const form = Form.useFormInstance<FormData>();
|
const form = Form.useFormInstance<FormData>();
|
||||||
|
const providerType = Form.useWatch(['config', 'type'], form);
|
||||||
const { getRuleMessage } = useAppUtils();
|
const { getRuleMessage } = useAppUtils();
|
||||||
|
|
||||||
const optionRender = (option: any) => {
|
const optionRender = (option: any) => {
|
||||||
@@ -30,7 +33,17 @@ const Basic = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Item<FormData> name="name" data-field="name">
|
<Form.Item<FormData>
|
||||||
|
name="name"
|
||||||
|
data-field="name"
|
||||||
|
required
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: getRuleMessage('input', 'common.table.name')
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
<SealInput.Input
|
<SealInput.Input
|
||||||
required
|
required
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
@@ -43,10 +56,7 @@ const Basic = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: getRuleMessage(
|
message: getRuleMessage('select', 'common.table.type')
|
||||||
'select',
|
|
||||||
intl.formatMessage({ id: 'common.table.type' })
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@@ -63,6 +73,16 @@ const Basic = () => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
{providerType === ProviderEnum.OPENAI && (
|
||||||
|
<Form.Item<FormData> name={['config', 'openaiCustomUrl']}>
|
||||||
|
<SealInput.Input
|
||||||
|
placeholder="http://<your-inference-server>/v1"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'providers.form.custombeckendUrl'
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="api_key"
|
name="api_key"
|
||||||
rules={[
|
rules={[
|
||||||
@@ -74,6 +94,7 @@ const Basic = () => {
|
|||||||
>
|
>
|
||||||
<SealInput.Password
|
<SealInput.Password
|
||||||
required
|
required
|
||||||
|
onBlur={onAPIKeyBlur}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'providers.form.tokens.title'
|
id: 'providers.form.tokens.title'
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -107,7 +107,9 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
|||||||
api_key: currentData.api_tokens?.[0] || '',
|
api_key: currentData.api_tokens?.[0] || '',
|
||||||
api_tokens: currentData.api_tokens?.slice(1) || [],
|
api_tokens: currentData.api_tokens?.slice(1) || [],
|
||||||
proxy_enabled: !!currentData.proxy_url,
|
proxy_enabled: !!currentData.proxy_url,
|
||||||
custom_config: json2Yaml(_.omit(currentData.config, ['type']) || {})
|
custom_config: json2Yaml(
|
||||||
|
_.omit(currentData.config, ['type', 'openaiCustomUrl']) || {}
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [form, currentData, action]);
|
}, [form, currentData, action]);
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import SealSelect from '@/components/seal-form/seal-select';
|
|||||||
import { categoryOptions } from '@/pages/llmodels/config';
|
import { categoryOptions } from '@/pages/llmodels/config';
|
||||||
import {
|
import {
|
||||||
CheckCircleFilled,
|
CheckCircleFilled,
|
||||||
CloseCircleFilled,
|
LoadingOutlined,
|
||||||
LoadingOutlined
|
WarningFilled
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Form, Tooltip } from 'antd';
|
import { Button, Form, Tooltip } from 'antd';
|
||||||
@@ -27,9 +27,15 @@ const SelectWrapper = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
interface ModelItemProps {
|
interface ModelItemProps {
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
onChange: (data: ProviderModel) => void;
|
onChange: (data: ProviderModel) => void;
|
||||||
providerModelList: Global.BaseOption<string>[];
|
providerModelList: Global.BaseOption<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
category: string;
|
||||||
|
accessible: boolean;
|
||||||
|
}
|
||||||
|
>[];
|
||||||
selectedModelList: ProviderModel[];
|
selectedModelList: ProviderModel[];
|
||||||
item: ProviderModel;
|
item: ProviderModel;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
@@ -63,10 +69,9 @@ const ModelItem: React.FC<ModelItemProps> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnChange = (value: string) => {
|
const handleOnChange = (value: string, option: any) => {
|
||||||
onChange({
|
onChange({
|
||||||
...item,
|
...option,
|
||||||
accessible: null,
|
|
||||||
name: value
|
name: value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -79,20 +84,26 @@ const ModelItem: React.FC<ModelItemProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderSuffixIcon = () => {
|
const renderSuffixIcon = () => {
|
||||||
|
console.log(
|
||||||
|
'testLoading',
|
||||||
|
testLoading,
|
||||||
|
item.accessible,
|
||||||
|
item.accessible === false
|
||||||
|
);
|
||||||
if (testLoading) {
|
if (testLoading) {
|
||||||
return <LoadingOutlined />;
|
return <LoadingOutlined />;
|
||||||
}
|
}
|
||||||
if (item.accessible === true) {
|
if (item.accessible === true) {
|
||||||
return (
|
return (
|
||||||
<CheckCircleFilled
|
<CheckCircleFilled
|
||||||
style={{ color: 'var(--ant-color-success)', fontSize: 16 }}
|
style={{ color: 'var(--ant-color-success)', fontSize: 14 }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (item.accessible === false) {
|
if (item.accessible === false) {
|
||||||
return (
|
return (
|
||||||
<CloseCircleFilled
|
<WarningFilled
|
||||||
style={{ color: 'var(--ant-color-error)', fontSize: 16 }}
|
style={{ color: 'var(--ant-color-error)', fontSize: 14 }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import MetadataList from '@/components/metadata-list';
|
import MetadataList from '@/components/metadata-list';
|
||||||
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
|
import { useRef } from 'react';
|
||||||
import { FormData, ProviderModel } from '../config/types';
|
import { FormData, ProviderModel } from '../config/types';
|
||||||
import { useQueryProviderModels } from '../hooks/use-query-provider-models';
|
import { useQueryProviderModels } from '../hooks/use-query-provider-models';
|
||||||
import ModelItem from './model-item';
|
import ModelItem from './model-item';
|
||||||
@@ -11,15 +13,26 @@ const SupportedModels = () => {
|
|||||||
useQueryProviderModels();
|
useQueryProviderModels();
|
||||||
const form = Form.useFormInstance<FormData>();
|
const form = Form.useFormInstance<FormData>();
|
||||||
const modelList = Form.useWatch('models', form) || [];
|
const modelList = Form.useWatch('models', form) || [];
|
||||||
|
const prevAPIKeyRef = useRef<string>('');
|
||||||
|
const { getRuleMessage } = useAppUtils();
|
||||||
|
|
||||||
const handleOpenChange = async (open: boolean) => {
|
const handleOpenChange = async (open: boolean) => {
|
||||||
try {
|
try {
|
||||||
await form.validateFields(['api_key']);
|
await form.validateFields(['api_key']);
|
||||||
|
|
||||||
if (open && providerModelList.length === 0) {
|
const currentAPIKey = form.getFieldValue('api_key') || '';
|
||||||
|
|
||||||
|
// Avoid repeated requests with the same API key
|
||||||
|
if (
|
||||||
|
open &&
|
||||||
|
providerModelList.length === 0 &&
|
||||||
|
prevAPIKeyRef.current !== currentAPIKey &&
|
||||||
|
currentAPIKey
|
||||||
|
) {
|
||||||
|
prevAPIKeyRef.current = currentAPIKey;
|
||||||
fetchProviderModels({
|
fetchProviderModels({
|
||||||
data: {
|
data: {
|
||||||
api_token: form.getFieldValue('api_key') || '',
|
api_token: currentAPIKey,
|
||||||
config: {
|
config: {
|
||||||
type: form.getFieldValue(['config', 'type']) || ''
|
type: form.getFieldValue(['config', 'type']) || ''
|
||||||
}
|
}
|
||||||
@@ -55,7 +68,33 @@ const SupportedModels = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Item name="models" data-field="supportedModels">
|
<Form.Item
|
||||||
|
name="models"
|
||||||
|
data-field="supportedModels"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: async (_, value) => {
|
||||||
|
if (!value || value.length === 0) {
|
||||||
|
return Promise.reject(
|
||||||
|
new Error(
|
||||||
|
getRuleMessage('input', 'providers.form.rules.models')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.some((item: ProviderModel) => !item.name)) {
|
||||||
|
return Promise.reject(
|
||||||
|
new Error(
|
||||||
|
getRuleMessage('select', 'providers.form.rules.model')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
<MetadataList
|
<MetadataList
|
||||||
styles={{
|
styles={{
|
||||||
wrapper: {
|
wrapper: {
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ export const useQueryProviderModels = () => {
|
|||||||
setProviderModelList(
|
setProviderModelList(
|
||||||
response.data?.map((item: any) => ({
|
response.data?.map((item: any) => ({
|
||||||
label: item.id,
|
label: item.id,
|
||||||
value: item.id
|
value: item.id,
|
||||||
|
accessible: item.accessible,
|
||||||
|
category: item.categories?.[0] || ''
|
||||||
})) || []
|
})) || []
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const TargetStatusLabelMap = {
|
|||||||
|
|
||||||
export const TargetStatus: Record<string, StatusType> = {
|
export const TargetStatus: Record<string, StatusType> = {
|
||||||
[TargetStatusValueMap.Active]: StatusMaps.success,
|
[TargetStatusValueMap.Active]: StatusMaps.success,
|
||||||
[TargetStatusValueMap.Unavailable]: StatusMaps.error
|
[TargetStatusValueMap.Unavailable]: StatusMaps.warning
|
||||||
};
|
};
|
||||||
|
|
||||||
// actions for each row
|
// actions for each row
|
||||||
|
|||||||
@@ -43,5 +43,8 @@ export interface RouteTarget {
|
|||||||
name: string;
|
name: string;
|
||||||
route_name: string;
|
route_name: string;
|
||||||
route_id: number;
|
route_id: number;
|
||||||
|
provider_id: number;
|
||||||
|
provider_model_name: string;
|
||||||
|
fallback_status_codes: string[];
|
||||||
state: string;
|
state: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import MetadataList from '@/components/metadata-list';
|
import MetadataList from '@/components/metadata-list';
|
||||||
import SealCascader from '@/components/seal-form/seal-cascader';
|
import SealCascader from '@/components/seal-form/seal-cascader';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@@ -10,6 +11,7 @@ import useTargetSourceModels from '../hooks/use-target-source-models';
|
|||||||
|
|
||||||
const TargetsForm = forwardRef((props, ref) => {
|
const TargetsForm = forwardRef((props, ref) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const { getRuleMessage } = useAppUtils();
|
||||||
const { sourceModels, loading, fetchSourceModels } = useTargetSourceModels();
|
const { sourceModels, loading, fetchSourceModels } = useTargetSourceModels();
|
||||||
const form = Form.useFormInstance<FormData>();
|
const form = Form.useFormInstance<FormData>();
|
||||||
const targets = Form.useWatch('targets', form) || [];
|
const targets = Form.useWatch('targets', form) || [];
|
||||||
@@ -38,19 +40,11 @@ const TargetsForm = forwardRef((props, ref) => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const handleTargetsChange = (value: any[], index: number, options: any[]) => {
|
const handleTargetsChange = (value: any[], index: number, options: any[]) => {
|
||||||
console.log(
|
|
||||||
'handleTargetsChange:',
|
|
||||||
value,
|
|
||||||
index,
|
|
||||||
options,
|
|
||||||
dataList,
|
|
||||||
sourceModels
|
|
||||||
);
|
|
||||||
const selectedOption =
|
const selectedOption =
|
||||||
options?.find?.((opt) => opt.value === value[1]) || {};
|
options?.find?.((opt) => opt.value === value[1]) || {};
|
||||||
const targetList = [...targets];
|
const targetList = [...targets];
|
||||||
targetList[index] = {
|
targetList[index] = {
|
||||||
weight: targetList[index]?.weight || null,
|
weight: targetList[index]?.weight,
|
||||||
...selectedOption?.data
|
...selectedOption?.data
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,11 +62,19 @@ const TargetsForm = forwardRef((props, ref) => {
|
|||||||
const newDataList = [
|
const newDataList = [
|
||||||
...dataList,
|
...dataList,
|
||||||
{
|
{
|
||||||
weight: null,
|
weight: 100,
|
||||||
value: []
|
value: []
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
setDataList(newDataList);
|
setDataList(newDataList);
|
||||||
|
const newTargets = [
|
||||||
|
...targets,
|
||||||
|
{
|
||||||
|
weight: 100,
|
||||||
|
value: []
|
||||||
|
}
|
||||||
|
];
|
||||||
|
form.setFieldValue('targets', newTargets);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnDelete = (index: number, item: any) => {
|
const handleOnDelete = (index: number, item: any) => {
|
||||||
@@ -85,13 +87,19 @@ const TargetsForm = forwardRef((props, ref) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleFallbackChange = (value: any[], options?: any[]) => {
|
const handleFallbackChange = (value: any[], options?: any[]) => {
|
||||||
|
if (!value || value.length === 0) {
|
||||||
|
form.setFieldValue('fallback_target', null);
|
||||||
|
setFallbackValues({
|
||||||
|
value: []
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
const selectedOption =
|
const selectedOption =
|
||||||
options?.find?.((opt) => opt.value === value[1]) || {};
|
options?.find?.((opt) => opt.value === value[1]) || {};
|
||||||
|
|
||||||
form.setFieldValue('fallback_target', {
|
form.setFieldValue('fallback_target', {
|
||||||
...selectedOption?.data
|
...selectedOption?.data
|
||||||
});
|
});
|
||||||
console.log('handleFallbackChange:', value, dataList);
|
|
||||||
setFallbackValues({
|
setFallbackValues({
|
||||||
value: value
|
value: value
|
||||||
});
|
});
|
||||||
@@ -163,27 +171,29 @@ const TargetsForm = forwardRef((props, ref) => {
|
|||||||
name="targets"
|
name="targets"
|
||||||
data-field="targets"
|
data-field="targets"
|
||||||
rules={[
|
rules={[
|
||||||
({ getFieldValue }) => ({
|
{
|
||||||
validator(rule, value) {
|
validator(rule, value) {
|
||||||
if (_.keys(value).length > 0) {
|
if (value && value?.length > 0) {
|
||||||
if (_.some(_.keys(value), (k: string) => !value[k])) {
|
if (_.some(value, (item: any) => !item.weight)) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
intl.formatMessage(
|
getRuleMessage('input', 'routes.form.target.weight')
|
||||||
{
|
);
|
||||||
id: 'common.validate.value'
|
}
|
||||||
},
|
|
||||||
{
|
if (
|
||||||
name: intl.formatMessage({
|
_.some(
|
||||||
id: 'models.form.selector'
|
dataList,
|
||||||
})
|
(item: any) => !item.value || item.value.length === 0
|
||||||
}
|
)
|
||||||
)
|
) {
|
||||||
|
return Promise.reject(
|
||||||
|
getRuleMessage('input', 'providers.form.target.placeholder')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<MetadataList
|
<MetadataList
|
||||||
@@ -203,6 +213,9 @@ const TargetsForm = forwardRef((props, ref) => {
|
|||||||
<SealCascader
|
<SealCascader
|
||||||
required
|
required
|
||||||
showSearch
|
showSearch
|
||||||
|
status={
|
||||||
|
!item.value || item.value.length === 0 ? 'error' : 'success'
|
||||||
|
}
|
||||||
expandTrigger="hover"
|
expandTrigger="hover"
|
||||||
multiple={false}
|
multiple={false}
|
||||||
alwaysFocus={true}
|
alwaysFocus={true}
|
||||||
@@ -235,6 +248,8 @@ const TargetsForm = forwardRef((props, ref) => {
|
|||||||
<SealInput.Number
|
<SealInput.Number
|
||||||
style={{ flex: 100 }}
|
style={{ flex: 100 }}
|
||||||
min={0}
|
min={0}
|
||||||
|
step={1}
|
||||||
|
status={item.weight === null ? 'error' : 'success'}
|
||||||
value={item.weight}
|
value={item.weight}
|
||||||
onChange={(value) => handleOnWeightChange(value, index)}
|
onChange={(value) => handleOnWeightChange(value, index)}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={intl.formatMessage({
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
|
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { RouteItem as ListItem } from '../config/types';
|
import { RouteItem as ListItem } from '../config/types';
|
||||||
|
|
||||||
const useAccessControl = () => {
|
const useAccessControl = () => {
|
||||||
|
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||||
const [openModalStatus, setOpenModalStatus] = useState<{
|
const [openModalStatus, setOpenModalStatus] = useState<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
@@ -27,6 +29,7 @@ const useAccessControl = () => {
|
|||||||
currentData,
|
currentData,
|
||||||
title: title
|
title: title
|
||||||
});
|
});
|
||||||
|
saveScrollHeight();
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
@@ -36,6 +39,7 @@ const useAccessControl = () => {
|
|||||||
currentData: undefined,
|
currentData: undefined,
|
||||||
title: ''
|
title: ''
|
||||||
});
|
});
|
||||||
|
restoreScrollHeight();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
|
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { RouteItem as ListItem } from '../config/types';
|
import { RouteItem as ListItem } from '../config/types';
|
||||||
|
|
||||||
const useCreateRoute = (options?: { refresh: () => void }) => {
|
const useCreateRoute = (options?: { refresh: () => void }) => {
|
||||||
|
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||||
const [openModalStatus, setOpenModalStatus] = useState<{
|
const [openModalStatus, setOpenModalStatus] = useState<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
@@ -27,6 +29,7 @@ const useCreateRoute = (options?: { refresh: () => void }) => {
|
|||||||
currentData,
|
currentData,
|
||||||
title: title
|
title: title
|
||||||
});
|
});
|
||||||
|
saveScrollHeight();
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
@@ -36,6 +39,7 @@ const useCreateRoute = (options?: { refresh: () => void }) => {
|
|||||||
currentData: undefined,
|
currentData: undefined,
|
||||||
title: ''
|
title: ''
|
||||||
});
|
});
|
||||||
|
restoreScrollHeight();
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ const ModelRoutes: React.FC = () => {
|
|||||||
loadend={dataSource.loadend}
|
loadend={dataSource.loadend}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
childParentKey="access_id"
|
childParentKey="route_id"
|
||||||
expandable={true}
|
expandable={true}
|
||||||
empty={
|
empty={
|
||||||
<NoResult
|
<NoResult
|
||||||
|
|||||||
Reference in New Issue
Block a user