style: adjust providers style

This commit is contained in:
jialin
2026-02-03 18:21:08 +08:00
parent 6fd886b27a
commit 51bab71291
18 changed files with 197 additions and 130 deletions
@@ -6,6 +6,7 @@ import React from 'react';
import styled from 'styled-components';
interface WrapperProps {
required?: boolean;
label?: React.ReactNode;
description?: React.ReactNode;
labelExtra?: React.ReactNode;
@@ -45,6 +46,7 @@ const ButtonWrapper = styled.div`
`;
const Wrapper: React.FC<WrapperProps> = ({
required,
children,
label,
description,
@@ -61,6 +63,7 @@ const Wrapper: React.FC<WrapperProps> = ({
{label && (
<span className="label">
<LabelInfo
required={required}
label={label}
description={description}
labelExtra={labelExtra}
+17 -1
View File
@@ -1,9 +1,10 @@
import _ from 'lodash';
import React from 'react';
import React, { useEffect } from 'react';
import Wrapper from '../label-selector/wrapper';
import ListItem from './list-item';
interface ListInputProps {
required?: boolean;
dataList: string[];
label?: React.ReactNode;
description?: React.ReactNode;
@@ -12,6 +13,10 @@ interface ListInputProps {
placeholder?: string;
labelExtra?: React.ReactNode;
trim?: boolean;
styles?: {
wrapper?: React.CSSProperties;
item?: React.CSSProperties;
};
onChange: (data: string[]) => void;
onBlur?: (e: any, index: number) => void;
onDelete?: (index: number) => void;
@@ -36,6 +41,8 @@ const ListInput: React.FC<ListInputProps> = (props) => {
options,
labelExtra,
trim = true,
styles,
required,
renderItem
} = props;
const [list, setList] = React.useState<{ value: string; uid: number }[]>([]);
@@ -86,9 +93,17 @@ const ListInput: React.FC<ListInputProps> = (props) => {
}
}, [dataList]);
useEffect(() => {
if (required && list.length === 0) {
handleOnAdd();
}
}, [required]);
return (
<Wrapper
styles={styles}
label={label}
required={required}
description={description}
labelExtra={labelExtra}
onAdd={handleOnAdd}
@@ -98,6 +113,7 @@ const ListInput: React.FC<ListInputProps> = (props) => {
{_.map(list, (item: any, index: number) => {
return (
<ListItem
required={required && list.length === 1}
placeholder={props.placeholder}
options={options}
data={item}
+12 -8
View File
@@ -21,6 +21,7 @@ interface LabelItemProps {
options?: Global.HintOptions[];
trim?: boolean;
data?: any;
required?: boolean;
}
const ListItem: React.FC<LabelItemProps> = (props) => {
@@ -33,6 +34,7 @@ const ListItem: React.FC<LabelItemProps> = (props) => {
options,
trim = true,
data,
required,
renderItem
} = props;
@@ -58,14 +60,16 @@ const ListItem: React.FC<LabelItemProps> = (props) => {
placeholder={props.placeholder}
/>
)}
<Button
size="small"
className="btn"
type="default"
shape="circle"
icon={<MinusOutlined />}
onClick={onRemove}
/>
{!required && (
<Button
size="small"
className="btn"
type="default"
shape="circle"
icon={<MinusOutlined />}
onClick={onRemove}
/>
)}
</div>
);
};
+1 -1
View File
@@ -85,7 +85,7 @@ const SelectWrapper = styled.div`
}
.ant-select .ant-select-input {
top: 5px;
top: -5px !important;
}
.ant-select-placeholder {
position: absolute;
+10 -2
View File
@@ -2,6 +2,14 @@ export default {
'providers.title': 'Providers',
'providers.button.add': 'Add Provider',
'providers.table.providerName': 'Provider',
'providers.table.modelsCounts': 'Models Count',
'providers.table.proxy': 'Proxy'
'providers.table.models': 'Models',
'providers.table.proxy': 'Proxy',
'providers.form.tokens.title': ' API Tokens',
'providers.form.tokens.add': 'Add API Token',
'providers.form.models.add': 'Add Model',
'providers.form.proxy.enable': 'Enable Proxy',
'providers.form.proxy.url': 'Proxy URL',
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
'providers.form.customConfig': 'Custom Configuration',
'providers.form.model.test': 'Test Connection'
};
+10 -2
View File
@@ -2,6 +2,14 @@ export default {
'providers.title': 'Providers',
'providers.button.add': 'Add Provider',
'providers.table.providerName': 'Provider',
'providers.table.modelsCounts': 'Models Count',
'providers.table.proxy': 'Proxy'
'providers.table.models': 'Models',
'providers.table.proxy': 'Proxy',
'providers.form.tokens.title': ' API Tokens',
'providers.form.tokens.add': 'Add API Token',
'providers.form.models.add': 'Add Model',
'providers.form.proxy.enable': 'Enable Proxy',
'providers.form.proxy.url': 'Proxy URL',
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
'providers.form.customConfig': 'Custom Configuration',
'providers.form.model.test': 'Test Connection'
};
+10 -2
View File
@@ -2,6 +2,14 @@ export default {
'providers.title': 'Providers',
'providers.button.add': 'Add Provider',
'providers.table.providerName': 'Provider',
'providers.table.modelsCounts': 'Models Count',
'providers.table.proxy': 'Proxy'
'providers.table.models': 'Models',
'providers.table.proxy': 'Proxy',
'providers.form.tokens.title': ' API Tokens',
'providers.form.tokens.add': 'Add API Token',
'providers.form.models.add': 'Add Model',
'providers.form.proxy.enable': 'Enable Proxy',
'providers.form.proxy.url': 'Proxy URL',
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
'providers.form.customConfig': 'Custom Configuration',
'providers.form.model.test': 'Test Connection'
};
+10 -2
View File
@@ -2,6 +2,14 @@ export default {
'providers.title': '提供商',
'providers.button.add': '添加提供商',
'providers.table.providerName': '提供商',
'providers.table.modelsCounts': '模型数量',
'providers.table.proxy': '代理'
'providers.table.models': '模型',
'providers.table.proxy': '代理',
'providers.form.tokens.title': 'API 令牌',
'providers.form.tokens.add': '添加 API 令牌',
'providers.form.models.add': '添加模型',
'providers.form.proxy.enable': '启用代理',
'providers.form.proxy.url': '代理 URL',
'providers.form.proxy.timeout': '代理超时(秒)',
'providers.form.customConfig': '自定义配置',
'providers.form.model.test': '测试连接'
};
@@ -3,7 +3,7 @@ import { modelCategoriesMap } from '@/pages/llmodels/config';
import {
CheckCircleOutlined,
CloseCircleOutlined,
ExclamationCircleOutlined
WarningOutlined
} from '@ant-design/icons';
import { Flex, Tag } from 'antd';
import React from 'react';
@@ -39,6 +39,21 @@ const models: {
name: 'model-4',
status: 'accessible',
type: modelCategoriesMap.llm
},
{
name: 'model-5',
status: 'inaccessible',
type: modelCategoriesMap.embedding
},
{
name: 'model-6model-6model-6model-6model-6model-6model-6',
status: 'none',
type: modelCategoriesMap.image
},
{
name: 'model-7',
status: 'accessible',
type: modelCategoriesMap.llm
}
];
@@ -46,7 +61,7 @@ const ProviderModels: React.FC<ProviderModelProps> = () => {
const iconsMap = {
accessible: <CheckCircleOutlined />,
inaccessible: <CloseCircleOutlined />,
none: <ExclamationCircleOutlined />
none: <WarningOutlined />
};
return (
<div style={{ paddingInline: 16 }}>
@@ -58,16 +73,18 @@ const ProviderModels: React.FC<ProviderModelProps> = () => {
variant="outlined"
styles={{
root: {
backgroundColor: 'transparent',
display: 'flex',
alignItems: 'center'
alignItems: 'center',
borderRadius: 4
}
}}
color={
model.status === 'accessible'
? 'success'
: model.status === 'inaccessible'
? 'red'
: 'default'
? 'error'
: 'warning'
}
>
<span className="flex-center">
+1 -1
View File
@@ -52,7 +52,7 @@ export const mockDataList: MaasProviderItem[] = [
name: 'Anthropic',
state: 'inactive',
state_message: 'Provider is inactive',
provider: 'anthropic',
provider: 'claude',
proxy_url: {},
builtIn: true
}
+13 -21
View File
@@ -1,26 +1,5 @@
import { maasProviderType } from '.';
export interface MaasProviderItem {
id: number;
created_at: string;
updated_at: string;
deleted_at: string;
name: string;
description: string;
api_tokens: string[];
timeout: number;
config: {
type: maasProviderType;
[key: string]: any;
};
support_models: unknown[];
proxy_url: string;
proxy_timeout: number;
builtin: boolean;
provider_model_count: number;
api_token_count: number;
}
export interface FormData {
name: string;
description: string;
@@ -30,3 +9,16 @@ export interface FormData {
[key: string]: any;
};
}
export interface MaasProviderItem extends FormData {
id: number;
created_at: string;
updated_at: string;
deleted_at: string;
timeout: number;
support_models: unknown[];
proxy_url: string;
proxy_timeout: number;
builtin: boolean;
provider_model_count: number;
api_token_count: number;
}
+17 -3
View File
@@ -1,20 +1,34 @@
import ListInput from '@/components/list-input';
import useAppUtils from '@/hooks/use-app-utils';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import { FormData } from '../config/types';
const AccessToken = () => {
const intl = useIntl();
const { getRuleMessage } = useAppUtils();
const form = Form.useFormInstance<FormData>();
const handleOnChange = (values: string[]) => {};
return (
<>
<Form.Item name="tokens">
<Form.Item
name="tokens"
rules={[
{
required: true,
message: getRuleMessage(
'input',
intl.formatMessage({ id: 'providers.form.tokens.title' })
)
}
]}
>
<ListInput
btnText="Add Token"
label="Access Tokens"
required={true}
btnText={intl.formatMessage({ id: 'providers.form.tokens.add' })}
label={intl.formatMessage({ id: 'providers.form.tokens.title' })}
dataList={[]}
onChange={handleOnChange}
></ListInput>
@@ -45,7 +45,7 @@ const AdvanceConfig: React.FC<{
</Form.Item>
<YamlEditor
ref={editorRef}
title={'Custom Config (YAML)'}
title={`${intl.formatMessage({ id: 'providers.form.customConfig' })} (YAML)`}
value={fileContent}
height={300}
onUpload={(content) => {
+5 -5
View File
@@ -47,14 +47,14 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
const segmentOptions = [
{
value: TABKeysMap.BASIC,
label: 'Basic',
label: intl.formatMessage({ id: 'common.title.basicInfo' }),
icon: <IconFont type="icon-basic" />,
field: 'name'
},
{
value: TABKeysMap.SUPPORTEDMODELS,
label: 'Supported Models',
icon: <IconFont type="icon-speed" />,
label: intl.formatMessage({ id: 'providers.table.models' }),
icon: <IconFont type="icon-models" />,
field: 'supportedModels'
},
{
@@ -125,13 +125,13 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
items={[
{
key: TABKeysMap.SUPPORTEDMODELS,
label: 'Supported Models',
label: intl.formatMessage({ id: 'providers.table.models' }),
forceRender: true,
children: <SupportedModels></SupportedModels>
},
{
key: TABKeysMap.ADVANCED,
label: 'Advanced',
label: intl.formatMessage({ id: 'resources.form.advanced' }),
forceRender: true,
children: (
<AdvanceConfig
+18 -4
View File
@@ -1,11 +1,13 @@
import CheckboxField from '@/components/seal-form/checkbox-field';
import SealInput from '@/components/seal-form/seal-input';
import useAppUtils from '@/hooks/use-app-utils';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import { useFormContext } from '../config/form-context';
const AdvanceConfig = () => {
const { getRuleMessage } = useAppUtils();
const form = Form.useFormInstance();
const intl = useIntl();
const { action } = useFormContext();
@@ -32,21 +34,33 @@ const AdvanceConfig = () => {
style={{ marginBottom: 8 }}
>
<CheckboxField
label={'Enable Proxy'}
label={intl.formatMessage({ id: 'providers.form.proxy.enable' })}
onChange={handleSpeculativeEnabledChange}
></CheckboxField>
</Form.Item>
{proxyConfigEnabled && (
<>
<Form.Item name={['proxy_config', 'url']}>
<Form.Item
name={['proxy_config', 'url']}
rules={[
{
required: true,
message: getRuleMessage(
'input',
intl.formatMessage({ id: 'providers.form.proxy.url' })
)
}
]}
>
<SealInput.Input
label={'Proxy URL'}
required
label={intl.formatMessage({ id: 'providers.form.proxy.url' })}
placeholder="http://proxy.example.com:8080"
></SealInput.Input>
</Form.Item>
<Form.Item name={['proxy_config', 'timeout']}>
<SealInput.Number
label={'Proxy Timeout'}
label={intl.formatMessage({ id: 'providers.form.proxy.timeout' })}
placeholder="30"
></SealInput.Number>
</Form.Item>
@@ -60,7 +60,7 @@ const SupportedModels = () => {
loading={data.loading}
onClick={() => handleTestModel(data)}
>
Test
{intl.formatMessage({ id: 'providers.form.model.test' })}
</Button>
</SelectWrapper>
);
@@ -73,8 +73,12 @@ const SupportedModels = () => {
<Form.Item name="models">
<div data-field="supportedModels"></div>
<ListInput
btnText="Add Model"
label="Supported Models"
styles={{
wrapper: {
paddingTop: 14
}
}}
btnText={intl.formatMessage({ id: 'providers.form.models.add' })}
dataList={[]}
renderItem={renderModelItem}
onChange={handleOnChange}
@@ -2,15 +2,13 @@
import AutoTooltip from '@/components/auto-tooltip';
import DropdownButtons from '@/components/drop-down-buttons';
import { SealColumnProps } from '@/components/seal-table/types';
import StatusTag from '@/components/status-tag';
import { StatusMaps } from '@/config';
import { tableSorter } from '@/config/settings';
import { CheckCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Tag } from 'antd';
import dayjs from 'dayjs';
import { useMemo } from 'react';
import ProviderLogo from '../components/provider-logo';
import ProviderModels from '../components/provider-models';
import { maasProviderLabelMap, rowActionList } from '../config';
import { MaasProviderItem } from '../config/types';
@@ -26,6 +24,7 @@ const useProviderColumns = (
title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name',
sorter: tableSorter(1),
minWidth: 160,
span: 5,
render: (text: string, record: MaasProviderItem) => (
<>
@@ -45,54 +44,25 @@ const useProviderColumns = (
dataIndex: 'provider',
sorter: tableSorter(2),
span: 4,
minWidth: 160,
render: (value: string) => (
<>
<ProviderLogo provider={value} style={{ marginRight: 8 }} />
<div className="flex-center gap-8">
<ProviderLogo provider={value} />
<AutoTooltip ghost minWidth={20}>
{maasProviderLabelMap[value]
? intl.formatMessage({ id: maasProviderLabelMap[value] })
: value}
</AutoTooltip>
</>
</div>
)
},
{
title: intl.formatMessage({ id: 'providers.table.modelsCounts' }),
title: intl.formatMessage({ id: 'providers.table.models' }),
dataIndex: 'models',
span: 3,
minWidth: 200,
sorter: tableSorter(3),
render: (value: number) => <span>{value}</span>
},
{
title: intl.formatMessage({ id: 'providers.table.proxy' }),
dataIndex: 'proxy_url',
span: 3,
render: (value: number) => (
<span>
{value ? (
<CheckCircleOutlined
style={{ color: 'var(--ant-color-success)' }}
/>
) : (
'-'
)}
</span>
)
},
{
title: 'Token Settings',
dataIndex: 'api_tokens',
span: 3,
render: (value: string, record: MaasProviderItem) => (
<StatusTag
statusValue={{
status:
value?.length > 0 ? StatusMaps.success : StatusMaps.inactive,
text: value?.length > 0 ? 'Configured' : 'Unconfigured',
message: ''
}}
/>
)
render: (value: number) => <ProviderModels></ProviderModels>
},
{
title: intl.formatMessage({ id: 'common.table.createTime' }),
@@ -109,6 +79,7 @@ const useProviderColumns = (
title: intl.formatMessage({ id: 'common.table.operation' }),
dataIndex: 'operations',
span: 3,
minWidth: 120,
render: (value: string, record: MaasProviderItem) => (
<DropdownButtons
items={rowActionList}
+30 -30
View File
@@ -2,8 +2,6 @@ import { expandKeysAtom } from '@/atoms/clusters';
import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import SealTable from '@/components/seal-table';
import TableContext from '@/components/seal-table/table-context';
import { TableOrder } from '@/components/seal-table/types';
import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
@@ -12,15 +10,15 @@ import useTableFetch from '@/hooks/use-table-fetch';
import useWatchList from '@/hooks/use-watch-list';
import { useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { message } from 'antd';
import { ConfigProvider, message, Table } from 'antd';
import { useAtom } from 'jotai';
import _ from 'lodash';
import NoResult from '../_components/no-result';
import PageBox from '../_components/page-box';
import {
deleteProvider,
MAAS_PROVIDERS_API,
PROVIDER_MODELS_API,
deleteProvider,
queryMaasProviders,
updateProvider
} from './apis';
@@ -160,6 +158,28 @@ const MaasProvider: React.FC = () => {
);
};
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
return (
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-extension-outline" />}
filters={_.omit(queryParams, ['sort_by'])}
noFoundText={intl.formatMessage({
id: 'noresult.providers.nofound'
})}
title={intl.formatMessage({ id: 'noresult.providers.title' })}
subTitle={intl.formatMessage({
id: 'noresult.providers.subTitle'
})}
onClick={handleClickDropdown}
buttonText={intl.formatMessage({ id: 'noresult.button.add' })}
></NoResult>
);
};
const columns = useProviderColumns(handleSelect);
return (
@@ -183,13 +203,10 @@ const MaasProvider: React.FC = () => {
)
}))}
></FilterBar>
<TableContext.Provider
value={{
allChildren: allProviderModels
}}
>
<SealTable
<ConfigProvider renderEmpty={renderEmpty}>
<Table
rowKey="id"
tableLayout="auto"
loadChildren={loadChildrenData}
sortDirections={TABLE_SORT_DIRECTIONS}
expandedRowKeys={expandedRowKeys}
@@ -204,25 +221,8 @@ const MaasProvider: React.FC = () => {
rowSelection={rowSelection}
columns={columns}
childParentKey="provider_id"
scroll={{ x: true }}
expandable={true}
empty={
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-extension-outline" />}
filters={_.omit(queryParams, ['sort_by'])}
noFoundText={intl.formatMessage({
id: 'noresult.providers.nofound'
})}
title={intl.formatMessage({ id: 'noresult.providers.title' })}
subTitle={intl.formatMessage({
id: 'noresult.providers.subTitle'
})}
onClick={handleClickDropdown}
buttonText={intl.formatMessage({ id: 'noresult.button.add' })}
></NoResult>
}
pagination={{
showSizeChanger: true,
pageSize: queryParams.perPage,
@@ -231,8 +231,8 @@ const MaasProvider: React.FC = () => {
hideOnSinglePage: queryParams.perPage === 10,
onChange: handlePageChange
}}
></SealTable>
</TableContext.Provider>
></Table>
</ConfigProvider>
</PageBox>
<AddMaasProvider
provider={openProviderModalStatus.provider}