chore: access setting notes

This commit is contained in:
jialin
2025-11-21 20:10:21 +08:00
parent e80ecfac8d
commit f49001b742
15 changed files with 220 additions and 43 deletions
+14 -1
View File
@@ -98,7 +98,8 @@ const CustomItem = styled.div`
background-color: var(--ant-color-bg-elevated); background-color: var(--ant-color-bg-elevated);
`; `;
export const ExtraContent = () => { export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
const { isDarkTheme } = props;
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
const [modal, contextHolder] = Modal.useModal(); const [modal, contextHolder] = Modal.useModal();
const [version] = useAtom(GPUStackVersionAtom); const [version] = useAtom(GPUStackVersionAtom);
@@ -130,6 +131,16 @@ export const ExtraContent = () => {
initialState?.currentUser?.is_admin initialState?.currentUser?.is_admin
]); ]);
const avatarStyle = useMemo(() => {
if (isDarkTheme) {
return {
color: 'var(--ant-color-text)',
border: 'none'
};
}
return {};
}, [isDarkTheme]);
const showVersion = () => { const showVersion = () => {
saveScrollHeight(); saveScrollHeight();
modal.info({ modal.info({
@@ -260,6 +271,7 @@ export const ExtraContent = () => {
<span className="flex-center gap-8"> <span className="flex-center gap-8">
<Avatar <Avatar
size={24} size={24}
style={{ ...avatarStyle }}
src={initialState?.currentUser?.avatar} src={initialState?.currentUser?.avatar}
icon={ icon={
<IconFont type="icon-user-filled" className="font-size-24" /> <IconFont type="icon-user-filled" className="font-size-24" />
@@ -309,6 +321,7 @@ export const ExtraContent = () => {
<IconWrapper> <IconWrapper>
<Avatar <Avatar
size={24} size={24}
style={{ ...avatarStyle }}
src={initialState?.currentUser?.avatar} src={initialState?.currentUser?.avatar}
icon={<IconFont type="icon-user-filled" className="font-size-24" />} icon={<IconFont type="icon-user-filled" className="font-size-24" />}
/> />
+6 -1
View File
@@ -516,7 +516,12 @@ export default (props: any) => {
onCollapse={onCollapse} onCollapse={onCollapse}
onMenuHeaderClick={onMenuHeaderClick} onMenuHeaderClick={onMenuHeaderClick}
menuHeaderRender={renderMenuHeader} menuHeaderRender={renderMenuHeader}
extra={[<ExtraContent key="extra-content" />]} extra={[
<ExtraContent
key="extra-content"
isDarkTheme={userSettings.isDarkTheme}
/>
]}
collapsed={userSettings.collapsed} collapsed={userSettings.collapsed}
onPageChange={onPageChange} onPageChange={onPageChange}
formatMessage={formatMessage} formatMessage={formatMessage}
+4
View File
@@ -178,6 +178,10 @@ export default {
'models.table.accessScope': 'Access Scope', 'models.table.accessScope': 'Access Scope',
'models.table.accessScope.all': 'All users', 'models.table.accessScope.all': 'All users',
'models.table.userSelection': 'User Selection', 'models.table.userSelection': 'User Selection',
'models.button.accessSettings.tips':
'Changes to access settings take effect after one minute.',
'models.table.userSelection.tips':
'Administrators always have access to all models.',
'models.table.filterByName': 'Filter by username', 'models.table.filterByName': 'Filter by username',
'models.table.admin': 'Admin', 'models.table.admin': 'Admin',
'models.table.noselected': 'No users selected', 'models.table.noselected': 'No users selected',
+1 -1
View File
@@ -90,7 +90,7 @@ export default {
'playground.audio.stoprecord': 'Stop Recording', 'playground.audio.stoprecord': 'Stop Recording',
'playground.audio.generating.tips': 'Generated text will appear here.', 'playground.audio.generating.tips': 'Generated text will appear here.',
'playground.audio.uploadfile.tips': 'playground.audio.uploadfile.tips':
'Upload an audio file, supported formats: {formats}', 'Upload an audio file, supported formats: {formats}.',
'playground.input.multiplePaste': 'Batch Input Mode', 'playground.input.multiplePaste': 'Batch Input Mode',
'playground.input.multiplePaste.tips': 'playground.input.multiplePaste.tips':
'When enabled, pasted multi-line text will be automatically split by newline into separate entries in the form.', 'When enabled, pasted multi-line text will be automatically split by newline into separate entries in the form.',
+6
View File
@@ -179,6 +179,10 @@ export default {
'models.table.accessScope': 'Access Scope', 'models.table.accessScope': 'Access Scope',
'models.table.accessScope.all': 'All users', 'models.table.accessScope.all': 'All users',
'models.table.userSelection': 'User Selection', 'models.table.userSelection': 'User Selection',
'models.button.accessSettings.tips':
'Changes to access settings take effect after one minute.',
'models.table.userSelection.tips':
'Administrators always have access to all models.',
'models.table.filterByName': 'Filter by username', 'models.table.filterByName': 'Filter by username',
'models.table.admin': 'Admin', 'models.table.admin': 'Admin',
'models.table.noselected': 'No users selected', 'models.table.noselected': 'No users selected',
@@ -338,4 +342,6 @@ export default {
// 60. models.form.backend.mindie': 'Only supports Ascend NPUs.', // 60. models.form.backend.mindie': 'Only supports Ascend NPUs.',
// 61. 'models.form.backend.sglang': 'Built-in support for NVIDIA/AMD GPUs and Ascend NPUs.', // 61. 'models.form.backend.sglang': 'Built-in support for NVIDIA/AMD GPUs and Ascend NPUs.',
// 62. 'models.form.backend_parameters.vllm.tips': 'For more details about {backend} parameters, see <a href={link} target="_blank">here</a>.', // 62. 'models.form.backend_parameters.vllm.tips': 'For more details about {backend} parameters, see <a href={link} target="_blank">here</a>.',
// 63. 'models.button.accessSettings.tips': 'Changes to access settings take effect after one minute.',
// 64. 'models.table.userSelection.tips': 'Administrators always have access to all models.',
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+6
View File
@@ -178,6 +178,10 @@ export default {
'models.table.accessScope': 'Область доступа', 'models.table.accessScope': 'Область доступа',
'models.table.accessScope.all': 'Все пользователи', 'models.table.accessScope.all': 'Все пользователи',
'models.table.userSelection': 'Выбор пользователей', 'models.table.userSelection': 'Выбор пользователей',
'models.button.accessSettings.tips':
'Changes to access settings take effect after one minute.',
'models.table.userSelection.tips':
'Administrators always have access to all models.',
'models.table.filterByName': 'Фильтр по имени пользователя', 'models.table.filterByName': 'Фильтр по имени пользователя',
'models.table.admin': 'Администратор', 'models.table.admin': 'Администратор',
'models.table.noselected': 'Пользователи не выбраны', 'models.table.noselected': 'Пользователи не выбраны',
@@ -266,4 +270,6 @@ export default {
// 6. 'models.form.backend.sglang': 'Built-in support for NVIDIA/AMD GPUs and Ascend NPUs.', // 6. 'models.form.backend.sglang': 'Built-in support for NVIDIA/AMD GPUs and Ascend NPUs.',
// 7. 'models.form.gpusAllocationType.auto.tips': 'The system automatically calculates the GPU count per replica, using powers of two by default and capped by the selected GPUs.', // 7. 'models.form.gpusAllocationType.auto.tips': 'The system automatically calculates the GPU count per replica, using powers of two by default and capped by the selected GPUs.',
// 8. 'models.form.backend_parameters.vllm.tips': 'For more details about {backend} parameters, see <a href={link} target="_blank">here</a>.', // 8. 'models.form.backend_parameters.vllm.tips': 'For more details about {backend} parameters, see <a href={link} target="_blank">here</a>.',
// 9. 'models.button.accessSettings.tips': 'Changes to access settings take effect after one minute.',
// 10. 'models.table.userSelection.tips': 'Administrators always have access to all models.',
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+2
View File
@@ -168,6 +168,8 @@ export default {
'models.table.accessScope': '访问范围', 'models.table.accessScope': '访问范围',
'models.table.accessScope.all': '所有用户', 'models.table.accessScope.all': '所有用户',
'models.table.userSelection': '用户选择', 'models.table.userSelection': '用户选择',
'models.button.accessSettings.tips': '访问设置的更改将在一分钟后生效。',
'models.table.userSelection.tips': '管理员始终拥有对所有模型的访问权限。',
'models.table.filterByName': '按用户名筛选', 'models.table.filterByName': '按用户名筛选',
'models.table.admin': '管理员', 'models.table.admin': '管理员',
'models.table.noselected': '未选择用户', 'models.table.noselected': '未选择用户',
@@ -19,7 +19,8 @@ const Label = styled.div`
const AllowModelsForm: React.FC<{ const AllowModelsForm: React.FC<{
currentData?: Partial<ListItem> | null; currentData?: Partial<ListItem> | null;
action: PageActionType; action: PageActionType;
}> = ({ currentData, action }) => { onValuesChange?: (changedValues: any, allValues: any) => void;
}> = ({ currentData, action, onValuesChange }) => {
const intl = useIntl(); const intl = useIntl();
const { getRuleMessage } = useAppUtils(); const { getRuleMessage } = useAppUtils();
const form = Form.useFormInstance(); const form = Form.useFormInstance();
@@ -102,6 +103,10 @@ const AllowModelsForm: React.FC<{
})} })}
onSelectChange={(selectedKeys) => { onSelectChange={(selectedKeys) => {
form.setFieldsValue({ allowed_model_names: selectedKeys }); form.setFieldsValue({ allowed_model_names: selectedKeys });
onValuesChange?.(
{ allowed_model_names: selectedKeys },
form.getFieldsValue()
);
}} }}
/> />
</Form.Item> </Form.Item>
@@ -12,7 +12,8 @@ import AllowModelsForm from './allow-models';
const APIKeyForm: React.FC<{ const APIKeyForm: React.FC<{
action: PageActionType; action: PageActionType;
currentData?: Partial<ListItem> | null; currentData?: Partial<ListItem> | null;
}> = ({ action, currentData }) => { onValuesChange?: (changedValues: any, allValues: any) => void;
}> = ({ action, currentData, onValuesChange }) => {
const intl = useIntl(); const intl = useIntl();
return ( return (
@@ -70,6 +71,7 @@ const APIKeyForm: React.FC<{
<AllowModelsForm <AllowModelsForm
currentData={currentData} currentData={currentData}
action={action} action={action}
onValuesChange={onValuesChange}
></AllowModelsForm> ></AllowModelsForm>
</> </>
); );
@@ -1,3 +1,4 @@
import AlertBlockInfo from '@/components/alert-info/block';
import CopyButton from '@/components/copy-button'; import CopyButton from '@/components/copy-button';
import ModalFooter from '@/components/modal-footer'; import ModalFooter from '@/components/modal-footer';
import GSDrawer from '@/components/scroller-modal/gs-drawer'; import GSDrawer from '@/components/scroller-modal/gs-drawer';
@@ -9,7 +10,7 @@ import { useIntl } from '@umijs/max';
import { Form, Tag } from 'antd'; import { Form, Tag } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import { useEffect, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { createApisKey, updateApisKey } from '../../apis'; import { createApisKey, updateApisKey } from '../../apis';
import { expirationOptions } from '../../config'; import { expirationOptions } from '../../config';
import { FormData, ListItem } from '../../config/types'; import { FormData, ListItem } from '../../config/types';
@@ -37,6 +38,11 @@ const AddModal: React.FC<AddModalProps> = ({
const [showKey, setShowKey] = useState(false); const [showKey, setShowKey] = useState(false);
const [apikeyValue, setAPIKeyValue] = useState(''); const [apikeyValue, setAPIKeyValue] = useState('');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isChanged, setIsChanged] = useState(false);
const cacheFormRef = useRef<{
allowed_type: string;
allowed_model_names: string[];
}>({} as any);
const getExpireValue = (val: number | null) => { const getExpireValue = (val: number | null) => {
const expires_in = val; const expires_in = val;
@@ -128,6 +134,21 @@ const AddModal: React.FC<AddModalProps> = ({
setShowKey(false); setShowKey(false);
}; };
const handleOnValuesChange = async (changedValues: any, allValues: any) => {
const initialValues = cacheFormRef.current;
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
console.log('initialValues', initialValues, allValues);
if (
_.isEqual(initialValues, _.pick(allValues, Object.keys(initialValues)))
) {
setIsChanged(false);
} else {
setIsChanged(true);
}
};
const initValues = () => { const initValues = () => {
if (action === PageAction.CREATE && open) { if (action === PageAction.CREATE && open) {
form.setFieldsValue({ form.setFieldsValue({
@@ -146,10 +167,19 @@ const AddModal: React.FC<AddModalProps> = ({
allowed_model_names: currentData.allowed_model_names || [] allowed_model_names: currentData.allowed_model_names || []
}); });
} }
cacheFormRef.current = {
allowed_type: form.getFieldValue('allowed_type'),
allowed_model_names: form.getFieldValue('allowed_model_names')
};
}; };
useEffect(() => { useEffect(() => {
initValues(); initValues();
if (!open) {
setIsChanged(false);
cacheFormRef.current = {} as any;
}
}, [open]); }, [open]);
return ( return (
@@ -183,29 +213,45 @@ const AddModal: React.FC<AddModalProps> = ({
}} }}
footer={ footer={
!showKey ? ( !showKey ? (
<ModalFooter <>
onOk={handleSumit} {isChanged && (
onCancel={onCancel} <div style={{ marginInline: 24 }}>
loading={loading} <AlertBlockInfo
style={{ type="warning"
padding: '16px 24px', style={{ marginBottom: 16 }}
display: 'flex', contentStyle={{ paddingInline: 0 }}
justifyContent: 'flex-end' message={intl.formatMessage({
}} id: 'models.button.accessSettings.tips'
></ModalFooter> })}
></AlertBlockInfo>
</div>
)}
<ModalFooter
onOk={handleSumit}
onCancel={onCancel}
loading={loading}
style={{
padding: '16px 24px',
display: 'flex',
justifyContent: 'flex-end'
}}
></ModalFooter>
</>
) : ( ) : (
<ModalFooter <>
onOk={handleDone} <ModalFooter
onCancel={onCancel} onOk={handleDone}
loading={loading} onCancel={onCancel}
okText={intl.formatMessage({ id: 'common.button.done' })} loading={loading}
showCancelBtn={false} okText={intl.formatMessage({ id: 'common.button.done' })}
style={{ showCancelBtn={false}
padding: '16px 24px', style={{
display: 'flex', padding: '16px 24px',
justifyContent: 'flex-end' display: 'flex',
}} justifyContent: 'flex-end'
></ModalFooter> }}
></ModalFooter>
</>
) )
} }
> >
@@ -213,10 +259,15 @@ const AddModal: React.FC<AddModalProps> = ({
name="addAPIKey" name="addAPIKey"
form={form} form={form}
onFinish={handleOnOk} onFinish={handleOnOk}
onValuesChange={handleOnValuesChange}
preserve={false} preserve={false}
> >
{!showKey && ( {!showKey && (
<APIKeyForm action={action} currentData={currentData}></APIKeyForm> <APIKeyForm
action={action}
currentData={currentData}
onValuesChange={handleOnValuesChange}
></APIKeyForm>
)} )}
{showKey && action === PageAction.CREATE && ( {showKey && action === PageAction.CREATE && (
<Form.Item> <Form.Item>
@@ -243,9 +243,6 @@ const ClusterCreate = () => {
const info = await queryClusterToken({ id: res.id }); const info = await queryClusterToken({ id: res.id });
setRegistrationInfo({ setRegistrationInfo({
...info, ...info,
image: info.container_registry
? `${info.container_registry}/${info.image}`
: info.image,
cluster_id: res.id cluster_id: res.id
}); });
return true; return true;
@@ -68,9 +68,6 @@ const AddWorker: React.FC<AddWorkerProps> = (props) => {
firstLoad.current = false; firstLoad.current = false;
setRegistrationInfo({ setRegistrationInfo({
...data, ...data,
image: data.container_registry
? `${data.container_registry}/${data.image}`
: data.image,
cluster_id: value cluster_id: value
}); });
} catch (error) { } catch (error) {
@@ -3,14 +3,24 @@ import { PageAction } from '@/config';
import { PageActionType } from '@/config/types'; import { PageActionType } from '@/config/types';
import TransferInner from '@/pages/_components/transfer'; import TransferInner from '@/pages/_components/transfer';
import { queryUsersList } from '@/pages/users/apis'; import { queryUsersList } from '@/pages/users/apis';
import { DownOutlined } from '@ant-design/icons'; import { DownOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Checkbox, Dropdown, DropdownProps, Empty, Form, Radio } from 'antd'; import {
Checkbox,
Dropdown,
DropdownProps,
Empty,
Form,
Radio,
RadioChangeEvent,
Tooltip
} from 'antd';
import { import {
forwardRef, forwardRef,
useEffect, useEffect,
useImperativeHandle, useImperativeHandle,
useMemo, useMemo,
useRef,
useState useState
} from 'react'; } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@@ -30,10 +40,11 @@ interface AccessControlFormProps {
action: PageActionType; action: PageActionType;
currentData?: ListItem | null; currentData?: ListItem | null;
onFinish: (values: AccessControlFormData) => void; onFinish: (values: AccessControlFormData) => void;
onValuesChange?: (changedValues: any, allValues: any) => void;
} }
const AccessControlForm = forwardRef((props: AccessControlFormProps, ref) => { const AccessControlForm = forwardRef((props: AccessControlFormProps, ref) => {
const { action, currentData, onFinish } = props; const { action, currentData, onFinish, onValuesChange } = props;
const intl = useIntl(); const intl = useIntl();
const [form] = Form.useForm(); const [form] = Form.useForm();
const accessPolicy = Form.useWatch('access_policy', form); const accessPolicy = Form.useWatch('access_policy', form);
@@ -47,6 +58,7 @@ const AccessControlForm = forwardRef((props: AccessControlFormProps, ref) => {
const [queryParams, setQueryParams] = useState<Global.SearchParams>({ const [queryParams, setQueryParams] = useState<Global.SearchParams>({
page: -1 page: -1
}); });
const formDataCacheRef = useRef<AccessControlFormData | null>(null);
const dataList = useMemo(() => { const dataList = useMemo(() => {
if (filterInUsers.size === 0) { if (filterInUsers.size === 0) {
@@ -85,7 +97,7 @@ const AccessControlForm = forwardRef((props: AccessControlFormProps, ref) => {
} }
}; };
const handleOnChange = ( const handleOnChange = async (
nextTargetKeys: TransferKey[], nextTargetKeys: TransferKey[],
direction: string, direction: string,
removeKeys: TransferKey[] removeKeys: TransferKey[]
@@ -93,6 +105,10 @@ const AccessControlForm = forwardRef((props: AccessControlFormProps, ref) => {
setTargetKeys(nextTargetKeys); setTargetKeys(nextTargetKeys);
const users = nextTargetKeys.map((key) => ({ id: key })); const users = nextTargetKeys.map((key) => ({ id: key }));
form.setFieldsValue({ users }); form.setFieldsValue({ users });
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
onValuesChange?.({ users }, form.getFieldsValue());
}; };
const onSearch = (dir: 'left' | 'right', value: string) => { const onSearch = (dir: 'left' | 'right', value: string) => {
@@ -115,6 +131,23 @@ const AccessControlForm = forwardRef((props: AccessControlFormProps, ref) => {
} }
}; };
const handleOnPolicyChange = async (e: RadioChangeEvent) => {
console.log('policy changed:', e.target.value);
const policy = e.target.value;
if (policy === 'allowed_users') {
form.setFieldsValue({ users: formDataCacheRef.current?.users || [] });
} else {
formDataCacheRef.current = {
access_policy: policy,
users: form.getFieldValue('users') || []
};
}
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
onValuesChange?.({ access_policy: policy }, form.getFieldsValue());
};
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
submit: () => { submit: () => {
form.submit(); form.submit();
@@ -222,16 +255,18 @@ const AccessControlForm = forwardRef((props: AccessControlFormProps, ref) => {
<Form <Form
form={form} form={form}
onFinish={onFinish} onFinish={onFinish}
preserve={false} preserve={true}
clearOnDestroy={true} clearOnDestroy={true}
scrollToFirstError={true} scrollToFirstError={true}
initialValues={{ initialValues={{
users: [],
access_policy: action === PageAction.CREATE ? 'authed' : undefined access_policy: action === PageAction.CREATE ? 'authed' : undefined
}} }}
> >
<Label>{intl.formatMessage({ id: 'models.table.accessScope' })}</Label> <Label>{intl.formatMessage({ id: 'models.table.accessScope' })}</Label>
<Form.Item<AccessControlFormData> name="access_policy" noStyle> <Form.Item<AccessControlFormData> name="access_policy" noStyle>
<Radio.Group <Radio.Group
onChange={handleOnPolicyChange}
style={{ marginBottom: 12 }} style={{ marginBottom: 12 }}
options={[ options={[
{ {
@@ -267,6 +302,13 @@ const AccessControlForm = forwardRef((props: AccessControlFormProps, ref) => {
<> <>
<Label> <Label>
{intl.formatMessage({ id: 'models.table.userSelection' })} {intl.formatMessage({ id: 'models.table.userSelection' })}
<Tooltip
title={intl.formatMessage({
id: 'models.table.userSelection.tips'
})}
>
<QuestionCircleOutlined style={{ marginLeft: 4 }} />
</Tooltip>
</Label> </Label>
<Form.Item<AccessControlFormData> name="users"> <Form.Item<AccessControlFormData> name="users">
<TransferInner <TransferInner
@@ -1,9 +1,11 @@
import AlertBlockInfo from '@/components/alert-info/block';
import ModalFooter from '@/components/modal-footer'; import ModalFooter from '@/components/modal-footer';
import ScrollerModal from '@/components/scroller-modal'; import ScrollerModal from '@/components/scroller-modal';
import { PageActionType } from '@/config/types'; import { PageActionType } from '@/config/types';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { message } from 'antd'; import { message } from 'antd';
import { useRef } from 'react'; import _ from 'lodash';
import { useEffect, useRef, useState } from 'react';
import { updateModelAccessUser } from '../../apis'; import { updateModelAccessUser } from '../../apis';
import { AccessControlFormData, ListItem } from '../../config/types'; import { AccessControlFormData, ListItem } from '../../config/types';
import AccessControlForm from './form'; import AccessControlForm from './form';
@@ -13,6 +15,8 @@ const AccessControlModal: React.FC<
> = ({ open, title, currentData, action, onCancel }) => { > = ({ open, title, currentData, action, onCancel }) => {
const intl = useIntl(); const intl = useIntl();
const form = useRef<any>(null); const form = useRef<any>(null);
const [isChanged, setIsChanged] = useState(false);
const formCacheRef = useRef<AccessControlFormData | null>(null);
const handleSumit = () => { const handleSumit = () => {
form.current?.submit(); form.current?.submit();
@@ -36,6 +40,32 @@ const AccessControlModal: React.FC<
} }
}; };
const handleOnValuesChange = async (changedValues: any, allValues: any) => {
console.log('changedValues', changedValues, allValues);
const initialValues = formCacheRef.current;
if (_.isEqual(initialValues, allValues)) {
setIsChanged(false);
} else {
setIsChanged(true);
}
};
useEffect(() => {
if (open) {
setIsChanged(false);
setTimeout(() => {
const initialValues = form.current?.getFieldsValue();
formCacheRef.current = {
access_policy: initialValues.access_policy,
users: initialValues.users || []
};
}, 200);
} else {
formCacheRef.current = null;
setIsChanged(false);
}
}, [open, currentData]);
return ( return (
<ScrollerModal <ScrollerModal
title={title} title={title}
@@ -48,7 +78,19 @@ const AccessControlModal: React.FC<
keyboard={false} keyboard={false}
width={700} width={700}
footer={ footer={
<ModalFooter onOk={handleSumit} onCancel={onCancel}></ModalFooter> <>
{isChanged && (
<AlertBlockInfo
type="warning"
style={{ marginBottom: 16 }}
contentStyle={{ paddingInline: 0 }}
message={intl.formatMessage({
id: 'models.button.accessSettings.tips'
})}
></AlertBlockInfo>
)}
<ModalFooter onOk={handleSumit} onCancel={onCancel}></ModalFooter>
</>
} }
> >
<AccessControlForm <AccessControlForm
@@ -56,6 +98,7 @@ const AccessControlModal: React.FC<
currentData={currentData} currentData={currentData}
action={action as PageActionType} action={action as PageActionType}
onFinish={handleOnFinish} onFinish={handleOnFinish}
onValuesChange={handleOnValuesChange}
/> />
</ScrollerModal> </ScrollerModal>
); );
+5 -1
View File
@@ -110,10 +110,14 @@ const ScheduleTypeForm: React.FC = () => {
await new Promise((resolve) => { await new Promise((resolve) => {
setTimeout(resolve, 100); setTimeout(resolve, 100);
}); });
if (value === ScheduleValueMap.Auto) { if (value === ScheduleValueMap.Auto) {
onValuesChange?.({}, form.getFieldsValue()); onValuesChange?.({}, form.getFieldsValue());
} else if (value === ScheduleValueMap.Manual) { } else if (value === ScheduleValueMap.Manual) {
form.setFieldValue(['gpu_selector', 'gpus_per_replica'], null); form.setFieldValue('gpu_selector', {
gpu_ids: [],
gpus_per_replica: null
});
requestAnimationFrame(() => { requestAnimationFrame(() => {
setContentPaddingBottom(); setContentPaddingBottom();
}); });