feat: 模型管理初步联调完成

develop2
donghao 11 months ago
parent cff535675c
commit c427830ef6

@ -189,7 +189,7 @@
.anticon.anticon-down.ant-dropdown-trigger.ant-transfer-list-header-dropdown { .anticon.anticon-down.ant-dropdown-trigger.ant-transfer-list-header-dropdown {
display: none; display: none;
} }
:where(.css-dev-only-do-not-override-1y19ift).ant-pro-steps-form-container { .ant-pro-steps-form-container {
min-width: 100%; min-width: 100%;
} }
.ant-transfer { .ant-transfer {

@ -220,7 +220,7 @@
.anticon.anticon-down.ant-dropdown-trigger.ant-transfer-list-header-dropdown { .anticon.anticon-down.ant-dropdown-trigger.ant-transfer-list-header-dropdown {
display: none; display: none;
} }
:where(.css-dev-only-do-not-override-1y19ift).ant-pro-steps-form-container { .ant-pro-steps-form-container {
min-width: 100%; min-width: 100%;
} }

@ -187,7 +187,7 @@ const BaseTree: React.FC<BaseTreeProps> = (props) => {
useEffect(() => { useEffect(() => {
setCurrTreeData(props.treeData); setCurrTreeData(props.treeData);
// TODO 后续改从业务层获取 默认选项 // 从业务层获取 默认选项
setCurrSeleted([props?.treeData[0]?.id]); setCurrSeleted([props?.treeData[0]?.id]);
console.log(props.treeData, 'props_treeData'); console.log(props.treeData, 'props_treeData');
}, [props.treeData]); }, [props.treeData]);

@ -0,0 +1,11 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-06-04 15:13:34
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-06-04 15:25:13
* @FilePath: \general-ai-platform-web\src\components\UploadFile\index.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import proFormUploadDraggerToken from './src/formUploadDraggerToken';
export const FormUploadDraggerToken = proFormUploadDraggerToken;

@ -0,0 +1,127 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-06-04 15:14:27
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-06-05 15:37:03
* @FilePath: \general-ai-platform-web\src\components\UploadFile\src\FormUploadDraggerToken.tsx
* @Description:
* // TODO 需要支持多文件上传
*/
import { apiFileDelete, apiFileUpload } from '@/services/business/file';
import { useIntl } from '@umijs/max';
import { RcFile, UploadChangeParam, UploadFile, message } from 'antd';
// import localCacha from '@/utils/localCacha';
import { isSuccessApi } from '@/utils/forApi';
import { ProFormUploadDragger } from '@ant-design/pro-components';
import React, { useState } from 'react';
import { FormUploadDraggerTokenProps } from '../typing';
// import { ProFormUploadDragger, type ProFormUploadDraggerProps } from '@ant-design/pro-components';
// import type { RcFile, UploadChangeParam, UploadFile } from 'antd/es/upload';
const FormUploadDraggerToken: React.FC<FormUploadDraggerTokenProps> = (props) => {
const intl = useIntl();
const { onChange } = props;
const [fileList, setFileList] = useState([
{ status: 'done', url: '/file/' + props.fileValues?.id, ...props.fileValues },
]);
/**
*
*/
const onChangeFile = (info: UploadChangeParam<UploadFile<R<string>>>) => {
// 当上传成功后才开始对数据进行校验
if (info.file.status === 'done') {
const res = info.file.response;
if (res?.code !== 200) {
info.file.status = 'error';
// 查看 antd 源码,可自定义错误信息 官方文档并未给出相关内容 message = file.error?.statusText || file.error?.message || locale.uploadError;
info.file.error = { statusText: res?.msg };
}
}
onChange?.(info);
};
/**
*
*/
const beforeUploadFile = async (file: RcFile) => {
const formData = new FormData();
formData.append('file', file);
const resp = await apiFileUpload(formData);
if (isSuccessApi(resp)) {
setFileList([
{
uid: resp?.data?.result,
name: file?.name,
status: 'done',
url: '/file/' + resp?.data?.result,
},
]);
props.afterUploadFile({ resp, file, fileList });
}
console.log(resp, 'apiFileUpload_resp');
return false; // 阻止自动上传
// if (!accept) return true;
// const suffix = file.name.substring(file.name.lastIndexOf('.'));
// const supportFile = accept.replace(/\s*/g, '').split(','); // 去除 accept 中的空格
// if (supportFile.length > 0) {
// const validate = supportFile.includes(suffix);
// if (!validate) {
// message.error(`支持上传的文件格式:${accept}`);
// return Upload.LIST_IGNORE;
// }
// return true;
// }
};
/**
*/
const onRemoveFile = async (file: UploadFile<R<string>>) => {
console.log(file, 'onRemoveFile_file');
const resp = await apiFileDelete({ file_md5: file?.uid });
console.log(resp, 'apiFileDelete_resp');
props.afterRemoveFile(resp, file);
if (isSuccessApi(resp)) {
message.success(`${file.name} 删除成功`);
} else {
message.error(
resp?.meta?.message ||
intl.formatMessage({ id: 'common.action.failure', defaultMessage: '$$$' }),
);
}
// props.beforeRemoveFile(file);
// rc- 是通过 antd 初次上传的自带uid没有 rc-证明是回显值值是文件id或图片url地址
// 若上传失败,删除时,直接清除页面文件,不走接口
// if (file.uid.indexOf('rc-') !== -1 && file.response?.code !== 200) return true;
// let fileId = '';
// // response 存在,则证明该文件是未提交的数据(上传,然后删除)
// if (file.response?.data) fileId = file.response.data;
// else fileId = file.uid;
// const { code } = await apiFileDelete({ file_md5: fileId });
// requestDelFile?.(code === 200);
// return code === 200;
};
// TODO 组装初始值
return (
<>
<ProFormUploadDragger
fileList={fileList}
onChange={onChangeFile}
beforeUpload={() => false} // 阻止自动上
{...props.proFieldProps}
fieldProps={{
className: 'gn_proFormUploadDragger_formItem',
name: 'files', // 这里可以指定和后端对接的 filename
onRemove: onRemoveFile,
beforeUpload: beforeUploadFile,
}}
/>
<div style={{ color: '#666666', marginTop: '-8px' }}>{props.nextText}</div>
</>
);
};
export default FormUploadDraggerToken;

@ -0,0 +1,15 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-26 11:09:49
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-06-05 14:50:40
* @FilePath: \general-ai-platform-web\src\components\UploadFile\typing.ts
* @Description:
*/
export interface FormUploadDraggerTokenProps extends ProFormUploadDraggerProps {
afterUploadFile?: (data: any) => void; // 上传完成后
afterRemoveFile?: (file: UploadFile<R<string>>) => void; // 删除开始
requestDelFile?: (status: boolean) => void;
nextText?: string | React.ReactNode;
}

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-10 17:21:34 * @Date: 2024-04-10 17:21:34
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-31 13:40:48 * @LastEditTime: 2024-06-06 15:07:50
* @FilePath: \general-ai-manage\src\locales\zh-CN\model.ts * @FilePath: \general-ai-manage\src\locales\zh-CN\model.ts
* @Description: * @Description:
*/ */
@ -41,7 +41,9 @@ export const model_runtimeLib: { [key: string]: string } = {
'model_runtimeLib.table.list.createTime': '创建时间', 'model_runtimeLib.table.list.createTime': '创建时间',
'model_runtimeLib.table.list.updateTime': '更新时间', 'model_runtimeLib.table.list.updateTime': '更新时间',
'model_runtimeLib.table.list.name': '运行库名称', 'model_runtimeLib.table.list.name': '运行库名称',
'model_runtimeLib.list.table.createForm.add': '新建运行库', 'model_runtimeLib.table.list.path': '文件地址',
'model_runtimeLib.list.table.form.action.add': '新建运行库',
'model_runtimeLib.list.table.form.action.edit': '编辑运行库',
'model_runtimeLib.list.table.form.name': '运行库名称', 'model_runtimeLib.list.table.form.name': '运行库名称',
'model_runtimeLib.list.table.form.rule.required.name': '请填写运行库名称', 'model_runtimeLib.list.table.form.rule.required.name': '请填写运行库名称',
'model_runtimeLib.list.table.form.IP': 'IP地址', 'model_runtimeLib.list.table.form.IP': 'IP地址',

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-22 15:23:36 * @Date: 2024-04-22 15:23:36
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-20 11:00:25 * @LastEditTime: 2024-06-06 15:46:51
* @FilePath: \general-ai-platform-web\src\pages\Node\BusinessModel\index.tsx * @FilePath: \general-ai-platform-web\src\pages\Node\BusinessModel\index.tsx
* @Description: * @Description:
* @ * @
@ -18,7 +18,9 @@
import TableActionCard from '@/components/TableActionCard'; import TableActionCard from '@/components/TableActionCard';
import IsDelete from '@/components/TableActionCard/isDelete'; import IsDelete from '@/components/TableActionCard/isDelete';
import { getBusinessModelList } from '@/services/testApi/businessModel'; import { apiModelDeploymentList } from '@/services/business/model';
import { isSuccessApi } from '@/utils/forApi';
import { ExclamationCircleFilled } from '@ant-design/icons'; import { ExclamationCircleFilled } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components'; import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProCard, ProTable } from '@ant-design/pro-components'; import { ProCard, ProTable } from '@ant-design/pro-components';
@ -63,7 +65,7 @@ const BusinessModel: React.FC = () => {
const columns: ProColumns<Record<string, any>>[] = [ const columns: ProColumns<Record<string, any>>[] = [
{ {
title: <FormattedMessage id="business_model.table.list.name" defaultMessage="名称" />, title: <FormattedMessage id="business_model.table.list.name" defaultMessage="名称" />,
dataIndex: 'name', dataIndex: 'model_name',
hideInSearch: true, hideInSearch: true,
key: 'fixedName', key: 'fixedName',
fixed: 'left', fixed: 'left',
@ -74,7 +76,7 @@ const BusinessModel: React.FC = () => {
title: ( title: (
<FormattedMessage id="business_model.table.list.createTime" defaultMessage="创建时间" /> <FormattedMessage id="business_model.table.list.createTime" defaultMessage="创建时间" />
), ),
dataIndex: 'createTime', dataIndex: 'create_time',
hideInSearch: true, hideInSearch: true,
valueType: 'dateTime', valueType: 'dateTime',
}, },
@ -185,7 +187,10 @@ const BusinessModel: React.FC = () => {
pageNo: current, pageNo: current,
...rest, ...rest,
}; };
let resp = await getBusinessModelList({ ...reqParams }); let resp = await apiModelDeploymentList({ ...reqParams });
if (!isSuccessApi(resp)) {
return { data: [], success: true };
}
console.log(resp, 'getModelVersionList_resp'); console.log(resp, 'getModelVersionList_resp');
return { return {
data: resp.data?.data, data: resp.data?.data,

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-22 15:23:36 * @Date: 2024-04-22 15:23:36
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-30 15:25:27 * @LastEditTime: 2024-06-04 16:33:16
* @FilePath: \general-ai-platform-web\src\pages\Business\DeviceGroup\components\deviceList.tsx * @FilePath: \general-ai-platform-web\src\pages\Business\DeviceGroup\components\deviceList.tsx
* @Description: deviceGroupdg * @Description: deviceGroupdg
* @ * @

@ -1,4 +1,6 @@
import { apiFileDelete, apiFileUpload } from '@/services/business/file'; import { FormUploadDraggerToken } from '@/components/UploadFile';
import { apiModelVersionAdd } from '@/services/business/model';
import { isSuccessApi } from '@/utils/forApi';
import { import {
ProForm, ProForm,
ProFormInstance, ProFormInstance,
@ -54,16 +56,14 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
const [configFileList, setConfigFileList] = useState<UploadFile<any>[]>([]); const [configFileList, setConfigFileList] = useState<UploadFile<any>[]>([]);
// const [form] = Form.useForm<Record<string, any>>(); // const [form] = Form.useForm<Record<string, any>>();
const [currFormData, setCurrFormData] = useState<Record<string, any>>({});
const [current, setCurrent] = useState(0); const [current, setCurrent] = useState(0);
const formBaseRef = useRef<ProFormInstance>(); const formBaseRef = useRef<ProFormInstance>();
const formProjectFileRef = useRef<ProFormInstance>();
// const [filePath, setFilePath] = useState(''); // const [filePath, setFilePath] = useState('');
const handleFileChange = ({ file }: { file: UploadFile }) => { const handleConfigFileChange = ({ file }: { file: UploadFile }) => {
console.log(configFileList, 'handleFileChange_configFileList'); console.log(configFileList, 'handleFileChange_configFileList');
let curFile: any; let curFile: any;
switch (file.status) { switch (file.status) {
case 'uploading': case 'uploading':
case 'done': case 'done':
@ -74,29 +74,44 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
curFile = []; curFile = [];
break; break;
} }
setConfigFileList([...curFile]); setConfigFileList([...curFile]);
}; };
// test 测试数据 // test 测试数据
useEffect(() => { useEffect(() => {
// if (formBaseRef) { // 初始化赋值
formBaseRef.current?.setFieldsValue({ // setCurrFormData({
version: '0.0.91', // 设备分类的suid // version: '1.0.91', // 设备分类的suid 59aa3d0c3162322e190942a9cc9add10
comment: // comment:
'测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注', // '测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注测试一下备注',
}); // });
// }
}, [props.createModalOpen]); }, [props.createModalOpen]);
return ( return (
<StepsForm<{ <StepsForm<{
name: string; name: string;
}> }>
onFinish={async (values) => { onFinish={async (values) => {
console.log('All form values:', values, { model_id: props.modelInfo?.model_id }); console.log('All form values:', values, {});
// 在这里处理提交数据例如调用API // 在这里处理提交数据例如调用API
message.success('提交成功'); let resp = await apiModelVersionAdd({
...values,
model_id: props.modelInfo?.model_id,
config_str: JSON.stringify(values.config_str_arr),
model_file: currFormData?.model_file,
config_file: '',
});
if (isSuccessApi(resp)) {
message.success(
intl.formatMessage({ id: 'common.action.success', defaultMessage: '$$$' }),
);
props.reload();
props.handleModal();
} else {
message.error(
resp?.meta?.message ||
intl.formatMessage({ id: 'common.action.failure', defaultMessage: '$$$' }),
);
}
return true;
}} }}
stepsProps={proFormStepsFormProps.stepsProps} stepsProps={proFormStepsFormProps.stepsProps}
current={current} current={current}
@ -165,17 +180,6 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
initialValue={props.modelInfo?.name} initialValue={props.modelInfo?.name}
debounceTime={1000} debounceTime={1000}
disabled={true} disabled={true}
// request={async (keyWord) => {
// const resp = await postAlgorithmModelGetAlgorithmModelFkSelect({
// keyword: keyWord?.keyWords || '',
// });
// return resp.data.list.map((v: any) => {
// return {
// label: v.name,
// value: v.id,
// };
// });
// }}
/> />
<ProFormText <ProFormText
width={proFormSmallItemStyleProps.width} width={proFormSmallItemStyleProps.width}
@ -183,6 +187,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
prefix: <span>V</span>, prefix: <span>V</span>,
}} }}
name="version" name="version"
initialValue={currFormData?.version}
label={<FormattedMessage id="model_detail.version.form.name" defaultMessage="版本号" />} label={<FormattedMessage id="model_detail.version.form.name" defaultMessage="版本号" />}
placeholder={`${intl.formatMessage({ placeholder={`${intl.formatMessage({
id: 'common.please_input', id: 'common.please_input',
@ -207,6 +212,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
<ProFormTextArea <ProFormTextArea
width={proFormSmallItemStyleProps.width} width={proFormSmallItemStyleProps.width}
name="comment" name="comment"
initialValue={currFormData?.comment}
label={<FormattedMessage id="model_detail.version.form.remark" defaultMessage="备注" />} label={<FormattedMessage id="model_detail.version.form.remark" defaultMessage="备注" />}
placeholder={`${intl.formatMessage({ placeholder={`${intl.formatMessage({
id: 'common.please_input', id: 'common.please_input',
@ -224,7 +230,6 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
<StepsForm.StepForm<{ <StepsForm.StepForm<{
project_file: string; project_file: string;
}> }>
formRef={formProjectFileRef}
className="gn_form" className="gn_form"
name="project_file" name="project_file"
title={ title={
@ -251,78 +256,32 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
return true; return true;
}} }}
> >
<ProFormUploadDragger <FormUploadDraggerToken
label={<span className="font-bold"></span>} proFieldProps={{
title={ label: <span className="font-bold"></span>,
<div className="gn_uploadfile_title py-[16px] text1 text-center"> title: (
<span></span> <div className="gn_uploadfile_title py-[16px] text1 text-center">
<a></a> <span></span>
</div> <a></a>
} </div>
description="" ),
icon={<i className="iconfont icon-shangchuanwenjian text_primary text-[32px]"></i>} description: '',
name="model_file_name" icon: <i className="iconfont icon-shangchuanwenjian text_primary text-[32px]"></i>,
// action={fileApiActionUrl} name: 'model_file_arr',
onRemove={async (file) => { max: 1,
// 这里可以添加删除文件的接口调用逻辑
// await deleteFileFromServer(file);
// setFileList((prevList) => {
// const newList = prevList.filter((item) => item.uid !== file.uid);
// return newList;
// });
const currFileId = formProjectFileRef?.current?.getFieldValue('model_file');
const resp = await apiFileDelete({ file_md5: currFileId });
console.log(resp, 'apiFileDelete_resp', file);
message.success('文件已删除');
return true; // 确保文件从文件列表中移除
}} }}
onChange={async ({ file, fileList }) => { afterUploadFile={({ resp }) => {
// 检查文件状态 setCurrFormData((data) => {
return { ...data, model_file: resp?.data?.result };
console.log( });
file,
fileList,
'onChange_ProjectFile',
formProjectFileRef?.current?.getFieldValue('model_file'),
);
if (file.status === 'removed') {
// 删除文件
// setProjectFileList(fileList.filter((item) => item.uid !== file.uid));
// const currFileId = formProjectFileRef?.current?.getFieldValue('model_file')
// const resp = await apiFileDelete({file_md5: currFileId})
// console.log(resp,'apiFileDelete_resp')
// message.success(`${file.name} 删除成功`);
} else {
// 处理上传
// formProjectFileRef?.current?.setFieldValue('model_file_name', [file]);
// handleUpload(file);
}
// setProjectFileList(fileList);
}} }}
max={1} afterRemoveFile={() => {
fieldProps={{ setCurrFormData((data) => {
className: 'gn_proFormUploadDragger_formItem', return { ...data, model_file: '' };
name: 'file', });
// beforeUpload: beforeUploadFile,
data: { path: `models/${Date.now()}` },
headers: {
// 'X-CSRFToken': cookie.load('csrftoken'),
// Authorization: `Bearer ${localStorage.getItem('access') || ''}`,
},
beforeUpload: async (file) => {
const formData = new FormData();
formData.append('file', file);
const resp = await apiFileUpload(formData);
formProjectFileRef?.current?.setFieldValue('model_file', resp?.data?.result);
console.log(resp, 'apiFileUpload_resp');
// form.setFieldValue('logo', base64);
return false; // 阻止自动上传
},
}} }}
nextText="请上传格式为zip.tar.gz的模型文件"
/> />
<div style={{ color: '#666666', marginTop: '-8px' }}>zip.tar.gz</div>
</StepsForm.StepForm> </StepsForm.StepForm>
{/* 参数配置 */} {/* 参数配置 */}
<StepsForm.StepForm<{ <StepsForm.StepForm<{
@ -347,22 +306,6 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
let formData1 = formBaseRef.current?.getFieldsValue(); let formData1 = formBaseRef.current?.getFieldsValue();
if (formData1?.model_name) { if (formData1?.model_name) {
await waitTime(500); await waitTime(500);
// formData1.modelConfig = { params: values?.params || [] };
// if (filePath) {
// formData1.path = filePath;
// }
// postModelVersionCreateModelVersion(formData)
// .then(() => {
// message.success(
// intl.formatMessage({ id: 'common.action.success', defaultMessage: '$$$' }),
// );
// props.handleModal();
// props.reload();
// })
// .catch(() => {
// message.error(intl.formatMessage({ id: 'common.action.failure', defaultMessage: '$$$' }));
// return false;
// });
} }
return true; return true;
}} }}
@ -379,11 +322,11 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
} }
description="" description=""
icon={<i className="iconfont icon-shangchuanwenjian text_primary text-[32px]"></i>} icon={<i className="iconfont icon-shangchuanwenjian text_primary text-[32px]"></i>}
name="config_file" name="config_file_name"
action={fileApiActionUrl} action={fileApiActionUrl}
fieldProps={{ fieldProps={{
className: 'gn_proFormUploadDragger_formItem', className: 'gn_proFormUploadDragger_formItem',
onChange: handleFileChange, onChange: handleConfigFileChange,
onRemove: () => { onRemove: () => {
let index_ids = actionFormListRef.current?.getList()?.map((v, i) => { let index_ids = actionFormListRef.current?.getList()?.map((v, i) => {
return i; return i;
@ -429,7 +372,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
}} }}
/> />
<div style={{ color: '#666666', marginTop: '-8px', marginBottom: '16px' }}> <div style={{ color: '#666666', marginTop: '-8px', marginBottom: '16px' }}>
zip.tar.gz .json.yaml.yml
</div> </div>
{/* // TODO label字重与上面统一, 操作按钮需要与输入框对齐 */} {/* // TODO label字重与上面统一, 操作按钮需要与输入框对齐 */}

@ -0,0 +1,447 @@
// TODO 上传文件限制为1时还可以上传
import { FormUploadDraggerToken } from '@/components/UploadFile';
import { apiModelVersionEdit } from '@/services/business/model';
import { isSuccessApi } from '@/utils/forApi';
import {
ProForm,
ProFormInstance,
ProFormList,
ProFormText,
ProFormTextArea,
ProFormUploadDragger,
StepsForm,
} from '@ant-design/pro-components';
import { FormListActionType } from '@ant-design/pro-form/lib';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Modal, UploadFile, message } from 'antd';
import yaml from 'js-yaml';
import React, { useEffect, useRef, useState } from 'react';
import {
proFormItemStyleProps,
proFormSmallItemStyleProps,
proFormSmallModelWidth,
proFormStepsFormProps,
} from '../../../../../config/defaultForm';
// import {beforeUploadFile} from "@/utils/common";
// @ts-ignore
import { isValidJson } from '@/utils/is';
import { fileApiActionUrl } from '../../../../../config/defaultApi';
export type UpdateFormProps = {
updateModalOpen: boolean;
values: Record<string, any>;
handleModal: () => void;
modelInfo: Record<string, any>;
reload: any;
};
const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
// interface ProjectConfig {
// params: Array<object>;
// }
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
const actionFormListRef = useRef<
FormListActionType<{
name: string;
}>
>();
const intl = useIntl();
const [dataFormList] = useState<any>([]);
const dataFormListRef = useRef(dataFormList);
// const [projectFileList, setProjectFileList] = useState<UploadFile<any>[]>([]);
const [configFileList, setConfigFileList] = useState<UploadFile<any>[]>([]);
// const [form] = Form.useForm<Record<string, any>>();
const [currFormData, setCurrFormData] = useState<Record<string, any>>({});
const [current, setCurrent] = useState(0);
const formBaseRef = useRef<ProFormInstance>();
const formProjectFileRef = useRef<ProFormInstance>();
// const [filePath, setFilePath] = useState('');
const handleConfigFileChange = ({ file }: { file: UploadFile }) => {
console.log(configFileList, 'handleFileChange_configFileList');
let curFile: any;
switch (file.status) {
case 'uploading':
case 'done':
curFile = [file];
break;
case 'removed':
default:
curFile = [];
break;
}
setConfigFileList([...curFile]);
};
function resetForm() {
setCurrFormData({});
}
// test 测试数据
useEffect(() => {
// 初始化赋值
if (props.updateModalOpen && props.values?.version_id) {
setCurrFormData({
...props.values,
model_file: props.values?.model_file_md5,
});
console.log(props.values, 'useEffect_values', currFormData);
} else {
resetForm();
}
}, [props.updateModalOpen, props.values]);
// useEffect(() => {
// if (formProjectFileRef) {
// formProjectFileRef.current?.setFieldValue('model_file_arr', [
// ]);
// }
// }, [props.updateModalOpen, props.values, formProjectFileRef]);
return (
<StepsForm<{
name: string;
}>
onFinish={async (values) => {
console.log('All form values:', values, {});
// 在这里处理提交数据例如调用API
let resp = await apiModelVersionEdit({
...values,
model_id: props.modelInfo?.model_id,
config_str: JSON.stringify(values.config_str_arr),
model_file: currFormData?.model_file,
version_id: currFormData?.version_id,
config_file: '',
});
if (isSuccessApi(resp)) {
message.success(
intl.formatMessage({ id: 'common.action.success', defaultMessage: '$$$' }),
);
props.reload();
props.handleModal();
} else {
message.error(
resp?.meta?.message ||
intl.formatMessage({ id: 'common.action.failure', defaultMessage: '$$$' }),
);
}
props.handleModal();
return true;
message.success('提交成功');
}}
stepsProps={proFormStepsFormProps.stepsProps}
current={current}
onCurrentChange={setCurrent}
formProps={{
validateMessages: {
required: '此项为必填项',
},
}}
stepsFormRender={(dom, submitter) => {
return (
<Modal
className="gn_model_steps_form"
title={
<FormattedMessage id="model_detail.version.table.list.add" defaultMessage="新增" />
}
width={proFormSmallModelWidth}
onCancel={() => {
setCurrent(0);
formBaseRef.current?.resetFields();
// setProjectFileList([]);
props.handleModal();
}}
open={props.updateModalOpen}
footer={submitter}
destroyOnClose
>
{dom}
</Modal>
);
}}
>
{/* 版本信息 */}
<StepsForm.StepForm<{
name: string;
}>
className="gn_form"
name="base"
formRef={formBaseRef}
title={
<FormattedMessage id="model_detail.version.stepForm.base" defaultMessage="版本信息" />
}
stepProps={{
description: (
<FormattedMessage
id="model_detail.version.stepForm.base.description"
defaultMessage="填写版本基础信息"
/>
),
}}
onFinish={async () => {
// setFormData(formBaseRef.current?.getFieldsValue());
await waitTime(500);
return true;
}}
>
<ProForm.Group>
{/* 默认填充模型 无需选择 */}
<ProFormText
width={proFormSmallItemStyleProps.width}
name="model_name"
label={
<FormattedMessage id="model_detail.version.form.modelFkId" defaultMessage="$$$" />
}
required={true}
initialValue={props.modelInfo?.name}
debounceTime={1000}
disabled={true}
/>
<ProFormText
width={proFormSmallItemStyleProps.width}
fieldProps={{
prefix: <span>V</span>,
}}
name="version"
initialValue={currFormData?.version}
label={<FormattedMessage id="model_detail.version.form.name" defaultMessage="版本号" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'model_detail.version.form.name',
defaultMessage: '$$$',
})}`}
required={true}
rules={[
{
required: true,
message: (
<FormattedMessage
id="model_detail.version.form.required.name"
defaultMessage="name is required"
/>
),
},
]}
/>
<ProFormTextArea
width={proFormSmallItemStyleProps.width}
name="comment"
initialValue={currFormData?.comment}
label={<FormattedMessage id="model_detail.version.form.remark" defaultMessage="备注" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'model_detail.version.form.remark',
defaultMessage: '$$$',
})}`}
required={false}
disabled={false}
/>
</ProForm.Group>
</StepsForm.StepForm>
{/* 版本上传 */}
<StepsForm.StepForm<{
project_file: string;
}>
className="gn_form"
name="project_file"
formRef={formProjectFileRef}
title={
<FormattedMessage
id="model_detail.version.stepForm.project_file"
defaultMessage="版本上传"
/>
}
style={{ width: proFormItemStyleProps.width }}
stepProps={{
description: (
<FormattedMessage
id="model_detail.version.stepForm.project_file.description"
defaultMessage="上传模型文件"
/>
),
}}
onFinish={async (values: any) => {
console.log(values, 'values');
// if ('projectFilePath' in values && values['projectFilePath'].length > 0) {
// let projectFilePath = values['projectFilePath'][0]?.response?.data?.path || '';
// setFilePath(projectFilePath);
// }
return true;
}}
>
<FormUploadDraggerToken
proFieldProps={{
label: <span className="font-bold"></span>,
title: (
<div className="gn_uploadfile_title py-[16px] text1 text-center">
<span></span>
<a></a>
</div>
),
description: '',
icon: <i className="iconfont icon-shangchuanwenjian text_primary text-[32px]"></i>,
name: 'model_file_arr',
max: 1,
initialValue: currFormData?.model_file_md5
? [
{
uid: currFormData?.model_file_md5,
name: currFormData?.model_file_name || currFormData?.model_file_md5,
status: 'done',
url: '/file/' + currFormData?.model_file_md5,
},
]
: [],
}}
afterUploadFile={({ resp, file }) => {
console.log(resp, file, 'afterRemoveFile');
setCurrFormData((data) => {
return { ...data, model_file: resp?.data?.result };
});
}}
afterRemoveFile={() => {
setCurrFormData((data) => {
return { ...data, model_file: '' };
});
}}
nextText="请上传格式为zip.tar.gz的模型文件"
/>
</StepsForm.StepForm>
{/* 参数配置 */}
<StepsForm.StepForm<{
config: string;
}>
className="gn_form"
name="config"
title={
<FormattedMessage id="model_detail.version.stepForm.config" defaultMessage="参数配置" />
}
stepProps={{
description: (
<FormattedMessage
id="model_detail.version.stepForm.config.description"
defaultMessage="参数配置"
/>
),
}}
onFinish={async (values: any) => {
// setConfigFileList([]);
console.log(values, 'config_values');
let formData1 = formBaseRef.current?.getFieldsValue();
if (formData1?.model_name) {
await waitTime(500);
}
return true;
}}
>
<ProFormUploadDragger
width={proFormSmallItemStyleProps.width}
max={1}
label={<span className="font-bold"></span>}
title={
<div className="gn_uploadfile_title py-[16px] text1 text-center">
<span></span>
<a></a>
</div>
}
description=""
icon={<i className="iconfont icon-shangchuanwenjian text_primary text-[32px]"></i>}
name="config_file_name"
action={fileApiActionUrl}
fieldProps={{
className: 'gn_proFormUploadDragger_formItem',
onChange: handleConfigFileChange,
onRemove: () => {
let index_ids = actionFormListRef.current?.getList()?.map((v, i) => {
return i;
});
actionFormListRef.current?.remove(index_ids || []);
},
beforeUpload: (file) => {
if (
!file.name.endsWith('.yaml') &&
!file.name.endsWith('.yml') &&
!file.name.endsWith('.json')
) {
message.error('请上传yaml或json文件').then(() => {});
return false;
} else {
let parsedData = {};
file
.text()
.then((text) => {
if (file.name.endsWith('.yaml') || file.name.endsWith('.yml')) {
parsedData = yaml.load(text) as Record<string, unknown>;
}
if (file.name.endsWith('.json')) {
parsedData = JSON.parse(text) as Record<string, unknown>;
}
if (Object.keys(parsedData).length > 0) {
dataFormListRef.current = Object.entries(parsedData).map(([key, value]) => ({
name: key,
default: value,
}));
dataFormListRef.current.forEach((v: any, i: number) => {
actionFormListRef.current?.add(v, i);
});
}
return true;
})
.catch(() => {
return false;
});
}
},
}}
/>
<div style={{ color: '#666666', marginTop: '-8px', marginBottom: '16px' }}>
.json.yaml.yml
</div>
{/* // TODO label字重与上面统一, 操作按钮需要与输入框对齐 */}
<ProFormList
name="config_str_arr"
label={
<div>
<span className="font-bold"></span>
<div style={{ color: '#333333', padding: '8px 0 4px' }}>
~
</div>
</div>
}
initialValue={
isValidJson(props.values?.config_str) ? JSON.parse(props.values?.config_str) : []
}
actionRef={actionFormListRef}
itemContainerRender={(doms) => {
return <ProForm.Group>{doms}</ProForm.Group>;
}}
alwaysShowItemLabel
>
{() => {
return (
<>
<ProFormText width={268} key="name" name="name" label="键名" />
<ProFormText width={268} key="default" name="default" label="默认值" />
</>
);
}}
</ProFormList>
</StepsForm.StepForm>
</StepsForm>
);
};
export default UpdateForm;

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-08 10:36:06 * @Date: 2024-04-08 10:36:06
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-31 14:38:28 * @LastEditTime: 2024-06-05 10:34:16
* @FilePath: \general-ai-manage\src\pages\Model\ModelDetail\index.tsx * @FilePath: \general-ai-manage\src\pages\Model\ModelDetail\index.tsx
* @Description: * @Description:
* @ * @
@ -14,34 +14,37 @@ import InnerPageBack from '@/components/Back/innerPageBack';
import { CommButton } from '@/components/Button'; import { CommButton } from '@/components/Button';
import TableActionCard from '@/components/TableActionCard'; import TableActionCard from '@/components/TableActionCard';
import IsDelete from '@/components/TableActionCard/isDelete'; import IsDelete from '@/components/TableActionCard/isDelete';
import { apiModelInfo, apiModelVersionList } from '@/services/business/model'; import {
apiModelInfo,
apiModelVersionDelete,
apiModelVersionInfo,
apiModelVersionList,
apiModelVersionSetdefault,
} from '@/services/business/model';
import { isSuccessApi } from '@/utils/forApi'; import { isSuccessApi } from '@/utils/forApi';
import type { ActionType, ProColumns } from '@ant-design/pro-components'; import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProCard, ProDescriptions, ProTable } from '@ant-design/pro-components'; import { ProCard, ProDescriptions, ProTable } from '@ant-design/pro-components';
import { FormattedMessage, useParams } from '@umijs/max'; import { FormattedMessage, useIntl, useParams } from '@umijs/max';
import { Button } from 'antd'; import { Button, message } from 'antd';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { proTablePaginationOptions } from '../../../../config/defaultTable'; import { proTablePaginationOptions } from '../../../../config/defaultTable';
import CreateForm from './components/createForm'; import CreateForm from './components/createForm';
import UpdateForm from './components/updateForm';
const ModelDetail: React.FC = () => { const ModelDetail: React.FC = () => {
const routeParams = useParams(); // 路由参数读取 const routeParams = useParams(); // 路由参数读取
// const access = useAccess(); // const access = useAccess();
// const intl = useIntl(); const intl = useIntl();
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const [createModalOpen, setCreateModalOpen] = useState<boolean>(false); const [createModalOpen, setCreateModalOpen] = useState<boolean>(false);
// const [categoryFkIdIds, setCategoryFkIdIds] = useState([]); // const [categoryFkIdIds, setCategoryFkIdIds] = useState([]);
// 动态设置每页数量 // 动态设置每页数量
const [currentPageSize, setCurrentPageSize] = useState<number>(10); const [currentPageSize, setCurrentPageSize] = useState<number>(10);
// const [currentRow, setCurrentRow] = useState<Record<string, any>>({}); const [currentRow, setCurrentRow] = useState<Record<string, any>>({});
const [modelInfo, setModelInfo] = useState<Record<string, any>>({}); const [modelInfo, setModelInfo] = useState<Record<string, any>>({});
/** const [updateModalOpen, setUpdateModalOpen] = useState<boolean>(false);
* @en-US The pop-up window of the distribution update window
* @zh-CN
* */
// const [updateModalOpen, setUpdateModalOpen] = useState<boolean>(false);
// const [showDetail, setShowDetail] = useState<boolean>(false); // const [showDetail, setShowDetail] = useState<boolean>(false);
/**新增 编辑 删除 */ /**新增 编辑 删除 */
@ -49,6 +52,19 @@ const ModelDetail: React.FC = () => {
const handleCreateModal = () => { const handleCreateModal = () => {
setCreateModalOpen(!createModalOpen); setCreateModalOpen(!createModalOpen);
}; };
// 编辑
const handleUpdateModal = () => {
setUpdateModalOpen(!updateModalOpen);
};
// 模型版本信息
async function loadDetail(record) {
const resp = await apiModelVersionInfo({ version_id: record?.version_id });
if (isSuccessApi(resp) && resp?.data) {
setCurrentRow({ ...resp?.data, version_id: record?.version_id });
setUpdateModalOpen(true);
}
}
function reloadList() { function reloadList() {
actionRef.current?.reload(); actionRef.current?.reload();
@ -64,11 +80,12 @@ const ModelDetail: React.FC = () => {
title: <FormattedMessage id="model_index.table.list.name" defaultMessage="$$$" />, title: <FormattedMessage id="model_index.table.list.name" defaultMessage="$$$" />,
dataIndex: 'name', dataIndex: 'name',
}, },
// TODO 缺失默认版本信息字段
{ {
title: ( title: (
<FormattedMessage id="model_index.table.list.defaultVersionFkId" defaultMessage="$$$" /> <FormattedMessage id="model_index.table.list.defaultVersionFkId" defaultMessage="$$$" />
), ),
dataIndex: 'default_version', dataIndex: 'version',
}, },
{ {
title: <FormattedMessage id="model_index.table.list.updateTime" defaultMessage="$$$" />, title: <FormattedMessage id="model_index.table.list.updateTime" defaultMessage="$$$" />,
@ -83,7 +100,7 @@ const ModelDetail: React.FC = () => {
const columns: ProColumns<Record<string, any>>[] = [ const columns: ProColumns<Record<string, any>>[] = [
{ {
title: <FormattedMessage id="model_detail.version.table.list.name" defaultMessage="版本号" />, title: <FormattedMessage id="model_detail.version.table.list.name" defaultMessage="版本号" />,
dataIndex: 'name', dataIndex: 'version',
hideInSearch: true, hideInSearch: true,
key: 'fixedName', key: 'fixedName',
fixed: 'left', fixed: 'left',
@ -91,9 +108,9 @@ const ModelDetail: React.FC = () => {
render: (dom, record) => ( render: (dom, record) => (
<div> <div>
<span>{dom}</span> <span>V{dom}</span>
<span> <span>
{record.isEnable && ( {record.is_default === 1 && (
<span <span
style={{ style={{
display: 'inline-block', display: 'inline-block',
@ -142,7 +159,7 @@ const ModelDetail: React.FC = () => {
defaultMessage="更新时间" defaultMessage="更新时间"
/> />
), ),
dataIndex: 'updateTime', dataIndex: 'update_time',
hideInSearch: true, hideInSearch: true,
valueType: 'dateTime', valueType: 'dateTime',
}, },
@ -157,7 +174,6 @@ const ModelDetail: React.FC = () => {
<TableActionCard <TableActionCard
key="TableActionCardRef" key="TableActionCardRef"
renderActions={[ renderActions={[
// TODO 设为默认交互补充
{ {
key: 'setDefault', key: 'setDefault',
renderDom: record.isEnable ? ( renderDom: record.isEnable ? (
@ -169,11 +185,25 @@ const ModelDetail: React.FC = () => {
key="setDefault" key="setDefault"
type="link" type="link"
size="small" size="small"
onClick={() => { onClick={async () => {
// setCurrentRow(record); const resp = await apiModelVersionSetdefault({
// history.push('/home/model-detail'); model_id: Number(routeParams.id),
// doToDetail(record); version_id: record?.version_id,
// setShowDetail(true); });
if (isSuccessApi(resp)) {
message.success(
intl.formatMessage({ id: 'common.action.success', defaultMessage: '$$$' }),
);
reloadList();
} else {
message.error(
resp?.meta?.message ||
intl.formatMessage({
id: 'common.action.failure',
defaultMessage: '$$$',
}),
);
}
}} }}
> >
<FormattedMessage id="pages.searchTable.setDefault" defaultMessage="设为默认" /> <FormattedMessage id="pages.searchTable.setDefault" defaultMessage="设为默认" />
@ -181,22 +211,17 @@ const ModelDetail: React.FC = () => {
), ),
}, },
{ {
key: 'updateDetail', key: 'update',
renderDom: ( renderDom: (
<Button <Button
key="updateDetail" key="update"
type="link" type="link"
size="small" size="small"
onClick={() => { onClick={() => {
// TODO 编辑在新增联调后实现 loadDetail(record);
// setCurrentRow(record);
// history.push('/home/model-detail');
// doToDetail(record);
// setShowDetail(true);
// setUpdateModalOpen(true);
}} }}
> >
<FormattedMessage id="pages.searchTable.updateDetail" defaultMessage="查看" /> <FormattedMessage id="pages.searchTable.update" defaultMessage="查看" />
</Button> </Button>
), ),
}, },
@ -205,7 +230,16 @@ const ModelDetail: React.FC = () => {
renderDom: ( renderDom: (
<IsDelete <IsDelete
deleteApi={() => { deleteApi={() => {
handleDestroy(record).then(() => {}); console.log('删除成功');
apiModelVersionDelete({ version_id: record.version_id }).then(() => {
message.success(
intl.formatMessage({
id: 'common.action.success',
defaultMessage: '$$$',
}),
);
reloadList();
});
}} }}
></IsDelete> ></IsDelete>
), ),
@ -331,6 +365,13 @@ const ModelDetail: React.FC = () => {
modelInfo={modelInfo} modelInfo={modelInfo}
reload={reloadList} reload={reloadList}
/> />
<UpdateForm
updateModalOpen={updateModalOpen}
handleModal={handleUpdateModal}
modelInfo={modelInfo}
values={currentRow}
reload={reloadList}
/>
</div> </div>
); );
}; };

@ -1,5 +1,5 @@
import { CommButton } from '@/components/Button'; import { CommButton } from '@/components/Button';
import { apiModelHubSync } from '@/services/business/model'; import { apiModelHubAdd, apiModelHubSync } from '@/services/business/model';
import { import {
ModalForm, ModalForm,
ProForm, ProForm,
@ -11,6 +11,7 @@ import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, message } from 'antd'; import { Form, message } from 'antd';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { isSuccessApi } from '@/utils/forApi';
import { import {
proFormSmallItemStyleProps, proFormSmallItemStyleProps,
proFormSmallModelWidth, proFormSmallModelWidth,
@ -31,7 +32,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
className="gn_form gn_modal_form" className="gn_form gn_modal_form"
width={proFormSmallModelWidth} width={proFormSmallModelWidth}
title={intl.formatMessage({ title={intl.formatMessage({
id: 'model_runtimeLib.list.table.createForm.add', id: 'model_runtimeLib.list.table.form.action.add',
defaultMessage: '新建', defaultMessage: '新建',
})} })}
open={props.createModalOpen} open={props.createModalOpen}
@ -44,16 +45,19 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
submitTimeout={2000} submitTimeout={2000}
onFinish={async (values) => { onFinish={async (values) => {
console.log(values, 'add_finish_values'); console.log(values, 'add_finish_values');
// TODO 对接新增接口 let resp = await apiModelHubAdd(values);
// postModelCategoryCreateModelCategory(values) if (isSuccessApi(resp)) {
// .then(() => { message.success(
// message.success(intl.formatMessage({ id: 'common.action.success', defaultMessage: '$$$' })); intl.formatMessage({ id: 'common.action.success', defaultMessage: '$$$' }),
// props.reload(); );
// }) props.reload();
// .catch(() => { props.handleModal();
// message.error(intl.formatMessage({ id: 'common.action.failure', defaultMessage: '$$$' })); } else {
// }); message.error(
props.handleModal(); resp?.meta?.message ||
intl.formatMessage({ id: 'common.action.failure', defaultMessage: '$$$' }),
);
}
return true; return true;
}} }}
> >
@ -87,8 +91,8 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
<ProForm.Group> <ProForm.Group>
<ProFormText <ProFormText
width={proFormSmallItemStyleProps.column2Width - 30} width={proFormSmallItemStyleProps.column2Width - 30}
name="ip" name="host"
initialValue={'http://127.0.0.1'} initialValue={'192.168.10.94'}
label={ label={
<FormattedMessage id="model_runtimeLib.list.table.form.IP" defaultMessage="IP地址" /> <FormattedMessage id="model_runtimeLib.list.table.form.IP" defaultMessage="IP地址" />
} }
@ -115,7 +119,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
<ProFormText <ProFormText
width={proFormSmallItemStyleProps.column2Width - 30} width={proFormSmallItemStyleProps.column2Width - 30}
name="port" name="port"
initialValue={'80'} initialValue={'5000'}
label={ label={
<FormattedMessage id="model_runtimeLib.list.table.form.port" defaultMessage="端口" /> <FormattedMessage id="model_runtimeLib.list.table.form.port" defaultMessage="端口" />
} }
@ -151,9 +155,8 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
color: '#154ddd', color: '#154ddd',
}} }}
onClick={() => { onClick={() => {
const { ip, port } = form.getFieldsValue(); const { host, port } = form.getFieldsValue();
if (ip && port) { if (host && port) {
// 访问接口拿数据
setOpenFiles(true); setOpenFiles(true);
} else { } else {
message.error('请填写IP和端口'); message.error('请填写IP和端口');
@ -167,7 +170,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
{openFiles ? ( {openFiles ? (
<ProFormSelect <ProFormSelect
width={proFormSmallItemStyleProps.width} width={proFormSmallItemStyleProps.width}
name="fileName" name="path"
placeholder={`${intl.formatMessage({ placeholder={`${intl.formatMessage({
id: 'common.please_select', id: 'common.please_select',
defaultMessage: '$$$', defaultMessage: '$$$',
@ -179,9 +182,13 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
showSearch showSearch
debounceTime={500} debounceTime={500}
request={async () => { request={async () => {
const { data } = await apiModelHubSync(); const { host, port } = form.getFieldsValue();
const { data } = await apiModelHubSync({
host,
port,
});
return data?.data?.map((v: Record<string, any>) => { return data?.data?.map((v: Record<string, any>) => {
return { ...v, label: v.name, value: v.id }; return { ...v, label: v, value: v };
}); });
}} }}
rules={[ rules={[
@ -202,7 +209,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
<ProFormTextArea <ProFormTextArea
width={proFormSmallItemStyleProps.width} width={proFormSmallItemStyleProps.width}
name="remark" name="comment"
label={ label={
<FormattedMessage id="model_runtimeLib.list.table.form.remark" defaultMessage="简介" /> <FormattedMessage id="model_runtimeLib.list.table.form.remark" defaultMessage="简介" />
} }

@ -0,0 +1,241 @@
import { CommButton } from '@/components/Button';
import { apiModelHubEdit, apiModelHubSync } from '@/services/business/model';
import {
ModalForm,
ProForm,
ProFormSelect,
ProFormText,
ProFormTextArea,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, message } from 'antd';
import React, { useEffect, useState } from 'react';
import { isSuccessApi } from '@/utils/forApi';
import {
proFormSmallItemStyleProps,
proFormSmallModelWidth,
} from '../../../../../config/defaultForm';
export type UpdateFormProps = {
updateModalOpen: boolean;
values: Record<string, any>;
handleModal: () => void;
reload: any;
};
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
const intl = useIntl();
const [openFiles, setOpenFiles] = useState<boolean>(false);
const [form] = Form.useForm<API.ModelCategory>();
function resetForm() {
form.resetFields();
}
useEffect(() => {
if (props.updateModalOpen && props.values?.id) {
form.setFieldsValue({ ...props.values });
console.log(props.values, 'useEffect_values');
} else {
resetForm();
}
}, [props.updateModalOpen, props.values]);
return (
<ModalForm<API.ModelCategory>
className="gn_form gn_modal_form"
width={proFormSmallModelWidth}
title={intl.formatMessage({
id: 'model_runtimeLib.list.table.form.action.edit',
defaultMessage: '编辑',
})}
open={props.updateModalOpen}
form={form}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => props.handleModal(),
}}
submitTimeout={2000}
onFinish={async (values) => {
console.log(values, 'add_finish_values');
let resp = await apiModelHubEdit({ ...values, id: props.values.id });
if (isSuccessApi(resp)) {
message.success(
intl.formatMessage({ id: 'common.action.success', defaultMessage: '$$$' }),
);
props.reload();
props.handleModal();
} else {
message.error(
resp?.meta?.message ||
intl.formatMessage({ id: 'common.action.failure', defaultMessage: '$$$' }),
);
}
return true;
}}
>
<ProForm.Group>
<ProFormText
width={proFormSmallItemStyleProps.width}
name="name"
label={
<FormattedMessage id="model_runtimeLib.list.table.form.name" defaultMessage="名称" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'model_runtimeLib.list.table.form.name',
defaultMessage: '$$$',
})}`}
required={true}
rules={[
{
required: true,
message: (
<FormattedMessage
id="model_runtimeLib.list.table.form.rule.required.name"
defaultMessage="name is required"
/>
),
},
]}
/>
<ProForm.Group>
<ProFormText
width={proFormSmallItemStyleProps.column2Width - 30}
name="host"
initialValue={'192.168.10.94'}
label={
<FormattedMessage id="model_runtimeLib.list.table.form.IP" defaultMessage="IP地址" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'model_runtimeLib.list.table.form.IP',
defaultMessage: '$$$',
})}`}
required={true}
rules={[
{
required: true,
message: (
<FormattedMessage
id="model_runtimeLib.list.table.form.rule.required.IP"
defaultMessage="name is required"
/>
),
},
]}
/>
<ProFormText
width={proFormSmallItemStyleProps.column2Width - 30}
name="port"
initialValue={'5000'}
label={
<FormattedMessage id="model_runtimeLib.list.table.form.port" defaultMessage="端口" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'model_runtimeLib.list.table.form.port',
defaultMessage: '$$$',
})}`}
required={true}
rules={[
{
required: true,
message: (
<FormattedMessage
id="model_runtimeLib.list.table.form.rule.required.port"
defaultMessage="name is required"
/>
),
},
]}
/>
<CommButton
style={{
width: '52px',
height: '32px',
marginTop: '30px',
marginLeft: '-4px',
borderRadius: '4px',
borderColor: '#154ddd',
color: '#154ddd',
}}
onClick={() => {
const { host, port } = form.getFieldsValue();
if (host && port) {
setOpenFiles(true);
} else {
message.error('请填写IP和端口');
}
console.log(form.getFieldsValue(), 'searchIP');
}}
buttonLabel={<FormattedMessage id="pages.searchTable.search" defaultMessage="查询" />}
></CommButton>
</ProForm.Group>
{openFiles ? (
<ProFormSelect
width={proFormSmallItemStyleProps.width}
name="path"
placeholder={`${intl.formatMessage({
id: 'common.please_select',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'model_runtimeLib.list.table.form.fileName',
defaultMessage: '$$$',
})}`}
required={true}
showSearch
debounceTime={500}
request={async () => {
const { host, port } = form.getFieldsValue();
const { data } = await apiModelHubSync({
host,
port,
});
return data?.data?.map((v: Record<string, any>) => {
return { ...v, label: v, value: v };
});
}}
rules={[
{
required: true,
message: (
<FormattedMessage
id="model_runtimeLib.list.table.form.rule.required.fileName"
defaultMessage="name is required"
/>
),
},
]}
/>
) : (
<></>
)}
<ProFormTextArea
width={proFormSmallItemStyleProps.width}
name="comment"
label={
<FormattedMessage id="model_runtimeLib.list.table.form.remark" defaultMessage="简介" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'model_runtimeLib.list.table.form.rule.required.remark',
defaultMessage: '$$$',
})}`}
required={false}
disabled={false}
/>
</ProForm.Group>
</ModalForm>
);
};
export default UpdateForm;

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-08 10:36:06 * @Date: 2024-04-08 10:36:06
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-31 14:20:31 * @LastEditTime: 2024-06-06 15:13:25
* @FilePath: \general-ai-manage\src\pages\Model\ModelRuntimeLib\index.tsx * @FilePath: \general-ai-manage\src\pages\Model\ModelRuntimeLib\index.tsx
* @Description: * @Description:
* @ * @
@ -11,7 +11,7 @@
* *
*/ */
import { apiModelHubList } from '@/services/business/model'; import { apiModelHubDelete, apiModelHubInfo, apiModelHubList } from '@/services/business/model';
import { isSuccessApi } from '@/utils/forApi'; import { isSuccessApi } from '@/utils/forApi';
import { CommButton } from '@/components/Button'; import { CommButton } from '@/components/Button';
@ -20,17 +20,19 @@ import IsDelete from '@/components/TableActionCard/isDelete';
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components'; import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProCard, ProForm, ProFormText, ProTable } from '@ant-design/pro-components'; import { ProCard, ProForm, ProFormText, ProTable } from '@ant-design/pro-components';
import { Access, FormattedMessage, history, useAccess } from '@umijs/max'; import { Access, FormattedMessage, history, useAccess, useIntl } from '@umijs/max';
import { Button } from 'antd'; import { Button, message } from 'antd';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { proTableCommonOptions, proTablePaginationOptions } from '../../../../config/defaultTable'; import { proTableCommonOptions, proTablePaginationOptions } from '../../../../config/defaultTable';
import CreateForm from './components/createForm'; import CreateForm from './components/createForm';
import UpdateForm from './components/updateForm';
import { ReactComponent as ResetIcon } from '/public/home/reset_icon.svg'; import { ReactComponent as ResetIcon } from '/public/home/reset_icon.svg';
import { ReactComponent as SearchIcon } from '/public/home/search_icon.svg'; import { ReactComponent as SearchIcon } from '/public/home/search_icon.svg';
const ModelRuntimeLib: React.FC = () => { const ModelRuntimeLib: React.FC = () => {
const access = useAccess(); const access = useAccess();
// const intl = useIntl(); const intl = useIntl();
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const [querysData, setQuerysData] = useState<Record<string, any>>({}); // 列表查询参数 const [querysData, setQuerysData] = useState<Record<string, any>>({}); // 列表查询参数
@ -41,24 +43,25 @@ const ModelRuntimeLib: React.FC = () => {
const [currentRow, setCurrentRow] = useState<Record<string, any>>({}); const [currentRow, setCurrentRow] = useState<Record<string, any>>({});
const [form] = ProForm.useForm(); // form 对象 const [form] = ProForm.useForm(); // form 对象
/** const [updateModalOpen, setUpdateModalOpen] = useState<boolean>(false);
* @en-US The pop-up window of the distribution update window
* @zh-CN
* */
// const [updateModalOpen, setUpdateModalOpen] = useState<boolean>(false);
// const [showDetail, setShowDetail] = useState<boolean>(false); // const [showDetail, setShowDetail] = useState<boolean>(false);
/**新增 编辑 删除 */ /**新增 编辑 删除 */
// 新增 // 新增
const handleCreateModal = () => { const handleCreateModal = () => {
if (createModalOpen) { setCreateModalOpen(!createModalOpen);
setCreateModalOpen(false);
setCurrentRow(undefined);
} else {
setCreateModalOpen(true);
}
}; };
// 编辑
const handleUpdateModal = () => {
setUpdateModalOpen(!updateModalOpen);
};
// 设备节点信息
async function loadDetail(record) {
const resp = await apiModelHubInfo({ id: record?.id });
if (isSuccessApi(resp) && resp?.data) {
setCurrentRow({ ...resp?.data, id: record?.id });
}
}
function reloadList() { function reloadList() {
actionRef.current?.reload(); actionRef.current?.reload();
} }
@ -68,7 +71,6 @@ const ModelRuntimeLib: React.FC = () => {
reloadList(); reloadList();
} }
}, [actionRef, querysData]); }, [actionRef, querysData]);
// TODO 完善列表展示字段
const columns: ProColumns<Record<string, any>>[] = [ const columns: ProColumns<Record<string, any>>[] = [
{ {
title: <FormattedMessage id="model_runtimeLib.table.list.name" defaultMessage="$$$" />, title: <FormattedMessage id="model_runtimeLib.table.list.name" defaultMessage="$$$" />,
@ -77,11 +79,17 @@ const ModelRuntimeLib: React.FC = () => {
key: 'fixedName', key: 'fixedName',
fixed: 'left', fixed: 'left',
}, },
{
title: <FormattedMessage id="model_runtimeLib.table.list.path" defaultMessage="创建时间" />,
dataIndex: 'path',
hideInSearch: true,
},
{ {
title: ( title: (
<FormattedMessage id="model_runtimeLib.table.list.createTime" defaultMessage="创建时间" /> <FormattedMessage id="model_runtimeLib.table.list.createTime" defaultMessage="创建时间" />
), ),
dataIndex: 'createTime', dataIndex: 'create_time',
hideInSearch: true, hideInSearch: true,
valueType: 'dateTime', valueType: 'dateTime',
}, },
@ -90,7 +98,7 @@ const ModelRuntimeLib: React.FC = () => {
title: ( title: (
<FormattedMessage id="model_runtimeLib.table.list.updateTime" defaultMessage="更新时间" /> <FormattedMessage id="model_runtimeLib.table.list.updateTime" defaultMessage="更新时间" />
), ),
dataIndex: 'updateTime', dataIndex: 'update_time',
hideInSearch: true, hideInSearch: true,
valueType: 'dateTime', valueType: 'dateTime',
}, },
@ -113,8 +121,8 @@ const ModelRuntimeLib: React.FC = () => {
type="link" type="link"
size="small" size="small"
onClick={() => { onClick={() => {
// setUpdateModalOpen(true); loadDetail(record);
setCurrentRow(record); setUpdateModalOpen(true);
}} }}
> >
<FormattedMessage id="pages.searchTable.update" defaultMessage="Update" /> <FormattedMessage id="pages.searchTable.update" defaultMessage="Update" />
@ -126,7 +134,16 @@ const ModelRuntimeLib: React.FC = () => {
renderDom: ( renderDom: (
<IsDelete <IsDelete
deleteApi={() => { deleteApi={() => {
handleDestroy(record).then(() => {}); console.log('删除成功');
apiModelHubDelete({ id: record.id }).then(() => {
message.success(
intl.formatMessage({
id: 'common.action.success',
defaultMessage: '$$$',
}),
);
reloadList();
});
}} }}
></IsDelete> ></IsDelete>
), ),
@ -216,7 +233,10 @@ const ModelRuntimeLib: React.FC = () => {
setCreateModalOpen(true); setCreateModalOpen(true);
}} }}
> >
<FormattedMessage id="pages.searchTable.new" defaultMessage="New" /> <FormattedMessage
id="model_runtimeLib.list.table.form.action.add"
defaultMessage="新建"
/>
</Button> </Button>
</Access> </Access>
} }
@ -250,23 +270,18 @@ const ModelRuntimeLib: React.FC = () => {
persistenceKey: 'algorithm_model_list', persistenceKey: 'algorithm_model_list',
persistenceType: 'localStorage', persistenceType: 'localStorage',
}} }}
request={async (params = {}, sort) => { request={async (params = {}) => {
const { current, ...rest } = params; const { current, ...rest } = params;
const reqParams = { const reqParams = {
pageNo: current, pageNo: current,
desc: false,
orderKey: '',
...rest, ...rest,
}; };
if (sort && Object.keys(sort).length) {
reqParams.orderKey = Object.keys(sort)[0];
let sort_select = sort[reqParams.orderKey];
reqParams.desc = sort_select === 'descend';
}
let resp = await apiModelHubList({ ...reqParams, ...querysData }); let resp = await apiModelHubList({ ...reqParams, ...querysData });
if (isSuccessApi(resp)) { if (!isSuccessApi(resp)) {
return { data: [], success: true }; return { data: [], success: true };
} }
console.log(resp, 'apiEntityNodesDeviceList_resp');
return { return {
data: resp.data?.data, data: resp.data?.data,
success: resp.success, success: resp.success,
@ -280,10 +295,15 @@ const ModelRuntimeLib: React.FC = () => {
</ProCard> </ProCard>
<CreateForm <CreateForm
createModalOpen={createModalOpen} createModalOpen={createModalOpen}
values={currentRow || {}}
handleModal={handleCreateModal} handleModal={handleCreateModal}
reload={reloadList} reload={reloadList}
/> />
<UpdateForm
updateModalOpen={updateModalOpen}
handleModal={handleUpdateModal}
values={currentRow}
reload={reloadList}
/>
</div> </div>
); );
}; };

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2024-05-23 13:50:50 * @Date: 2024-05-23 13:50:50
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-30 15:01:21 * @LastEditTime: 2024-06-06 15:39:10
* @FilePath: \general-ai-platform-web\src\services\Business\entity.ts * @FilePath: \general-ai-platform-web\src\services\Business\entity.ts
* @Description: api * @Description: api
*/ */

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2024-05-24 17:57:19 * @Date: 2024-05-24 17:57:19
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-31 14:19:28 * @LastEditTime: 2024-06-06 15:44:20
* @FilePath: \general-ai-platform-web\src\services\business\model.ts * @FilePath: \general-ai-platform-web\src\services\business\model.ts
* @Description: api * @Description: api
*/ */
@ -15,7 +15,7 @@ import { request } from '@umijs/max';
// 添加企业部署的业务模型 // 添加企业部署的业务模型
export async function apiModelDeploymentAdd(body: any, options?: { [key: string]: any }) { export async function apiModelDeploymentAdd(body: any, options?: { [key: string]: any }) {
return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>( return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>(
`/api/v1/model/deployment/add`, `/api/v1/enterprise/model/deployment/add`,
{ {
method: 'POST', method: 'POST',
headers: { headers: {
@ -30,7 +30,7 @@ export async function apiModelDeploymentAdd(body: any, options?: { [key: string]
// 编辑企业部署的业务模型 // 编辑企业部署的业务模型
export async function apiModelDeploymentEdit(body: any, options?: { [key: string]: any }) { export async function apiModelDeploymentEdit(body: any, options?: { [key: string]: any }) {
return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>( return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>(
`/api/v1/model/deployment/edit`, `/api/v1/enterprise/model/deployment/edit`,
{ {
method: 'POST', method: 'POST',
headers: { headers: {
@ -45,7 +45,7 @@ export async function apiModelDeploymentEdit(body: any, options?: { [key: string
// 企业部署的业务模型列表 // 企业部署的业务模型列表
export async function apiModelDeploymentList(body: any, options?: { [key: string]: any }) { export async function apiModelDeploymentList(body: any, options?: { [key: string]: any }) {
return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>( return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>(
`/api/v1/model/deployment/list`, `/api/v1/enterprise/model/deployment/list`,
{ {
method: 'POST', method: 'POST',
headers: { headers: {
@ -60,7 +60,7 @@ export async function apiModelDeploymentList(body: any, options?: { [key: string
// // 删除企业部署的业务模型 // // 删除企业部署的业务模型
// export async function apiModelDeploymentDelete(body: any, options?: { [key: string]: any }) { // export async function apiModelDeploymentDelete(body: any, options?: { [key: string]: any }) {
// return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>( // return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>(
// `/api/v1/model/deployment/delete`, // `/api/v1/enterprise/model/deployment/delete`,
// { // {
// method: 'POST', // method: 'POST',
// headers: { // headers: {
@ -76,7 +76,7 @@ export async function apiModelDeploymentList(body: any, options?: { [key: string
// 企业部署的业务模型信息 // 企业部署的业务模型信息
export async function apiModelDeploymentInfo(body: any, options?: { [key: string]: any }) { export async function apiModelDeploymentInfo(body: any, options?: { [key: string]: any }) {
return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>( return request<API.Response & { data?: API.ENTITY_INDEX_DATA; msg?: string }>(
`/api/v1/model/deployment/info`, `/api/v1/enterprise/model/deployment/info`,
{ {
method: 'POST', method: 'POST',
headers: { headers: {

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-24 10:11:48 * @Date: 2024-04-24 10:11:48
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-27 14:00:45 * @LastEditTime: 2024-06-05 10:47:32
* @FilePath: \general-ai-platform-web\src\utils\forApi.ts * @FilePath: \general-ai-platform-web\src\utils\forApi.ts
* @Description: * @Description:
*/ */
@ -18,6 +18,7 @@ export function isSuccessApi(result: API.API_COMMON_DATA): boolean {
if (meta.code < 300 && meta.code >= 200) { if (meta.code < 300 && meta.code >= 200) {
return true; return true;
} }
return false;
} }
return false; return false;
} }

@ -0,0 +1,22 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-06-05 13:32:31
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-06-06 13:13:25
* @FilePath: \general-ai-platform-web\src\utils\is.ts
* @Description:
*/
/**
* @JSONString
* @param jsonString
* @returns boolean
*/
export function isValidJson(jsonString) {
try {
JSON.parse(jsonString);
return true;
} catch (e) {
return false;
}
}
Loading…
Cancel
Save