mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
feat: feature/1.5-Model Deployment Process Popup Optimization
This commit is contained in:
@@ -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()
|
||||
|
||||
+1
-1
@@ -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 (
|
||||
|
||||
Reference in New Issue
Block a user