feat: feature/1.5-Model Deployment Process Popup Optimization

This commit is contained in:
ningyv
2025-02-13 18:22:58 +08:00
parent 86bb513db6
commit 2fbaba710d
6 changed files with 130 additions and 66 deletions
@@ -11,6 +11,7 @@ export type SystemTableListItem = {
serviceNum: number,
description:string;
master:EntityItem;
state: string
service_kind:'ai'|'rest',
createTime:string;
};
@@ -110,6 +110,13 @@ const LocalModelList: React.FC = () => {
const [searchWord, setSearchWord] = useState<string>('')
const localAiDeployRef = useRef<LocalAiDeployHandle>()
const EditLocalModelModalRef = useRef<EditLocalModelModalHandle>()
const [stateColumnMap] = useState<{ [k: string]: { text: string; className?: string } }>({
normal: { text: '正常' },
deploying: { text: '部署中', className: 'text-[#2196f3] cursor-pointer' },
error: { text: '模型异常', className: 'text-[#ff4d4f]' },
disabled: { text: '停用', className: 'text-[#999]' },
deploying_error: { text: '部署失败', className: 'text-[#ff4d4f] cursor-pointer' }
})
const handleEdit = (record: ModelListData) => {
modal.confirm({
@@ -289,15 +296,15 @@ const LocalModelList: React.FC = () => {
ellipsis: true,
render: (dom: React.ReactNode, entity: ModelListData) => (
<span
className={`text-[13px] ${entity?.state === 'deploying' ? '[&>.ant-typography]:text-[#2196f3] cursor-pointer' : entity?.state === 'error' ? '[&>.ant-typography]:text-[#ff4d4f] cursor-pointer' : ''}`}
className={`text-[13px] ${stateColumnMap[entity?.state as string]?.className}`}
onClick={(e) => {
if (['deploying', 'error'].includes(entity?.state as string)) {
if (['deploying', 'deploying_error'].includes(entity?.state as string)) {
e?.stopPropagation()
openLogsModal(entity)
}
}}
>
{dom}
{stateColumnMap[entity?.state as string]?.text || '-'}
</span>
)
},
@@ -1,6 +1,6 @@
export type ModelStatus = 'enabled' | 'abnormal' | 'disabled'
export type KeyStatus = 'normal' | 'abnormal' | 'disabled'
export type ModelDeployStatus = 'normal' | 'disabled' | 'deploying' | 'error' | undefined
export type ModelDeployStatus = 'normal' | 'disabled' | 'deploying' | 'error' | 'deploying_error' | undefined
export interface KeyData {
id: string
@@ -31,10 +31,10 @@ const SystemList: FC = () => {
const [open, setOpen] = useState(false)
const drawerFormRef = useRef<SystemConfigHandle>(null)
const { checkPermission, accessInit, getGlobalAccessData, state } = useGlobalContext()
const [stateColumnMap] = useState<{ [k: string]: { text: string } }>({
const [stateColumnMap] = useState<{ [k: string]: { text: string; className?: string } }>({
normal: { text: '正常' },
deploying: { text: '部署中' },
error: { text: '异常' },
deploying: { text: '部署中', className: 'text-[#2196f3]' },
error: { text: '异常', className: 'text-[#ff4d4f]' },
public: { text: '公共服务' },
private: { text: '私有服务' }
})
@@ -171,9 +171,9 @@ const SystemList: FC = () => {
})
}
if ((x.dataIndex as string) === 'state') {
x.render = (text: any, record: any) => (
x.render = (dom: React.ReactNode, record: any) => (
<span
className={`text-[13px] ${record.state === 'deploying' ? 'text-[#2196f3]' : record.state === 'error' ? 'text-[#ff4d4f]' : ''}`}
className={`text-[13px] ${stateColumnMap[record.state]?.className}`}
onClick={(e) => {
if (['deploying', 'error'].includes(record.state)) {
e?.stopPropagation()
@@ -73,7 +73,7 @@ export const LogsFooter = (props: any) => {
}
return (
<>
{record.state === 'error' ? (
{['deploying_error', 'error'].includes(record.state) ? (
<div className="flex justify-end items-center">
<Button onClick={() => { modalInstance.destroy() }}>{$t('取消')}</Button>
<Button onClick={deleteService} type="primary" danger>
@@ -1,30 +1,30 @@
import { SystemTableListItem } from '@core/const/system/type'
import { Steps } from 'antd'
import { App, Steps } from 'antd'
import { CheckCircleOutlined, LoadingOutlined, ClockCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'
import { Codebox } from '@common/components/postcat/api/Codebox'
import { Collapse } from 'antd'
import { useEffect, useRef, useState } from 'react'
import { $t } from '@common/locales/index.ts'
import { useFetch } from '@common/hooks/http'
const getIcon = (status: string) => {
switch (status) {
case 'completed':
return <CheckCircleOutlined style={{ color: 'green', fontSize: '40px' }} />
case 'inProgress':
return <LoadingOutlined style={{ color: '#2196f3', fontSize: '40px' }} />
case 'pending':
return <ClockCircleOutlined style={{ color: 'gray', fontSize: '40px' }} />
case 'error':
return <CloseCircleOutlined style={{ color: 'red', fontSize: '40px' }} />
default:
return null
}
}
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
export const ServiceDeployment = (props: { record: SystemTableListItem, closeModal?: () => void }) => {
const { record, closeModal } = props
const { message } = App.useApp()
const getIcon = (status: string) => {
switch (status) {
case 'completed':
return <CheckCircleOutlined style={{ color: 'green', fontSize: '40px' }} />
case 'inProgress':
return <LoadingOutlined style={{ color: '#2196f3', fontSize: '40px' }} />
case 'pending':
return <ClockCircleOutlined style={{ color: 'gray', fontSize: '40px' }} />
case 'error':
return <CloseCircleOutlined style={{ color: 'red', fontSize: '40px' }} />
default:
return null
}
}
const [stepItem, setStepItem] = useState<
{
id: string
@@ -55,54 +55,110 @@ export const ServiceDeployment = (props: { record: SystemTableListItem, closeMod
const [collapseText] = useState('Progress log')
const { fetchData } = useFetch()
const updateStepItems = (targetStep: number, description = '') => {
/**
* 根据状态获取当前步骤
* @param currentState 当前状态
* @returns
*/
const getCurrentStep = (currentState?: string) => {
switch (currentState) {
case 'download':
case 'download_error':
return 0
case 'deploy':
case 'deploy_error':
return 1
case 'initializing':
case 'initializing_error':
return 2
default:
return 0
}
}
/**
* 更新步骤
* @param targetStep 目标步骤
* @param description 描述
* @param currentState 当前状态
*/
const updateStepItems = (targetStep: number, description = '', currentState?: string) => {
setStepItem((prevItems) =>
prevItems.map((item, index) => ({
...item,
description: item.id === 'download' ? description : item.description,
status: index < targetStep ? 'completed' : index === targetStep ? 'inProgress' : 'pending',
status: index < targetStep ? 'completed' : index === targetStep ? currentState && currentState.includes('error') ? 'error' : 'inProgress' : 'pending',
}))
);
step.current = targetStep;
};
useEffect(() => {
fetchData(
'model/local/deploy',
{
method: 'POST',
eoBody: { recordId: record.id },
headers: {
'Content-Type': 'event-stream'
},
isStream: true,
handleStream: (chunk) => {
const parsedChunk = JSON.parse(chunk)
// 下载中
if (parsedChunk?.data?.state.includes('download')) {
updateStepItems(0, `${parsedChunk?.data?.info?.current} / ${parsedChunk?.data?.info?.total}`);
// 部署中
} else if (parsedChunk?.data?.state.includes('deploy')) {
updateStepItems(1);
// 初始化中
} else if (parsedChunk?.data?.state.includes('initializing')) {
updateStepItems(2);
// 完成
} else if (parsedChunk?.data?.state.includes('finish')) {
updateStepItems(4);
setTimeout(() => {
closeModal?.()
}, 200)
} else if (parsedChunk?.data?.state.includes('error')) {
setStepItem((prevItems) =>
prevItems.map((item, index) => {
return { ...item, status: index === step.current ? 'error' : item.status }
})
)
}
setScriptStr(parsedChunk?.data?.message || '')
/**
* 获取本地模型状态
* @returns
*/
const getLocalModelState = () => {
fetchData<BasicResponse<any>>('model/local/state', {
method: 'DELETE',
eoParams: {
model: record.id
},
eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
})
.then((response) => {
if (response.code === STATUS_CODE.SUCCESS) {
updateStepItems(getCurrentStep(response.data?.state), `${response.data?.info?.current} / ${response.data?.info?.total}`, response.data?.state)
setScriptStr(response?.data?.info?.last_message || '')
} else {
message.error(response.msg || RESPONSE_TIPS.error)
}
}
)
})
.catch((error) => {
message.error(RESPONSE_TIPS.error)
})
}
useEffect(() => {
if (['deploying_error', 'error'].includes(record.state)) {
getLocalModelState()
} else {
fetchData(
'model/local/deploy',
{
method: 'POST',
eoBody: { recordId: record.id },
headers: {
'Content-Type': 'event-stream'
},
isStream: true,
handleStream: (chunk) => {
const parsedChunk = JSON.parse(chunk)
// 下载中
if (parsedChunk?.data?.state.includes('download')) {
updateStepItems(0, `${parsedChunk?.data?.info?.current} / ${parsedChunk?.data?.info?.total}`);
// 部署中
} else if (parsedChunk?.data?.state.includes('deploy')) {
updateStepItems(1);
// 初始化中
} else if (parsedChunk?.data?.state.includes('initializing')) {
updateStepItems(2);
// 完成
} else if (parsedChunk?.data?.state.includes('finish')) {
updateStepItems(4);
setTimeout(() => {
closeModal?.()
}, 200)
} else if (parsedChunk?.data?.state.includes('error')) {
setStepItem((prevItems) =>
prevItems.map((item, index) => {
return { ...item, status: index === step.current ? 'error' : item.status }
})
)
}
setScriptStr(parsedChunk?.data?.message || '')
}
}
)
}
}, [])
return (