|
- /*
- * @Author: 赵伟
- * @Date: 2024-04-16 13:58:08
- * @Description: 创建服务版本
- */
- import CodeSelect from '@/components/CodeSelect';
- import PageTitle from '@/components/PageTitle';
- import ResourceSelect, {
- ResourceSelectorType,
- requiredValidator,
- type ParameterInputObject,
- } from '@/components/ResourceSelect';
- import SubAreaTitle from '@/components/SubAreaTitle';
- import { useComputingResource } from '@/hooks/resource';
- import {
- createServiceVersionReq,
- getServiceInfoReq,
- updateServiceVersionReq,
- } from '@/services/modelDeployment';
- import { changePropertyName } from '@/utils';
- import { to } from '@/utils/promise';
- import SessionStorage from '@/utils/sessionStorage';
- import { modalConfirm } from '@/utils/ui';
- import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons';
- import { useNavigate, useParams } from '@umijs/max';
- import { App, Button, Col, Flex, Form, Input, InputNumber, Row, Select } from 'antd';
- import { omit, pick } from 'lodash';
- import { useEffect, useState } from 'react';
- import { CreateServiceVersionFrom, ServiceOperationType, ServiceVersionData } from '../types';
- import styles from './index.less';
-
- // 表单数据
- export type FormData = {
- service_name: string; // 服务名称
- version: string; // 服务版本
- description: string; // 描述
- model: ParameterInputObject; // 模型
- image: ParameterInputObject; // 镜像
- code_config: ParameterInputObject; // 代码
- resource: string; // 资源规格
- replicas: string; // 副本数量
- mount_path: string; // 模型路径
- env_variables: { key: string; value: string }[]; // 环境变量
- };
-
- type ServiceVersionCache = ServiceVersionData & {
- operationType: ServiceOperationType;
- lastPage: CreateServiceVersionFrom;
- };
-
- function CreateServiceVersion() {
- const navigate = useNavigate();
- const [form] = Form.useForm();
- const [resourceStandardList, filterResourceStandard] = useComputingResource();
- const [operationType, setOperationType] = useState(ServiceOperationType.Create);
- const [lastPage, setLastPage] = useState(CreateServiceVersionFrom.ServiceInfo);
- const { message } = App.useApp();
- // const [serviceInfo, setServiceInfo] = useState<ServiceData | undefined>(undefined);
- const [versionInfo, setVersionInfo] = useState<ServiceVersionData | undefined>(undefined);
- const params = useParams();
- const serviceId = params.serviceId;
-
- useEffect(() => {
- const res: ServiceVersionCache | undefined = SessionStorage.getItem(
- SessionStorage.serviceVersionInfoKey,
- true,
- );
- if (res) {
- setOperationType(res.operationType);
- setLastPage(res.lastPage);
- setVersionInfo(res);
- let model, codeConfig, envVariables;
- if (res.model && typeof res.model === 'object') {
- model = changePropertyName(res.model, { show_value: 'showValue' });
- // 接口返回是数据没有 value 值,但是 form 需要 value
- model.value = model.showValue;
- }
- if (res.code_config && typeof res.code_config === 'object') {
- codeConfig = changePropertyName(res.code_config, { show_value: 'showValue' });
- // 接口返回是数据没有 value 值,但是 form 需要 value
- codeConfig.value = codeConfig.showValue;
- }
- if (res.env_variables && typeof res.env_variables === 'object') {
- envVariables = Object.entries(res.env_variables).map(([key, value]) => ({
- key,
- value,
- }));
- }
-
- const formData = {
- ...omit(res, 'model', 'code_config', 'env_variables'),
- model: model,
- code_config: codeConfig,
- env_variables: envVariables,
- };
- form.setFieldsValue(formData);
- }
- return () => {
- SessionStorage.removeItem(SessionStorage.serviceVersionInfoKey);
- };
- }, []);
-
- useEffect(() => {
- getServiceInfo();
- }, []);
-
- // 获取服务详情
- const getServiceInfo = async () => {
- const [res] = await to(getServiceInfoReq(serviceId));
- if (res && res.data) {
- // setServiceInfo(res.data);
- form.setFieldsValue({
- service_name: res.data.service_name,
- });
- }
- };
-
- // 创建版本
- const createServiceVersion = async (formData: FormData) => {
- const envList = formData['env_variables'];
- const image = formData['image'];
- const model = formData['model'];
- const codeConfig = formData['code_config'];
- const envVariables = envList?.reduce((acc, cur) => {
- acc[cur.key] = cur.value;
- return acc;
- }, {} as Record<string, string>);
-
- // 根据后台要求,修改表单数据
- const object = {
- ...omit(formData, ['replicas', 'env_variables', 'image', 'model', 'code_config']),
- replicas: Number(formData.replicas),
- env_variables: envVariables,
- image: image.value,
- model: changePropertyName(
- pick(model, ['id', 'name', 'version', 'path', 'identifier', 'owner', 'showValue']),
- { showValue: 'show_value' },
- ),
- code_config: codeConfig
- ? changePropertyName(pick(codeConfig, ['code_path', 'branch', 'showValue']), {
- showValue: 'show_value',
- })
- : undefined,
- service_id: serviceId,
- };
-
- const params =
- operationType === ServiceOperationType.Create
- ? {
- ...object,
- deploy_type: 'web',
- }
- : {
- id: versionInfo?.id,
- rerun: operationType === ServiceOperationType.Restart ? true : false,
- deployment_name: versionInfo?.deployment_name,
- ...object,
- };
-
- const request =
- operationType === ServiceOperationType.Create
- ? createServiceVersionReq
- : updateServiceVersionReq;
-
- const [res] = await to(request(params));
- if (res) {
- message.success('操作成功');
- if (lastPage === CreateServiceVersionFrom.ServiceInfo) {
- navigate(-1);
- } else {
- navigate(`/modelDeployment/serviceInfo/${serviceId}`, { replace: true });
- }
- }
- };
-
- // 提交
- const handleSubmit = (values: FormData) => {
- createServiceVersion(values);
- };
-
- // 取消
- const cancel = () => {
- navigate(-1);
- };
-
- const disabled = operationType !== ServiceOperationType.Create;
- let buttonText = '新建';
- let title = '新增服务版本';
- if (operationType === ServiceOperationType.Update) {
- title = '更新服务版本';
- buttonText = '更新';
- } else if (operationType === ServiceOperationType.Restart) {
- title = '重启服务版本';
- buttonText = '重启';
- }
-
- return (
- <div className={styles['create-service-version']}>
- <PageTitle title={title}></PageTitle>
- <div className={styles['create-service-version__content']}>
- <div>
- <Form
- name="create-service-version"
- labelCol={{ flex: '100px' }}
- labelAlign="left"
- form={form}
- onFinish={handleSubmit}
- size="large"
- autoComplete="off"
- >
- <SubAreaTitle
- title="基本信息"
- image={require('@/assets/img/mirror-basic.png')}
- style={{ marginBottom: '26px' }}
- ></SubAreaTitle>
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item
- label="服务名称"
- name="service_name"
- rules={[
- {
- required: true,
- message: '请输入服务名称',
- },
- ]}
- >
- <Input
- placeholder="请输入服务名称"
- maxLength={30}
- disabled
- showCount
- allowClear
- />
- </Form.Item>
- </Col>
- </Row>
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item
- label="服务版本"
- name="version"
- rules={[
- {
- required: true,
- message: '请输入服务版本',
- },
- {
- pattern: /^[a-zA-Z0-9._-]+$/,
- message: '版本只支持字母、数字、点、下划线、中横线',
- },
- ]}
- >
- <Input
- placeholder="请输入服务版本"
- maxLength={30}
- disabled={disabled}
- showCount
- allowClear
- />
- </Form.Item>
- </Col>
- </Row>
- <Row gutter={8}>
- <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/model-deployment.png')}
- style={{ marginTop: '20px', marginBottom: '24px' }}
- ></SubAreaTitle>
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item
- label="选择模型"
- name="model"
- rules={[
- {
- validator: requiredValidator,
- message: '请选择模型',
- },
- ]}
- required
- >
- <ResourceSelect
- type={ResourceSelectorType.Model}
- placeholder="请选择模型"
- disabled={disabled}
- canInput={false}
- size="large"
- />
- </Form.Item>
- </Col>
- </Row>
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item
- label="选择镜像"
- name="image"
- rules={[
- {
- validator: requiredValidator,
- message: '请选择镜像',
- },
- ]}
- required
- >
- <ResourceSelect
- type={ResourceSelectorType.Mirror}
- placeholder="请选择镜像"
- canInput={false}
- size="large"
- disabled={disabled}
- />
- </Form.Item>
- </Col>
- </Row>
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item label="代码配置" name="code_config">
- <CodeSelect
- placeholder="请选择代码配置"
- canInput={false}
- size="large"
- disabled={disabled}
- />
- </Form.Item>
- </Col>
- </Row>
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item
- label="资源规格"
- name="resource"
- rules={[
- {
- required: true,
- message: '请选择资源规格',
- },
- ]}
- >
- <Select
- showSearch
- placeholder="请选择资源规格"
- filterOption={filterResourceStandard}
- options={resourceStandardList}
- fieldNames={{
- label: 'description',
- value: 'standard',
- }}
- />
- </Form.Item>
- </Col>
- </Row>
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item
- label="副本数量"
- name="replicas"
- rules={[
- {
- required: true,
- message: '请输入副本数量',
- },
- {
- pattern: /^[1-9]\d*$/,
- message: '副本数量必须是正整数',
- },
- ]}
- >
- <InputNumber
- style={{ width: '100%' }}
- placeholder="请输入副本数量"
- min={1}
- precision={0}
- />
- </Form.Item>
- </Col>
- </Row>
-
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item
- label="挂载路径"
- name="mount_path"
- rules={[
- {
- required: true,
- message: '请输入模型挂载路径',
- },
- {
- pattern: /^\/[a-zA-Z0-9._/-]+$/,
- message:
- '请输入正确的挂载路径,以斜杠(/)开头,只支持字母、数字、点(.)、下划线(_)、中横线(-)、斜杠(/)',
- },
- ]}
- >
- <Input
- placeholder="请输入模型挂载路径"
- disabled={disabled}
- maxLength={64}
- showCount
- allowClear
- />
- </Form.Item>
- </Col>
- </Row>
-
- <Row gutter={8}>
- <Col span={10}>
- <Form.Item label="环境变量">
- <Form.List name="env_variables">
- {(fields, { add, remove }) => (
- <>
- {fields.map(({ key, name, ...restField }, index) => (
- <Flex key={key} align="center" gap="0 8px">
- <Form.Item
- {...restField}
- name={[name, 'key']}
- style={{ flex: 1 }}
- rules={[
- { required: true, message: '请输入变量名' },
- {
- pattern: /^[a-zA-Z_][a-zA-Z0-9_-]*$/,
- message:
- '变量名只支持字母、数字、下划线、中横线且开头必须是字母或下划线',
- },
- ]}
- >
- <Input placeholder="请输入变量名" disabled={disabled} />
- </Form.Item>
- <span style={{ marginBottom: '24px' }}>=</span>
- <Form.Item
- {...restField}
- name={[name, 'value']}
- style={{ flex: 1 }}
- rules={[{ required: true, message: '请输入变量值' }]}
- >
- <Input placeholder="请输入变量值" disabled={disabled} />
- </Form.Item>
- <Flex
- style={{
- width: '76px',
- marginLeft: '10px',
- height: '46px',
- marginBottom: '24px',
- }}
- align="center"
- >
- <Button
- style={{
- marginRight: '3px',
- }}
- shape="circle"
- size="middle"
- type="text"
- icon={<MinusCircleOutlined />}
- disabled={disabled}
- onClick={() => {
- modalConfirm({
- title: '确定要删除该环境变量吗?',
- onOk: () => {
- remove(name);
- },
- });
- }}
- ></Button>
- {index === fields.length - 1 && (
- <Button
- shape="circle"
- size="middle"
- type="text"
- disabled={disabled}
- icon={<PlusCircleOutlined />}
- onClick={() => add()}
- ></Button>
- )}
- </Flex>
- </Flex>
- ))}
- {fields.length === 0 && (
- <Form.Item className={styles['create-service-version__content__env']}>
- <Button
- className={styles['create-service-version__content__env__add-btn']}
- color="primary"
- variant="dashed"
- disabled={disabled}
- icon={<PlusOutlined />}
- block
- onClick={() => add()}
- >
- 环境变量
- </Button>
- </Form.Item>
- )}
- </>
- )}
- </Form.List>
- </Form.Item>
- </Col>
- </Row>
-
- <Form.Item wrapperCol={{ offset: 0, span: 16 }}>
- <Button type="primary" htmlType="submit">
- {buttonText}
- </Button>
- <Button
- type="default"
- htmlType="button"
- onClick={cancel}
- style={{ marginLeft: '20px' }}
- >
- 取消
- </Button>
- </Form.Item>
- </Form>
- </div>
- </div>
- </div>
- );
- }
-
- export default CreateServiceVersion;
|