| @@ -188,14 +188,23 @@ export default [ | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'modelDseployment', | |||||
| path: '/modelDseployment', | |||||
| name: 'modelDeployment', | |||||
| path: '/modelDeployment', | |||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: '模型部署', | |||||
| name: '模型列表', | |||||
| path: '', | path: '', | ||||
| key: 'modelDseployment', | |||||
| component: './missingPage.jsx', | |||||
| component: './ModelDeployment/list', | |||||
| }, | |||||
| { | |||||
| name: '镜像详情', | |||||
| path: ':id', | |||||
| component: './ModelDeployment/info', | |||||
| }, | |||||
| { | |||||
| name: '创建镜像', | |||||
| path: 'create', | |||||
| component: './ModelDeployment/create', | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| @@ -0,0 +1,11 @@ | |||||
| .mirror-status-cell { | |||||
| color: @text-color; | |||||
| &--success { | |||||
| color: @success-color; | |||||
| } | |||||
| &--error { | |||||
| color: @error-color; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,39 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-18 18:35:41 | |||||
| * @Description: | |||||
| */ | |||||
| import { MirrorVersionStatus } from '@/enums'; | |||||
| import styles from './index.less'; | |||||
| type MirrorVersionStatusKeys = keyof typeof MirrorVersionStatus; | |||||
| type MirrorVersionStatusValues = (typeof MirrorVersionStatus)[MirrorVersionStatusKeys]; | |||||
| export type MirrorVersionStatusInfo = { | |||||
| text: string; | |||||
| classname: string; | |||||
| }; | |||||
| const statusInfo: Record<MirrorVersionStatusValues, MirrorVersionStatusInfo> = { | |||||
| [MirrorVersionStatus.Building]: { | |||||
| text: '构建中', | |||||
| classname: styles['mirror-status-cell'], | |||||
| }, | |||||
| [MirrorVersionStatus.Available]: { | |||||
| classname: styles['mirror-status-cell--success'], | |||||
| text: '可用', | |||||
| }, | |||||
| [MirrorVersionStatus.Failed]: { | |||||
| classname: styles['mirror-status-cell--error'], | |||||
| text: '构建失败', | |||||
| }, | |||||
| }; | |||||
| function MirrorStatusCell(status: MirrorVersionStatus) { | |||||
| if (status === null || status === undefined || !statusInfo[status]) { | |||||
| return <span>--</span>; | |||||
| } | |||||
| return <span className={statusInfo[status].classname}>{statusInfo[status].text}</span>; | |||||
| } | |||||
| export default MirrorStatusCell; | |||||
| @@ -0,0 +1,17 @@ | |||||
| .model-deployment-create { | |||||
| height: 100%; | |||||
| &__content { | |||||
| height: calc(100% - 60px); | |||||
| margin-top: 10px; | |||||
| padding: 30px 30px 10px; | |||||
| overflow: auto; | |||||
| background-color: white; | |||||
| border-radius: 10px; | |||||
| &__type { | |||||
| color: @text-color; | |||||
| font-size: @font-size-input-lg; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,297 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-16 13:58:08 | |||||
| * @Description: 创建模型部署 | |||||
| */ | |||||
| import PageTitle from '@/components/PageTitle'; | |||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||||
| import { CommonTabKeys } from '@/enums'; | |||||
| import { createMirrorReq } from '@/services/mirror'; | |||||
| import { getComputingResourceReq } from '@/services/pipeline'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { getSessionItemThenRemove, mirrorNameKey } from '@/utils/sessionStorage'; | |||||
| import { validateUploadFiles } from '@/utils/ui'; | |||||
| import { useNavigate } from '@umijs/max'; | |||||
| import { Button, Col, Form, Input, Row, Select, UploadFile, message, type SelectProps } from 'antd'; | |||||
| import { omit } from 'lodash'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import styles from './create.less'; | |||||
| type FormData = { | |||||
| name: string; | |||||
| tag: string; | |||||
| description: string; | |||||
| path?: string; | |||||
| upload_type: string; | |||||
| fileList?: UploadFile[]; | |||||
| }; | |||||
| function ModelDeploymentCreate() { | |||||
| const navgite = useNavigate(); | |||||
| const [form] = Form.useForm(); | |||||
| const [nameDisabled, setNameDisabled] = useState(false); | |||||
| const [resourceStandardList, setResourceStandardList] = useState([]); | |||||
| useEffect(() => { | |||||
| const name = getSessionItemThenRemove(mirrorNameKey); | |||||
| if (name) { | |||||
| form.setFieldValue('name', name); | |||||
| setNameDisabled(true); | |||||
| } | |||||
| getComputingResource(); | |||||
| }, []); | |||||
| const getComputingResource = async () => { | |||||
| const params = { | |||||
| page: 0, | |||||
| size: 1000, | |||||
| resource_type: '', | |||||
| }; | |||||
| const [res] = await to(getComputingResourceReq(params)); | |||||
| if (res && res.data && res.data.content) { | |||||
| setResourceStandardList(res.data.content); | |||||
| } | |||||
| }; | |||||
| const filterResourceStandard: SelectProps['filterOption'] = ( | |||||
| input: string, | |||||
| { computing_resource = '' }, | |||||
| ) => { | |||||
| return computing_resource.toLocaleLowerCase().includes(input.toLocaleLowerCase()); | |||||
| }; | |||||
| // 创建公网、本地镜像 | |||||
| const createPublicMirror = async (formData: FormData) => { | |||||
| const upload_type = formData['upload_type']; | |||||
| let params; | |||||
| if (upload_type === CommonTabKeys.Public) { | |||||
| params = { | |||||
| ...omit(formData, ['upload_type']), | |||||
| upload_type: 0, | |||||
| image_type: 0, | |||||
| }; | |||||
| } else { | |||||
| const fileList = formData['fileList'] ?? []; | |||||
| if (validateUploadFiles(fileList)) { | |||||
| const file = fileList[0]; | |||||
| params = { | |||||
| ...omit(formData, ['fileList', 'upload_type']), | |||||
| path: file.response.data.url, | |||||
| file_size: file.response.data.fileSize, | |||||
| upload_type: 1, | |||||
| image_type: 0, | |||||
| }; | |||||
| } | |||||
| } | |||||
| const [res] = await to(createMirrorReq(params)); | |||||
| if (res) { | |||||
| message.success('创建成功'); | |||||
| navgite(-1); | |||||
| } | |||||
| }; | |||||
| // 提交 | |||||
| const handleSubmit = (values: FormData) => { | |||||
| createPublicMirror(values); | |||||
| }; | |||||
| // 取消 | |||||
| const cancel = () => { | |||||
| navgite(-1); | |||||
| }; | |||||
| return ( | |||||
| <div className={styles['model-deployment-create']}> | |||||
| <PageTitle title="创建推理服务"></PageTitle> | |||||
| <div className={styles['model-deployment-create__content']}> | |||||
| <div> | |||||
| <Form | |||||
| name="model-deployment-create" | |||||
| labelCol={{ flex: '130px' }} | |||||
| wrapperCol={{ flex: 1 }} | |||||
| labelAlign="left" | |||||
| form={form} | |||||
| initialValues={{ upload_type: CommonTabKeys.Public }} | |||||
| onFinish={handleSubmit} | |||||
| size="large" | |||||
| > | |||||
| <SubAreaTitle | |||||
| title="基本信息" | |||||
| image={require('@/assets/img/mirror-basic.png')} | |||||
| style={{ marginBottom: '26px' }} | |||||
| ></SubAreaTitle> | |||||
| <Row gutter={10}> | |||||
| <Col span={10}> | |||||
| <Form.Item | |||||
| label="服务名称" | |||||
| name="name" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入服务名称', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input | |||||
| placeholder="请输入服务名称" | |||||
| maxLength={64} | |||||
| disabled={nameDisabled} | |||||
| showCount | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={10}> | |||||
| <Col span={20}> | |||||
| <Form.Item | |||||
| label="描 述" | |||||
| name="description" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入描述', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input.TextArea | |||||
| autoSize={{ minRows: 2, maxRows: 6 }} | |||||
| placeholder="请输入描述,最长128字符" | |||||
| maxLength={128} | |||||
| showCount | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <SubAreaTitle | |||||
| title="部署构建" | |||||
| image={require('@/assets/img/mirror-version.png')} | |||||
| style={{ marginTop: '20px', marginBottom: '24px' }} | |||||
| ></SubAreaTitle> | |||||
| <Row gutter={10}> | |||||
| <Col span={10}> | |||||
| <Form.Item | |||||
| label="选择模型" | |||||
| name="name" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入模型', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input | |||||
| placeholder="请输入模型" | |||||
| maxLength={64} | |||||
| disabled={nameDisabled} | |||||
| showCount | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={10}> | |||||
| <Col span={10}> | |||||
| <Form.Item | |||||
| label="选择镜像" | |||||
| name="name" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入镜像', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input | |||||
| placeholder="请输入镜像" | |||||
| maxLength={64} | |||||
| disabled={nameDisabled} | |||||
| showCount | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={10}> | |||||
| <Col span={10}> | |||||
| <Form.Item | |||||
| label="资源规格" | |||||
| name="name" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请选择资源规格', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Select | |||||
| showSearch | |||||
| placeholder="请选择资源规格" | |||||
| filterOption={filterResourceStandard} | |||||
| options={resourceStandardList} | |||||
| fieldNames={{ | |||||
| label: 'description', | |||||
| value: 'standard', | |||||
| }} | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={10}> | |||||
| <Col span={10}> | |||||
| <Form.Item | |||||
| label="副本数量" | |||||
| name="name" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: '请输入副本数量', | |||||
| }, | |||||
| ]} | |||||
| > | |||||
| <Input | |||||
| placeholder="请输入副本数量" | |||||
| maxLength={64} | |||||
| disabled={nameDisabled} | |||||
| showCount | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={10}> | |||||
| <Col span={10}> | |||||
| <Form.Item label="环境变量" name="name"> | |||||
| <Button type="link" style={{ padding: '0' }}> | |||||
| 添加环境变量 | |||||
| </Button> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Form.Item wrapperCol={{ offset: 0, span: 16 }}> | |||||
| <Button type="primary" htmlType="submit"> | |||||
| 确定 | |||||
| </Button> | |||||
| <Button | |||||
| type="default" | |||||
| htmlType="button" | |||||
| onClick={cancel} | |||||
| style={{ marginLeft: '20px' }} | |||||
| > | |||||
| 取消 | |||||
| </Button> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ModelDeploymentCreate; | |||||
| @@ -0,0 +1,53 @@ | |||||
| .model-deployment-info { | |||||
| height: 100%; | |||||
| &__basic { | |||||
| &__item { | |||||
| display: flex; | |||||
| align-items: flex-start; | |||||
| font-size: 16px; | |||||
| line-height: 1.6; | |||||
| .label { | |||||
| width: 80px; | |||||
| color: @text-color-secondary; | |||||
| } | |||||
| .value { | |||||
| flex: 1; | |||||
| color: @text-color; | |||||
| } | |||||
| } | |||||
| } | |||||
| &__content { | |||||
| height: calc(100% - 60px); | |||||
| margin-top: 10px; | |||||
| padding: 30px 30px 0; | |||||
| background-color: white; | |||||
| border-radius: 10px; | |||||
| &__title { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| } | |||||
| &__table { | |||||
| :global { | |||||
| .ant-table-wrapper { | |||||
| height: 100%; | |||||
| .ant-spin-nested-loading { | |||||
| height: 100%; | |||||
| } | |||||
| .ant-spin-container { | |||||
| height: 100%; | |||||
| } | |||||
| .ant-table { | |||||
| height: calc(100% - 74px); | |||||
| overflow: auto; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,149 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-16 13:58:08 | |||||
| * @Description: 镜像详情 | |||||
| */ | |||||
| import KFIcon from '@/components/KFIcon'; | |||||
| import PageTitle from '@/components/PageTitle'; | |||||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||||
| import { getMirrorInfoReq } from '@/services/mirror'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { useNavigate, useParams } from '@umijs/max'; | |||||
| import { Col, Row, Tabs, type TabsProps } from 'antd'; | |||||
| import dayjs from 'dayjs'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import styles from './info.less'; | |||||
| type MirrorInfoData = { | |||||
| name?: string; | |||||
| description?: string; | |||||
| version_count?: string; | |||||
| create_time?: string; | |||||
| }; | |||||
| type MirrorVersionData = { | |||||
| id: number; | |||||
| version: string; | |||||
| url: string; | |||||
| status: string; | |||||
| file_size: string; | |||||
| create_time: string; | |||||
| }; | |||||
| const tabItems = [ | |||||
| { | |||||
| key: '1', | |||||
| label: '预测', | |||||
| icon: <KFIcon type="icon-yuce" />, | |||||
| }, | |||||
| { | |||||
| key: '2', | |||||
| label: '调用指南', | |||||
| icon: <KFIcon type="icon-tiaoyongzhinan" />, | |||||
| }, | |||||
| { | |||||
| key: '3', | |||||
| label: '服务日志', | |||||
| icon: <KFIcon type="icon-fuwurizhi" />, | |||||
| }, | |||||
| ]; | |||||
| function ModelDeploymentInfo() { | |||||
| const navigate = useNavigate(); | |||||
| const urlParams = useParams(); | |||||
| const [mirrorInfo, setMirrorInfo] = useState<MirrorInfoData>({}); | |||||
| const [activeTab, setActiveTab] = useState<string>('1'); | |||||
| useEffect(() => { | |||||
| getMirrorInfo(); | |||||
| }, []); | |||||
| // 获取镜像详情 | |||||
| const getMirrorInfo = async () => { | |||||
| const id = Number(urlParams.id); | |||||
| const [res] = await to(getMirrorInfoReq(id)); | |||||
| if (res && res.data) { | |||||
| const { name = '', description = '', version_count = '', create_time: time } = res.data; | |||||
| let create_time = | |||||
| time && dayjs(time).isValid() ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '--'; | |||||
| setMirrorInfo({ | |||||
| name, | |||||
| description, | |||||
| version_count, | |||||
| create_time, | |||||
| }); | |||||
| } | |||||
| }; | |||||
| // 切换 Tab,重置数据 | |||||
| const hanleTabChange: TabsProps['onChange'] = (value) => { | |||||
| setActiveTab(value); | |||||
| }; | |||||
| return ( | |||||
| <div className={styles['model-deployment-info']}> | |||||
| <PageTitle title="服务详情"></PageTitle> | |||||
| <div className={styles['model-deployment-info__content']}> | |||||
| <div> | |||||
| <SubAreaTitle | |||||
| title="基本信息" | |||||
| image={require('@/assets/img/mirror-basic.png')} | |||||
| style={{ marginBottom: '26px' }} | |||||
| ></SubAreaTitle> | |||||
| <div className={styles['model-deployment-info__basic']}> | |||||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||||
| <Col span={10}> | |||||
| <div className={styles['model-deployment-info__basic__item']}> | |||||
| <div className={styles['label']}>服务名称:</div> | |||||
| <div className={styles['value']}>{mirrorInfo.name}</div> | |||||
| </div> | |||||
| </Col> | |||||
| <Col span={10}> | |||||
| <div className={styles['model-deployment-info__basic__item']}> | |||||
| <div className={styles['label']}>镜像:</div> | |||||
| <div className={styles['value']}>{mirrorInfo.version_count ?? '--'}</div> | |||||
| </div> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||||
| <Col span={10}> | |||||
| <div className={styles['model-deployment-info__basic__item']}> | |||||
| <div className={styles['label']}>状态:</div> | |||||
| <div className={styles['value']}>{mirrorInfo.name}</div> | |||||
| </div> | |||||
| </Col> | |||||
| <Col span={10}> | |||||
| <div className={styles['model-deployment-info__basic__item']}> | |||||
| <div className={styles['label']}>模型:</div> | |||||
| <div className={styles['value']}>{mirrorInfo.version_count ?? '--'}</div> | |||||
| </div> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||||
| <Col span={10}> | |||||
| <div className={styles['model-deployment-info__basic__item']}> | |||||
| <div className={styles['label']}>环境变量:</div> | |||||
| <div className={styles['value']}>{mirrorInfo.name}</div> | |||||
| </div> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={40}> | |||||
| <Col span={24}> | |||||
| <div className={styles['model-deployment-info__basic__item']}> | |||||
| <div className={styles['label']}>描述:</div> | |||||
| <div className={styles['value']}>{mirrorInfo.description}</div> | |||||
| </div> | |||||
| </Col> | |||||
| </Row> | |||||
| </div> | |||||
| <div> | |||||
| <Tabs activeKey={activeTab} items={tabItems} onChange={hanleTabChange} /> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ModelDeploymentInfo; | |||||
| @@ -0,0 +1,21 @@ | |||||
| .model-deployment { | |||||
| height: 100%; | |||||
| &__content { | |||||
| height: calc(100% - 60px); | |||||
| margin-top: 10px; | |||||
| padding: 20px 30px 0; | |||||
| background-color: white; | |||||
| border-radius: 10px; | |||||
| &__filter { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: space-between; | |||||
| } | |||||
| &__table { | |||||
| height: calc(100% - 32px - 28px); | |||||
| margin-top: 28px; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,283 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-16 13:58:08 | |||||
| * @Description: 模型部署列表 | |||||
| */ | |||||
| import CommonTableCell from '@/components/CommonTableCell'; | |||||
| import DateTableCell from '@/components/DateTableCell'; | |||||
| import KFIcon from '@/components/KFIcon'; | |||||
| import PageTitle from '@/components/PageTitle'; | |||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { deleteMirrorReq, getMirrorListReq } from '@/services/mirror'; | |||||
| import themes from '@/styles/theme.less'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { modalConfirm } from '@/utils/ui'; | |||||
| import { useNavigate } from '@umijs/max'; | |||||
| import { | |||||
| App, | |||||
| Button, | |||||
| ConfigProvider, | |||||
| Input, | |||||
| Table, | |||||
| type TablePaginationConfig, | |||||
| type TableProps, | |||||
| } from 'antd'; | |||||
| import { type SearchProps } from 'antd/es/input'; | |||||
| import classNames from 'classnames'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import styles from './list.less'; | |||||
| export type MirrorData = { | |||||
| id: number; | |||||
| name: string; | |||||
| description: string; | |||||
| create_time: string; | |||||
| }; | |||||
| function ModelDeployment() { | |||||
| const navigate = useNavigate(); | |||||
| const { message } = App.useApp(); | |||||
| const [cacheState, setCacheState] = useCacheState(); | |||||
| const [searchText, setSearchText] = useState(cacheState?.searchText); | |||||
| const [inputText, setInputText] = useState(cacheState?.searchText); | |||||
| const [tableData, setTableData] = useState<MirrorData[]>([]); | |||||
| const [total, setTotal] = useState(0); | |||||
| const [pagination, setPagination] = useState<Required<TablePaginationConfig>>( | |||||
| cacheState?.pagination ?? { | |||||
| current: 1, | |||||
| pageSize: 10, | |||||
| }, | |||||
| ); | |||||
| useEffect(() => { | |||||
| getMirrorList(); | |||||
| }, [pagination, searchText]); | |||||
| // 获取镜像列表 | |||||
| const getMirrorList = async () => { | |||||
| const params: Record<string, any> = { | |||||
| page: pagination.current - 1, | |||||
| size: pagination.pageSize, | |||||
| name: searchText, | |||||
| image_type: 1, | |||||
| }; | |||||
| const [res] = await to(getMirrorListReq(params)); | |||||
| if (res && res.data) { | |||||
| const { content = [], totalElements = 0 } = res.data; | |||||
| setTableData(content); | |||||
| setTotal(totalElements); | |||||
| } | |||||
| }; | |||||
| // 删除镜像 | |||||
| const deleteMirror = async (id: number) => { | |||||
| const [res] = await to(deleteMirrorReq(id)); | |||||
| if (res) { | |||||
| message.success('删除成功'); | |||||
| // 如果是一页的唯一数据,删除时,请求第一页的数据 | |||||
| // 否则直接刷新这一页的数据 | |||||
| // 避免回到第一页 | |||||
| if (tableData.length > 1) { | |||||
| setPagination((prev) => ({ | |||||
| ...prev, | |||||
| current: 1, | |||||
| })); | |||||
| } else { | |||||
| getMirrorList(); | |||||
| } | |||||
| } | |||||
| }; | |||||
| // 搜索 | |||||
| const onSearch: SearchProps['onSearch'] = (value) => { | |||||
| setSearchText(value); | |||||
| }; | |||||
| // 查看详情 | |||||
| const toDetail = (record: MirrorData) => { | |||||
| navigate(`/modelDeployment/${record.id}`); | |||||
| setCacheState({ | |||||
| pagination, | |||||
| searchText, | |||||
| }); | |||||
| }; | |||||
| // 处理删除 | |||||
| const handleMirrorDelete = (record: MirrorData) => { | |||||
| modalConfirm({ | |||||
| title: '删除后,该镜像将不可恢复', | |||||
| content: '是否确认删除?', | |||||
| onOk: () => { | |||||
| deleteMirror(record.id); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| // 创建镜像 | |||||
| const createMirror = () => { | |||||
| navigate(`/modelDeployment/create`); | |||||
| setCacheState({ | |||||
| pagination, | |||||
| searchText, | |||||
| }); | |||||
| }; | |||||
| // 分页切换 | |||||
| const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => { | |||||
| if (action === 'paginate') { | |||||
| setPagination(pagination); | |||||
| } | |||||
| // console.log(pagination, filters, sorter, action); | |||||
| }; | |||||
| const columns: TableProps<MirrorData>['columns'] = [ | |||||
| { | |||||
| title: '序号', | |||||
| dataIndex: 'index', | |||||
| key: 'index', | |||||
| width: 100, | |||||
| align: 'center', | |||||
| render(text, record, index) { | |||||
| return <span>{(pagination.current - 1) * pagination.pageSize + index + 1}</span>; | |||||
| }, | |||||
| }, | |||||
| { | |||||
| title: '服务名称', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: '模型', | |||||
| dataIndex: 'version_count', | |||||
| key: 'version_count', | |||||
| width: '20%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: '状态', | |||||
| dataIndex: 'version_count', | |||||
| key: 'version_count', | |||||
| width: '10%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: '创建人', | |||||
| dataIndex: 'description', | |||||
| key: 'description', | |||||
| render: CommonTableCell(true), | |||||
| width: '20%', | |||||
| ellipsis: { showTitle: false }, | |||||
| }, | |||||
| { | |||||
| title: '更新时间', | |||||
| dataIndex: 'create_time', | |||||
| key: 'create_time', | |||||
| width: '20%', | |||||
| render: DateTableCell, | |||||
| }, | |||||
| { | |||||
| title: '操作', | |||||
| dataIndex: 'operation', | |||||
| width: 350, | |||||
| key: 'operation', | |||||
| render: (_: any, record: MirrorData) => ( | |||||
| <div> | |||||
| <Button | |||||
| type="link" | |||||
| size="small" | |||||
| key="edit" | |||||
| icon={<KFIcon type="icon-bianji" />} | |||||
| onClick={() => toDetail(record)} | |||||
| > | |||||
| 编辑 | |||||
| </Button> | |||||
| <Button | |||||
| type="link" | |||||
| size="small" | |||||
| key="run" | |||||
| icon={<KFIcon type="icon-yunhang" />} | |||||
| onClick={() => toDetail(record)} | |||||
| > | |||||
| 启动 | |||||
| </Button> | |||||
| <Button | |||||
| type="link" | |||||
| size="small" | |||||
| key="stop" | |||||
| icon={<KFIcon type="icon-tingzhi" />} | |||||
| onClick={() => toDetail(record)} | |||||
| > | |||||
| 停止 | |||||
| </Button> | |||||
| <ConfigProvider | |||||
| theme={{ | |||||
| token: { | |||||
| colorLink: themes['warningColor'], | |||||
| }, | |||||
| }} | |||||
| > | |||||
| <Button | |||||
| type="link" | |||||
| size="small" | |||||
| key="remove" | |||||
| icon={<KFIcon type="icon-shanchu" />} | |||||
| onClick={() => handleMirrorDelete(record)} | |||||
| > | |||||
| 删除 | |||||
| </Button> | |||||
| </ConfigProvider> | |||||
| </div> | |||||
| ), | |||||
| }, | |||||
| ]; | |||||
| return ( | |||||
| <div className={styles['model-deployment']}> | |||||
| <PageTitle title="模型列表"></PageTitle> | |||||
| <div className={styles['model-deployment__content']}> | |||||
| <div className={styles['model-deployment__filter']}> | |||||
| <Input.Search | |||||
| placeholder="按数据集名称筛选" | |||||
| allowClear | |||||
| onSearch={onSearch} | |||||
| onChange={(e) => setInputText(e.target.value)} | |||||
| style={{ width: 300 }} | |||||
| value={inputText} | |||||
| /> | |||||
| <Button | |||||
| style={{ marginLeft: '20px' }} | |||||
| type="default" | |||||
| onClick={createMirror} | |||||
| icon={<KFIcon type="icon-xinjian2" />} | |||||
| > | |||||
| 创建推理服务 | |||||
| </Button> | |||||
| </div> | |||||
| <div | |||||
| className={classNames( | |||||
| 'vertical-scroll-table', | |||||
| styles['model-deployment__content__table'], | |||||
| )} | |||||
| > | |||||
| <Table | |||||
| dataSource={tableData} | |||||
| columns={columns} | |||||
| scroll={{ y: 'calc(100% - 55px)' }} | |||||
| pagination={{ | |||||
| ...pagination, | |||||
| total: total, | |||||
| showSizeChanger: true, | |||||
| showQuickJumper: true, | |||||
| }} | |||||
| onChange={handleTableChange} | |||||
| rowKey="id" | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ModelDeployment; | |||||
| @@ -1,5 +1,5 @@ | |||||
| .quick-start { | .quick-start { | ||||
| width: calc(100% - 326px); | |||||
| width: calc(100% - 326px - 15px); | |||||
| padding: 20px 30px; | padding: 20px 30px; | ||||
| background-color: white; | background-color: white; | ||||
| border-radius: 4px; | border-radius: 4px; | ||||
| @@ -29,9 +29,10 @@ function QuickStart() { | |||||
| } | } | ||||
| }; | }; | ||||
| changeScale(); | |||||
| const debounceFunc = debounce(changeScale, 16); | const debounceFunc = debounce(changeScale, 16); | ||||
| window.addEventListener('resize', debounceFunc); | window.addEventListener('resize', debounceFunc); | ||||
| changeScale(); | |||||
| return () => { | return () => { | ||||
| window.removeEventListener('resize', debounceFunc); | window.removeEventListener('resize', debounceFunc); | ||||
| }; | }; | ||||
| @@ -1,10 +1,11 @@ | |||||
| /* | /* | ||||
| * @Author: 赵伟 | * @Author: 赵伟 | ||||
| * @Date: 2024-04-13 10:08:35 | * @Date: 2024-04-13 10:08:35 | ||||
| * @Description: | |||||
| * @Description: 以函数的方式打开 Modal | |||||
| */ | */ | ||||
| import { ConfigProvider, type ModalProps } from 'antd'; | import { ConfigProvider, type ModalProps } from 'antd'; | ||||
| import { globalConfig } from 'antd/es/config-provider'; | import { globalConfig } from 'antd/es/config-provider'; | ||||
| import zhCN from 'antd/locale/zh_CN'; | |||||
| import React, { useState } from 'react'; | import React, { useState } from 'react'; | ||||
| import { createRoot } from 'react-dom/client'; | import { createRoot } from 'react-dom/client'; | ||||
| @@ -59,7 +60,12 @@ export const openAntdModal = <T extends ModalProps>( | |||||
| ); | ); | ||||
| root.render( | root.render( | ||||
| <ConfigProvider prefixCls={rootPrefixCls} iconPrefixCls={iconPrefixCls} theme={theme}> | |||||
| <ConfigProvider | |||||
| prefixCls={rootPrefixCls} | |||||
| iconPrefixCls={iconPrefixCls} | |||||
| theme={theme} | |||||
| locale={zhCN} | |||||
| > | |||||
| {global.holderRender ? global.holderRender(dom) : dom} | {global.holderRender ? global.holderRender(dom) : dom} | ||||
| </ConfigProvider>, | </ConfigProvider>, | ||||
| ); | ); | ||||