| @@ -188,14 +188,23 @@ export default [ | |||
| ], | |||
| }, | |||
| { | |||
| name: 'modelDseployment', | |||
| path: '/modelDseployment', | |||
| name: 'modelDeployment', | |||
| path: '/modelDeployment', | |||
| routes: [ | |||
| { | |||
| name: '模型部署', | |||
| name: '模型列表', | |||
| 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 { | |||
| width: calc(100% - 326px); | |||
| width: calc(100% - 326px - 15px); | |||
| padding: 20px 30px; | |||
| background-color: white; | |||
| border-radius: 4px; | |||
| @@ -29,9 +29,10 @@ function QuickStart() { | |||
| } | |||
| }; | |||
| changeScale(); | |||
| const debounceFunc = debounce(changeScale, 16); | |||
| window.addEventListener('resize', debounceFunc); | |||
| changeScale(); | |||
| return () => { | |||
| window.removeEventListener('resize', debounceFunc); | |||
| }; | |||
| @@ -1,10 +1,11 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-13 10:08:35 | |||
| * @Description: | |||
| * @Description: 以函数的方式打开 Modal | |||
| */ | |||
| import { ConfigProvider, type ModalProps } from 'antd'; | |||
| import { globalConfig } from 'antd/es/config-provider'; | |||
| import zhCN from 'antd/locale/zh_CN'; | |||
| import React, { useState } from 'react'; | |||
| import { createRoot } from 'react-dom/client'; | |||
| @@ -59,7 +60,12 @@ export const openAntdModal = <T extends ModalProps>( | |||
| ); | |||
| root.render( | |||
| <ConfigProvider prefixCls={rootPrefixCls} iconPrefixCls={iconPrefixCls} theme={theme}> | |||
| <ConfigProvider | |||
| prefixCls={rootPrefixCls} | |||
| iconPrefixCls={iconPrefixCls} | |||
| theme={theme} | |||
| locale={zhCN} | |||
| > | |||
| {global.holderRender ? global.holderRender(dom) : dom} | |||
| </ConfigProvider>, | |||
| ); | |||