chore: show existing when add file
This commit is contained in:
@@ -20,8 +20,9 @@ export const OverlayScroller: React.FC<
|
||||
maxHeight?: number;
|
||||
style?: React.CSSProperties;
|
||||
children: React.ReactNode;
|
||||
onScroll?: (e: React.UIEvent<HTMLDivElement, UIEvent>) => void;
|
||||
}
|
||||
> = ({ children, maxHeight, scrollbars, oppositeTheme, style }) => {
|
||||
> = ({ children, maxHeight, scrollbars, oppositeTheme, style, onScroll }) => {
|
||||
const scroller = React.useRef<any>(null);
|
||||
const { initialize } = useOverlayScroller({
|
||||
options: {
|
||||
@@ -43,6 +44,7 @@ export const OverlayScroller: React.FC<
|
||||
className="overlay-scroller-wrapper"
|
||||
$maxHeight={maxHeight || 200}
|
||||
hidden={false}
|
||||
onWheel={onScroll}
|
||||
style={{
|
||||
paddingInlineStart: 8,
|
||||
paddingInlineEnd: 8,
|
||||
|
||||
+7
-7
@@ -1,11 +1,11 @@
|
||||
import { PageActionType, StatusType } from './types';
|
||||
import { StatusType } from './types';
|
||||
|
||||
export const PageAction: Record<string, PageActionType> = {
|
||||
CREATE: 'create',
|
||||
UPDATE: 'update',
|
||||
VIEW: 'view',
|
||||
EDIT: 'edit'
|
||||
};
|
||||
export enum PageAction {
|
||||
CREATE = 'create',
|
||||
UPDATE = 'update',
|
||||
VIEW = 'view',
|
||||
EDIT = 'edit'
|
||||
}
|
||||
|
||||
export const StatusColorMap: Record<
|
||||
StatusType,
|
||||
|
||||
@@ -94,5 +94,6 @@ export default {
|
||||
'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+',
|
||||
'resource.register.windows.support': 'win 10, win 11',
|
||||
'resources.model.instance': 'Model Instance',
|
||||
'resources.worker.download.privatekey': 'Download Private Key'
|
||||
'resources.worker.download.privatekey': 'Download Private Key',
|
||||
'resources.modelfiles.form.exsting': 'Existing'
|
||||
};
|
||||
|
||||
@@ -95,7 +95,8 @@ export default {
|
||||
'resource.register.windows.support': 'win 10, win 11',
|
||||
'resources.model.instance': 'Model Instance',
|
||||
'resources.worker.download.privatekey': 'Download Private Key',
|
||||
'resources.worker': 'Worker'
|
||||
'resources.worker': 'Worker',
|
||||
'resources.modelfiles.form.exsting': 'Existing'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
@@ -114,4 +115,5 @@ export default {
|
||||
// 13. 'resources.model.instance': 'Model Instance',
|
||||
// 14. 'resources.worker.download.privatekey': 'Download Private Key',
|
||||
// 15. 'resources.worker': 'Worker',
|
||||
// 16. 'resources.modelfiles.form.exsting': 'Existing'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -93,7 +93,8 @@ export default {
|
||||
'resource.register.windows.support': 'Windows 10, Windows 11',
|
||||
'resources.model.instance': 'Модель экземпляра',
|
||||
'resources.worker.download.privatekey': 'Скачать приватный ключ',
|
||||
'resources.worker': 'Worker'
|
||||
'resources.worker': 'Worker',
|
||||
'resources.modelfiles.form.exsting': 'Существующий'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
@@ -92,5 +92,6 @@ export default {
|
||||
'resource.register.maos.support': 'M 芯片,macOS 14+',
|
||||
'resource.register.windows.support': 'win 10, win 11',
|
||||
'resources.model.instance': '模型实例',
|
||||
'resources.worker.download.privatekey': '下载私钥'
|
||||
'resources.worker.download.privatekey': '下载私钥',
|
||||
'resources.modelfiles.form.exsting': '已存在'
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@ export default {
|
||||
'users.table.username': '用户名',
|
||||
'users.table.role': '角色',
|
||||
'users.table.status': '状态',
|
||||
'users.status.active': '激活',
|
||||
'users.status.inactive': '禁用',
|
||||
'users.status.active': '启用',
|
||||
'users.status.inactive': '停用',
|
||||
'users.form.active': '激活账户',
|
||||
'users.form.active.description': '启用或禁用此用户账户',
|
||||
'users.form.fullname': '全名',
|
||||
|
||||
@@ -139,7 +139,7 @@ const SelectPanel: React.FC<SelectPanelProps> = ({
|
||||
</Button>
|
||||
</Header>
|
||||
<SelectedList
|
||||
maxHeight={height - 40}
|
||||
maxHeight={height - 50}
|
||||
selectedList={options.filter((item) =>
|
||||
selectedKeys.includes(item.key)
|
||||
)}
|
||||
@@ -194,7 +194,7 @@ const SelectPanel: React.FC<SelectPanelProps> = ({
|
||||
</Header>
|
||||
{showOptions.length > 0 ? (
|
||||
<List
|
||||
maxHeight={height - 40}
|
||||
maxHeight={height - 50}
|
||||
dataList={showOptions}
|
||||
selectedKeys={selectedKeys}
|
||||
onSelectChange={handleSelectChange}
|
||||
|
||||
@@ -547,6 +547,11 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
>
|
||||
<FormWrapper>
|
||||
<ColumnWrapper
|
||||
styles={{
|
||||
container: {
|
||||
paddingTop: 0
|
||||
}
|
||||
}}
|
||||
paddingBottom={
|
||||
warningStatus.show
|
||||
? Array.isArray(warningStatus.message)
|
||||
|
||||
@@ -8,13 +8,13 @@ import '../style/gpu-card.less';
|
||||
|
||||
const CardWrapper = styled.div`
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--ant-color-split);
|
||||
padding-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
@@ -25,7 +25,7 @@ const Description = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
color: var(--ant-color-text-tertiary);
|
||||
`;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import React, { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { modelCategories, modelCategoriesMap, modelSourceMap } from '../config';
|
||||
import { categoryToPathMap } from '../config/button-actions';
|
||||
import { categoryConfig } from './model-tag';
|
||||
|
||||
const MyModelsStatusValueMap = {
|
||||
Inactive: 'Inactive',
|
||||
@@ -198,10 +199,21 @@ const ModelItem: React.FC<{
|
||||
<div className="content">
|
||||
<div className="footer">
|
||||
<div className="extra-info">
|
||||
<ThemeTag className="tag-item" color="blue">
|
||||
{_.find(modelCategories, { value: model.categories?.[0] })
|
||||
?.label || model.categories?.[0]}
|
||||
</ThemeTag>
|
||||
{model.categories?.length > 0 &&
|
||||
model.categories.map((sItem: string) => {
|
||||
return (
|
||||
<ThemeTag
|
||||
icon={categoryConfig[sItem]?.icon}
|
||||
key={sItem}
|
||||
className="tag-item"
|
||||
color={categoryConfig[sItem]?.color || 'blue'}
|
||||
opacity={0.7}
|
||||
>
|
||||
{_.find(modelCategories, { value: sItem })?.label ||
|
||||
sItem}
|
||||
</ThemeTag>
|
||||
);
|
||||
})}
|
||||
{maxToken > 0 && (
|
||||
<ThemeTag className="tag-item" color="purple">
|
||||
{maxToken}K context
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import { Tag } from 'antd';
|
||||
import { modelCategoriesMap } from '../config';
|
||||
|
||||
const categoryConfig = {
|
||||
export const categoryConfig = {
|
||||
[modelCategoriesMap.reranker]: {
|
||||
icon: <IconFont type="icon-rank1" />,
|
||||
color: 'cyan',
|
||||
|
||||
@@ -22,6 +22,7 @@ type AddModalProps = {
|
||||
width?: string | number;
|
||||
hasLinuxWorker?: boolean;
|
||||
workerOptions: any[];
|
||||
workersList?: any[];
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
@@ -47,7 +48,8 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
|
||||
hasLinuxWorker,
|
||||
source,
|
||||
width = 600,
|
||||
workerOptions
|
||||
workerOptions,
|
||||
workersList
|
||||
} = props || {};
|
||||
const SEARCH_SOURCE = [
|
||||
modelSourceMap.huggingface_value,
|
||||
@@ -66,7 +68,7 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
|
||||
if (source === modelSourceMap.huggingface_value) {
|
||||
const huggingFaceModel = {
|
||||
huggingface_repo_id: selectedModel.name,
|
||||
huggingface_filename: null
|
||||
huggingface_filename: fileName || null
|
||||
};
|
||||
return huggingFaceModel;
|
||||
}
|
||||
@@ -74,7 +76,7 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
|
||||
if (source === modelSourceMap.modelscope_value) {
|
||||
const modelScopeModel = {
|
||||
model_scope_model_id: selectedModel.name,
|
||||
model_scope_file_path: null
|
||||
model_scope_file_path: fileName || null
|
||||
};
|
||||
return modelScopeModel;
|
||||
}
|
||||
@@ -238,6 +240,9 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
|
||||
ref={form}
|
||||
onOk={handleOk}
|
||||
source={source}
|
||||
selectedModel={selectedModel}
|
||||
fileName={fileName}
|
||||
workersList={workersList}
|
||||
workerOptions={workerOptions}
|
||||
></TargetForm>
|
||||
</>
|
||||
|
||||
@@ -7,22 +7,64 @@ import { ModelFileFormData as FormData } from '@/pages/resources/config/types';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { forwardRef, useImperativeHandle, useMemo } from 'react';
|
||||
import { minimatch } from 'minimatch';
|
||||
import React, {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo
|
||||
} from 'react';
|
||||
import { localPathTipsList, modelSourceMap, sourceOptions } from '../config';
|
||||
import { useGenerateWorkersModelFileOptions } from '../hooks';
|
||||
|
||||
type EmptyObject = Record<never, never>;
|
||||
|
||||
type CascaderOption<T extends object = EmptyObject> = {
|
||||
label: string;
|
||||
value: string | number;
|
||||
parent?: boolean;
|
||||
disabled?: boolean;
|
||||
index?: number;
|
||||
children?: CascaderOption<T>[];
|
||||
} & Partial<T>;
|
||||
|
||||
interface TargetFormProps {
|
||||
ref?: any;
|
||||
source: string;
|
||||
workerOptions: any[];
|
||||
workerOptions: CascaderOption<{ state: string }>[];
|
||||
workersList?: Global.BaseOption<
|
||||
number,
|
||||
{ state: string; labels: Record<string, string>; cluster_id: number }
|
||||
>[];
|
||||
selectedModel?: Record<string, any>;
|
||||
fileName?: string;
|
||||
onOk: (values: any) => void;
|
||||
}
|
||||
|
||||
const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||
const { onOk, source, workerOptions } = props;
|
||||
const {
|
||||
modelFileOptions,
|
||||
getModelFileList,
|
||||
generateWorkersModelFileOptions
|
||||
} = useGenerateWorkersModelFileOptions();
|
||||
const { onOk, source, workerOptions, workersList, selectedModel, fileName } =
|
||||
props;
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const intl = useIntl();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
try {
|
||||
if (workersList && workersList?.length > 0) {
|
||||
const modelFiles = await getModelFileList();
|
||||
generateWorkersModelFileOptions(modelFiles, workersList || []);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
init();
|
||||
}, [workersList]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
form
|
||||
}));
|
||||
@@ -33,10 +75,6 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||
...data,
|
||||
worker_id: data.worker_id?.[1]
|
||||
});
|
||||
console.log('Form Data: ', {
|
||||
...data,
|
||||
worker_id: data.worker_id?.[1]
|
||||
});
|
||||
};
|
||||
|
||||
const handleOnLocalPathBlur = (e: any) => {
|
||||
@@ -49,6 +87,32 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||
});
|
||||
};
|
||||
|
||||
const renderOptionNode = (props: { data: any }) => {
|
||||
const { data } = props;
|
||||
const currentWorker = modelFileOptions.find(
|
||||
(item) => item.value === data.value
|
||||
);
|
||||
|
||||
const isExisting = currentWorker?.children?.some((child) => {
|
||||
const isSameFile =
|
||||
child.fileName === (fileName || '') ||
|
||||
minimatch(child.fileName || '', fileName || '');
|
||||
|
||||
return child.repoId === selectedModel?.name && isSameFile;
|
||||
});
|
||||
|
||||
return (
|
||||
<span>
|
||||
{data.label}
|
||||
{isExisting && (
|
||||
<span className="text-tertiary m-l-4">
|
||||
({intl.formatMessage({ id: 'resources.modelfiles.form.exsting' })})
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const renderLocalPathFields = () => {
|
||||
return (
|
||||
<>
|
||||
@@ -136,6 +200,7 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||
label={intl.formatMessage({ id: 'resources.worker' })}
|
||||
options={workerOptions}
|
||||
showCheckedStrategy="SHOW_CHILD"
|
||||
optionNode={renderOptionNode}
|
||||
getPopupContainer={(triggerNode) => triggerNode.parentNode}
|
||||
></SealCascader>
|
||||
</Form.Item>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import CollapsePanel from '@/pages/_components/collapse-panel';
|
||||
import { useIntl } from '@umijs/max';
|
||||
@@ -122,22 +123,21 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
|
||||
const SegmentedTop = useMemo(() => {
|
||||
if (
|
||||
[
|
||||
modelSourceMap.huggingface_value,
|
||||
modelSourceMap.modelscope_value
|
||||
].includes(source) &&
|
||||
formKey === deployFormKeyMap.deployment
|
||||
modelSourceMap.local_path_value === source ||
|
||||
action === PageAction.EDIT ||
|
||||
formKey === deployFormKeyMap.catalog
|
||||
) {
|
||||
return {
|
||||
top: 50,
|
||||
offsetTop: 146
|
||||
top: 0,
|
||||
offsetTop: 96
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
top: 0,
|
||||
offsetTop: 96
|
||||
top: 50,
|
||||
offsetTop: 146
|
||||
};
|
||||
}, [source, formKey]);
|
||||
}, [source, formKey, action]);
|
||||
|
||||
const handleSumit = () => {
|
||||
form.submit();
|
||||
|
||||
@@ -98,7 +98,6 @@ const LocalPathForm: React.FC = () => {
|
||||
<SealInput.Input
|
||||
allowClear
|
||||
required
|
||||
onBlur={handleOnBlur}
|
||||
onFocus={handleOnFocus}
|
||||
label={intl.formatMessage({ id: 'models.form.filePath' })}
|
||||
description={<TooltipList list={localPathTipsList}></TooltipList>}
|
||||
|
||||
@@ -94,6 +94,14 @@ const ScheduleTypeForm: React.FC = () => {
|
||||
const handleOnStepReplica = (value: number | null) => {
|
||||
if (value === null) {
|
||||
form.setFieldValue(['gpu_selector', 'gpus_per_replica'], 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const isPowerOfTwo = (n: number) => (n & (n - 1)) === 0 && n !== 0; // check power of two
|
||||
if (!isPowerOfTwo(value)) {
|
||||
const newValue = Math.pow(2, Math.round(Math.log2(value)));
|
||||
form.setFieldValue(['gpu_selector', 'gpus_per_replica'], newValue);
|
||||
onValuesChange?.({}, form.getFieldsValue());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -25,12 +25,27 @@ export type MessageStatus = {
|
||||
evaluateResult?: EvaluateResult;
|
||||
};
|
||||
|
||||
export interface ModelFileOption {
|
||||
label: string;
|
||||
value: number;
|
||||
labels?: Record<string, string>;
|
||||
parent: boolean;
|
||||
repoId?: string;
|
||||
fileName?: string;
|
||||
[key: string]: any;
|
||||
children?: ModelFileOption[];
|
||||
}
|
||||
|
||||
export type WarningStausOptions = {
|
||||
lockAfterUpdate?: boolean;
|
||||
override?: boolean;
|
||||
};
|
||||
|
||||
export const useGenerateModelFileOptions = () => {
|
||||
export const useGenerateWorkersModelFileOptions = () => {
|
||||
const [modelFileOptions, setModelFileOptions] = useState<ModelFileOption[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
const getModelFileList = async () => {
|
||||
try {
|
||||
const res = await queryModelFilesList({ page: 1, perPage: 100 });
|
||||
@@ -42,7 +57,7 @@ export const useGenerateModelFileOptions = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const generateModelFileOptions = (list: any[], workerList: any[]) => {
|
||||
const generateWorkersModelFileOptions = (list: any[], workerList: any[]) => {
|
||||
const workerFields = new Set(['name', 'id', 'ip', 'status']);
|
||||
const workersMap = new Map<number, WorkerListItem>();
|
||||
|
||||
@@ -54,7 +69,7 @@ export const useGenerateModelFileOptions = () => {
|
||||
|
||||
const result = Array.from(workersMap.values()).map((worker) => ({
|
||||
label: worker.name,
|
||||
value: worker.name,
|
||||
value: worker.id,
|
||||
labels: worker.labels,
|
||||
parent: true,
|
||||
children: list
|
||||
@@ -63,18 +78,16 @@ export const useGenerateModelFileOptions = () => {
|
||||
item.worker_id === worker.id && !!item.resolved_paths?.length
|
||||
)
|
||||
.map((item) => {
|
||||
const resolved_paths =
|
||||
Array.isArray(item.resolved_paths) && item.resolved_paths.length
|
||||
? item.resolved_paths[0].split('/')
|
||||
: [];
|
||||
const label =
|
||||
resolved_paths.length > 0 ? resolved_paths.pop() : 'Unknown File';
|
||||
return {
|
||||
label: item.resolved_paths[0] || '',
|
||||
value: item.resolved_paths[0] || '',
|
||||
worker_labels: worker.labels,
|
||||
worker_name: worker.name,
|
||||
parent: false,
|
||||
repoId: item.huggingface_repo_id || item.model_scope_model_id || '',
|
||||
fileName:
|
||||
item.huggingface_filename ||
|
||||
item.model_scope_file_path ||
|
||||
item.local_path ||
|
||||
'',
|
||||
...item
|
||||
};
|
||||
}),
|
||||
@@ -95,12 +108,15 @@ export const useGenerateModelFileOptions = () => {
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
setModelFileOptions(result);
|
||||
|
||||
return childrenList;
|
||||
};
|
||||
|
||||
return {
|
||||
getModelFileList,
|
||||
generateModelFileOptions
|
||||
modelFileOptions,
|
||||
generateWorkersModelFileOptions
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { GPUListItem, ListItem } from '../config/types';
|
||||
|
||||
type EmptyObject = Record<never, never>;
|
||||
|
||||
type CascaderOption<T extends object = EmptyObject> = {
|
||||
export type CascaderOption<T extends object = EmptyObject> = {
|
||||
label: string;
|
||||
value: string | number;
|
||||
parent?: boolean;
|
||||
@@ -210,7 +210,10 @@ export const useGenerateWorkerOptions = () => {
|
||||
cluster_id: item.cluster_id,
|
||||
state: item.state,
|
||||
label: item.name,
|
||||
value: item.id
|
||||
value: item.id,
|
||||
id: item.id,
|
||||
labels: item.labels || {},
|
||||
name: item.name
|
||||
}))
|
||||
);
|
||||
setClusterList(
|
||||
|
||||
@@ -349,6 +349,7 @@ const ModelFiles = () => {
|
||||
width={downloadModalStatus.width}
|
||||
hasLinuxWorker={downloadModalStatus.hasLinuxWorker}
|
||||
workerOptions={readyWorkers}
|
||||
workersList={workersList}
|
||||
></DownloadModal>
|
||||
<DeployModal
|
||||
deploymentType="modelFiles"
|
||||
|
||||
@@ -161,6 +161,7 @@ const getModelInfo = (record: ListItem) => {
|
||||
filename: record.huggingface_filename || record.huggingface_repo_id
|
||||
};
|
||||
}
|
||||
|
||||
if (record.source === modelSourceMap.modelscope_value) {
|
||||
return {
|
||||
source: `${source}/${record.model_scope_model_id}`,
|
||||
@@ -169,14 +170,7 @@ const getModelInfo = (record: ListItem) => {
|
||||
filename: record.model_scope_file_path || record.model_scope_model_id
|
||||
};
|
||||
}
|
||||
if (record.source === modelSourceMap.ollama_library_value) {
|
||||
return {
|
||||
source: `${source}/${record.ollama_library_model_name}`,
|
||||
repo_id: record.ollama_library_model_name,
|
||||
title: record.ollama_library_model_name,
|
||||
filename: record.ollama_library_model_name
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
source: `${source}${record.local_path}`,
|
||||
repo_id: record.local_path,
|
||||
@@ -275,7 +269,7 @@ const useFilesColumns = (props: {
|
||||
},
|
||||
render: (text: string, record: ListItem) => {
|
||||
const modelInfo = getModelInfo(record);
|
||||
const { repo_id, source } = modelInfo;
|
||||
const { source } = modelInfo;
|
||||
return (
|
||||
<TextWrapper style={{ paddingRight: 8 }}>
|
||||
<AutoTooltip ghost title={source}>
|
||||
|
||||
@@ -6,7 +6,7 @@ import SealSelect from '@/components/seal-form/seal-select';
|
||||
import SealSwitch from '@/components/seal-form/seal-switch';
|
||||
import { PageAction, PasswordReg } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useIntl, useModel } from '@umijs/max';
|
||||
import { Form, Select } from 'antd';
|
||||
import { useEffect } from 'react';
|
||||
import { UserRoles, UserRolesOptions } from '../config';
|
||||
@@ -28,6 +28,7 @@ const AddModal: React.FC<AddModalProps> = ({
|
||||
data,
|
||||
onCancel
|
||||
}) => {
|
||||
const { initialState } = useModel('@@initialState') || {};
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -125,20 +126,23 @@ const AddModal: React.FC<AddModalProps> = ({
|
||||
</SealSelect>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Form.Item<FormData>
|
||||
name="is_active"
|
||||
rules={[{ required: false }]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<SealSwitch
|
||||
label={intl.formatMessage({ id: 'users.form.active' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'users.form.active.description'
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
{(data?.id !== initialState?.currentUser?.id ||
|
||||
action === PageAction.CREATE) && (
|
||||
<div style={{ flex: 1 }}>
|
||||
<Form.Item<FormData>
|
||||
name="is_active"
|
||||
rules={[{ required: false }]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<SealSwitch
|
||||
label={intl.formatMessage({ id: 'users.form.active' })}
|
||||
description={intl.formatMessage({
|
||||
id: 'users.form.active.description'
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Form.Item<FormData>
|
||||
|
||||
@@ -109,20 +109,37 @@ const useUsersColumns = ({
|
||||
render: (text: string, record: ListItem) => {
|
||||
return (
|
||||
<>
|
||||
<Tag
|
||||
style={{
|
||||
marginRight: 0,
|
||||
paddingInline: 12,
|
||||
borderRadius: 12
|
||||
}}
|
||||
color={record.is_active ? 'success' : 'default'}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: record.is_active
|
||||
? 'users.status.active'
|
||||
: 'users.status.inactive'
|
||||
})}
|
||||
</Tag>
|
||||
{record.is_active ? (
|
||||
<Tag
|
||||
style={{
|
||||
marginRight: 0,
|
||||
paddingInline: 12,
|
||||
borderRadius: 12,
|
||||
background: 'unset',
|
||||
borderColor: 'var(--ant-color-success)'
|
||||
}}
|
||||
color="success"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'users.status.active'
|
||||
})}
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag
|
||||
style={{
|
||||
marginRight: 0,
|
||||
paddingInline: 12,
|
||||
borderRadius: 12,
|
||||
background: 'unset',
|
||||
color: 'var(--ant-color-text-description)'
|
||||
}}
|
||||
color="default"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'users.status.inactive'
|
||||
})}
|
||||
</Tag>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user