fix(style): textarea label background

This commit is contained in:
jialin
2026-05-25 22:42:02 +08:00
committed by jialin
parent 27d57d7563
commit f886422f91
6 changed files with 286 additions and 28 deletions
@@ -45,6 +45,24 @@ export const GPU_SERVICE_INSTANCE_PV_EVENTS_API = (params: {
return `/clusters/${params.clusterID}/proxy/apis/worker.gpustack.ai/v1/namespaces/${params.namespace}/instancepersistentvolumes/${params.name}/events`;
};
// TODO: replace placeholder once start/stop endpoints are finalized
export const GPU_SERVICE_INSTANCE_START_API = (params: {
namespace: string;
name: string;
clusterID?: number;
}) => {
return `/clusters/${params.clusterID}/proxy/apis/worker.gpustack.ai/v1/namespaces/${params.namespace}/instances/${params.name}/start`;
};
// TODO: replace placeholder once start/stop endpoints are finalized
export const GPU_SERVICE_INSTANCE_STOP_API = (params: {
namespace: string;
name: string;
clusterID?: number;
}) => {
return `/clusters/${params.clusterID}/proxy/apis/worker.gpustack.ai/v1/namespaces/${params.namespace}/instances/${params.name}/stop`;
};
// =========== Instances ===========
export async function queryGPUServiceInstances(
@@ -212,6 +230,56 @@ export async function queryGPUServiceInstancePVEvents(
);
}
// TODO: replace placeholder once start/stop endpoints are finalized
export async function startGPUServiceInstance(
params: {
namespace: string;
name: string;
clusterID?: number;
},
option?: any
) {
if (!params.clusterID) {
return;
}
return request(
GPU_SERVICE_INSTANCE_START_API({
namespace: params.namespace,
clusterID: params.clusterID,
name: params.name
}),
{
method: 'POST',
cancelToken: option?.token
}
);
}
// TODO: replace placeholder once start/stop endpoints are finalized
export async function stopGPUServiceInstance(
params: {
namespace: string;
name: string;
clusterID?: number;
},
option?: any
) {
if (!params.clusterID) {
return;
}
return request(
GPU_SERVICE_INSTANCE_STOP_API({
namespace: params.namespace,
clusterID: params.clusterID,
name: params.name
}),
{
method: 'POST',
cancelToken: option?.token
}
);
}
export async function queryGPUServiceInstanceLog(
params: InstanceLogQueryParams & {
namespace: string;
@@ -64,6 +64,18 @@ export const rowActionList = [
locale: true,
icon: icons.ProfileOutlined
},
{
label: 'common.button.start',
key: 'start',
locale: true,
icon: icons.Play
},
{
label: 'common.button.stop',
key: 'stop',
locale: true,
icon: icons.Stop
},
// {
// label: 'common.button.edit',
// key: 'edit',
@@ -87,6 +99,27 @@ export const rowActionList = [
}
];
export const batchActionList = [
{
label: 'common.button.start',
key: 'start',
icon: icons.Play
},
{
label: 'common.button.stop',
key: 'stop',
icon: icons.Stop
},
{
label: 'common.button.delete',
key: 'delete',
icon: icons.DeleteOutlined,
props: {
danger: true
}
}
];
export const InstanceTypePhaseValueMap = {
// Available: 'Available',
// Unavailable: 'Unavailable'
@@ -4,6 +4,7 @@ import { Input as CInput } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import { FormData } from '../config/types';
import formStyles from '../styles/instances.module.less';
const Basic = ({
action,
@@ -37,13 +38,15 @@ const Basic = ({
required
/>
</Form.Item>
<Form.Item<FormData> name={['spec', 'description']}>
<CInput.TextArea
disabled={disabled}
label={intl.formatMessage({ id: 'common.table.description' })}
scaleSize
/>
</Form.Item>
<div className={formStyles.command}>
<Form.Item<FormData> name={['spec', 'description']}>
<CInput.TextArea
disabled={disabled}
label={intl.formatMessage({ id: 'common.table.description' })}
scaleSize
/>
</Form.Item>
</div>
</>
);
};
+148 -5
View File
@@ -5,16 +5,28 @@ import { PaginationKey, TABLE_SORT_DIRECTIONS } from '@/config/settings';
import useTableFetch from '@/hooks/use-table-fetch';
import { ProviderValueMap } from '@/pages/cluster-management/config';
import { useQueryClusterList } from '@/pages/cluster-management/services/use-query-cluster-list';
import { handleBatchRequest } from '@/utils';
import { PlusOutlined } from '@ant-design/icons';
import {
BaseSelect,
DeleteModal,
DropdownButtons,
FilterBar,
IconFont,
NoResult
} from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Divider, Flex, message, Modal, Table } from 'antd';
import {
Button,
ConfigProvider,
Divider,
Flex,
message,
Modal,
Space,
Table
} from 'antd';
import { useAtom } from 'jotai';
import _ from 'lodash';
import { useCallback, useEffect, useMemo } from 'react';
@@ -22,11 +34,14 @@ import { PageContainerInner } from '../../_components/page-box';
import {
deleteGPUServiceInstance,
GPU_SERVICE_INSTANCES_API,
queryGPUServiceInstances
queryGPUServiceInstances,
startGPUServiceInstance,
stopGPUServiceInstance
} from './apis';
import AddModal from './components/add-modal';
import ViewEventsModal from './components/view-events-modal';
import ViewLogsModal from './components/view-logs-modal';
import { batchActionList } from './config';
import { FormData, ListItem } from './config/types';
import useCreateInstance from './hooks/use-create-instance';
import useInstancesColumns from './hooks/use-instances-columns';
@@ -49,6 +64,26 @@ const GPUService: React.FC = () => {
[namespace, clusterID]
);
const startInstance = useCallback(
(row: ListItem) =>
startGPUServiceInstance({
namespace: row.metadata?.namespace || namespace,
clusterID,
name: row.metadata?.name as string
}),
[namespace, clusterID]
);
const stopInstance = useCallback(
(row: ListItem) =>
stopGPUServiceInstance({
namespace: row.metadata?.namespace || namespace,
clusterID,
name: row.metadata?.name as string
}),
[namespace, clusterID]
);
const fetchInstances = useMemoizedFn(
async (
params: any,
@@ -225,6 +260,80 @@ const GPUService: React.FC = () => {
});
});
const handleStart = useMemoizedFn(async (row: ListItem) => {
modalRef.current?.show({
title: intl.formatMessage({ id: 'common.title.start.confirm' }),
content: 'gpuservice.instance',
okText: 'common.button.start',
operation: 'common.start.single.confirm',
name: row.metadata?.name,
async onOk() {
try {
await startInstance(row);
message.success(intl.formatMessage({ id: 'common.message.success' }));
fetchData();
} catch (error) {
// ignore
}
}
});
});
const handleStop = useMemoizedFn(async (row: ListItem) => {
modalRef.current?.show({
title: intl.formatMessage({ id: 'common.title.stop.confirm' }),
content: 'gpuservice.instance',
okText: 'common.button.stop',
operation: 'common.stop.single.confirm',
name: row.metadata?.name,
async onOk() {
try {
await stopInstance(row);
message.success(intl.formatMessage({ id: 'common.message.success' }));
fetchData();
} catch (error) {
// ignore
}
}
});
});
const handleStartBatch = useMemoizedFn(() => {
modalRef.current?.show({
title: intl.formatMessage({ id: 'common.title.start.confirm' }),
content: 'gpuservice.instance',
okText: 'common.button.start',
operation: 'common.start.confirm',
selection: true,
async onOk() {
const res = await handleBatchRequest(
rowSelection.selectedRows as ListItem[],
startInstance
);
fetchData();
return res;
}
});
});
const handleStopBatch = useMemoizedFn(() => {
modalRef.current?.show({
title: intl.formatMessage({ id: 'common.title.stop.confirm' }),
content: 'gpuservice.instance',
okText: 'common.button.stop',
operation: 'common.stop.confirm',
selection: true,
async onOk() {
const res = await handleBatchRequest(
rowSelection.selectedRows as ListItem[],
stopInstance
);
fetchData();
return res;
}
});
});
const handleSelect = useMemoizedFn((val: string, row: ListItem) => {
if (val === 'view') {
openViewInstanceModal(row);
@@ -244,6 +353,20 @@ const GPUService: React.FC = () => {
openViewLogsModal(row);
} else if (val === 'viewevent') {
openViewEventsModal(row);
} else if (val === 'start') {
handleStart(row);
} else if (val === 'stop') {
handleStop(row);
}
});
const handleBatchActionSelect = useMemoizedFn((val: string) => {
if (val === 'start') {
handleStartBatch();
} else if (val === 'stop') {
handleStopBatch();
} else if (val === 'delete') {
handleDeleteBatch();
}
});
@@ -317,14 +440,34 @@ const GPUService: React.FC = () => {
selectHolder={intl.formatMessage({
id: 'gpuservice.instance.filter.cluster'
})}
buttonText={intl.formatMessage({ id: 'gpuservice.instance.add' })}
handleSearch={handleSearch}
handleSelectChange={handleClusterChange}
handleDeleteByBatch={handleDeleteBatch}
handleClickPrimary={openCreateInstanceModal}
handleInputChange={handleNameChange}
rowSelection={rowSelection}
widths={{ input: 300 }}
right={
<Space size={16}>
<Button
icon={<PlusOutlined />}
type="primary"
onClick={openCreateInstanceModal}
>
{intl.formatMessage({ id: 'gpuservice.instance.add' })}
</Button>
<DropdownButtons
items={batchActionList}
extra={
rowSelection.selectedRowKeys.length > 0 && (
<span>({rowSelection.selectedRowKeys.length})</span>
)
}
size="large"
showText={true}
disabled={!rowSelection.selectedRowKeys.length}
onSelect={handleBatchActionSelect}
/>
</Space>
}
/>
<ConfigProvider renderEmpty={renderEmpty}>
<Table
@@ -24,3 +24,11 @@
flex: 1;
min-height: 0;
}
.command {
:global {
.seal-textarea-label {
background-color: transparent;
}
}
}
+19 -16
View File
@@ -10,6 +10,7 @@ import { useIntl } from '@umijs/max';
import { Flex, Form } from 'antd';
import { useMemo } from 'react';
import { GPUsConfigs } from '../../../resources/config/gpu-driver';
import formStyles from '../../instances/styles/instances.module.less';
import {
ImagePullPolicyOptions,
normalizeCommand,
@@ -154,22 +155,24 @@ const Basic: React.FC<BasicProps> = ({
}))}
/>
</Form.Item>
<Form.Item<FormData>
name={['spec', 'command']}
normalize={normalizeCommand}
getValueProps={(value) => ({ value: stringifyCommand(value) })}
>
<Textarea
disabled={disabled}
label={intl.formatMessage({ id: 'gpuservice.template.command' })}
placeholder={intl.formatMessage({
id: 'gpuservice.template.command.placeholder'
})}
trim={false}
alwaysFocus
scaleSize
/>
</Form.Item>
<div className={formStyles.command}>
<Form.Item<FormData>
name={['spec', 'command']}
normalize={normalizeCommand}
getValueProps={(value) => ({ value: stringifyCommand(value) })}
>
<Textarea
disabled={disabled}
label={intl.formatMessage({ id: 'gpuservice.template.command' })}
placeholder={intl.formatMessage({
id: 'gpuservice.template.command.placeholder'
})}
trim={false}
alwaysFocus
scaleSize
/>
</Form.Item>
</div>
<Flex gap={16}>
<div style={{ flex: 1 }}>