feat: 完善设备节点设置的基础模型模块

develop2
donghao 12 months ago
parent 1eb526d45d
commit d8590ee313

@ -2,13 +2,14 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-11 14:13:34
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-24 17:58:27
* @LastEditTime: 2024-05-06 13:55:01
* @FilePath: \general-ai-manage\mock\model.ts
* @Description: mock api
*/
/**模型管理模块 mock */
import {
mockGetGroupBaseModelListData,
mockGetModelListData,
mockGetModelRuntimeLibFilesListData,
mockGetModelRuntimeLibListData,
@ -18,7 +19,7 @@ import { successMockApiProps } from './typing';
import { fetchCurrPageByList, fetchMockSuccessFullByOther } from './utils/apiMock';
export default {
// 模型列表分页
'GET /api/model/list/': async (req: Request, res: Response) => {
'GET /api/model/list': async (req: Request, res: Response) => {
// get 使用 query 读取参数
const { page, pageSize } = req.query;
const resData: successMockApiProps = {
@ -29,8 +30,8 @@ export default {
};
res.json(resData);
},
// 模型列表分页
'GET /api/model/detail/': async (req: Request, res: Response) => {
// 模型详情
'GET /api/model/detail': async (req: Request, res: Response) => {
// get 使用 query 读取参数
const { id } = req.query;
let finalData = {};
@ -47,7 +48,7 @@ export default {
},
// 模型版本列表
'GET /api/model/versions/list/': async (req: Request, res: Response) => {
'GET /api/model/versions/list': async (req: Request, res: Response) => {
// get 使用 query 读取参数
const { page, pageSize } = req.query;
const resData: successMockApiProps = {
@ -60,7 +61,7 @@ export default {
},
// 模型运行库列表分页
'GET /api/model/runtimeLib/list/': async (req: Request, res: Response) => {
'GET /api/model/runtimeLib/list': async (req: Request, res: Response) => {
// get 使用 query 读取参数
const { page, pageSize } = req.query;
const resData: successMockApiProps = {
@ -71,11 +72,17 @@ export default {
};
res.json(resData);
},
// 模型运行库列表分页
'GET /api/model/runtimeLib/filesList/': async (req: Request, res: Response) => {
// 模型运行库文件列表
'GET /api/model/runtimeLib/filesList': async (req: Request, res: Response) => {
const resData: successMockApiProps = fetchMockSuccessFullByOther(
mockGetModelRuntimeLibFilesListData,
);
res.json(resData);
},
// 节点下基础模型列表
'GET /api/model/groupBaseModel/list': async (req: Request, res: Response) => {
const resData: successMockApiProps = fetchMockSuccessFullByOther(mockGetGroupBaseModelListData);
res.json(resData);
},
};

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-24 17:51:07
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-25 15:19:23
* @LastEditTime: 2024-05-06 16:59:44
* @FilePath: \general-ai-platform-web\mock\pools\businessModelData.ts
* @Description: mock
*/
@ -19,11 +19,11 @@ const startBusinessModelList = () => {
const randomString = generateRandomString(20);
// const hash = generateHash(randomString)
for (let i = 0; i < count; i++) {
const isEnable = i === 0;
currList.push({
id: i + '1',
createTime: generateRandomDateTimeByYear(2022),
updateTime: generateRandomDateTimeByYear(2023),
isEnable: Math.floor(Math.random() * 8) % 2 === 0,
industry: '通用',
name: nameArr[i],
deviceSort: '文字识别',

@ -3251,3 +3251,51 @@ export const mockGetModelVersionListData = {
...JSON.parse(JSON.stringify(startModelVersionList())),
},
};
// 节点下模型列表
const startGroupBaseModelList = () => {
const currList: Record<string, any>[] = [];
const nameArr = ['陌生人群检测模型', '在岗打瞌睡检测模型', '上班离岗检测模型'];
const count = nameArr.length;
const baseModelItems = () => {
const items = [];
const listCount = Math.floor(Math.random() * 3) + 1;
const baseVersion = 'V' + Math.floor(Math.random() * 10);
const currFileTime = new Date().getTime();
for (let i = 0; i < listCount; i++) {
const isEnable = i === 0;
items.push({
id: i + '1',
name: '基础模型00' + (i + 1),
createTime: generateRandomDateTimeByYear(2022),
updateTime: generateRandomDateTimeByYear(2023),
version: baseVersion + '.' + Math.floor(Math.random() * 4) + '.' + i,
runtimeLibFile:
Math.floor(Math.random() * 2) === 1
? ''
: currFileTime + Math.floor(Math.random() * 1000) + i + '.zip',
});
}
return items;
};
for (let i = 0; i < count; i++) {
const isEnable = i === 0;
currList.push({
id: i + '1',
createTime: generateRandomDateTimeByYear(2022),
updateTime: generateRandomDateTimeByYear(2023),
name: nameArr[i],
list: baseModelItems(),
});
}
return {
count,
results: currList,
};
};
export const mockGetGroupBaseModelListData = {
data: {
...startGroupBaseModelList(),
},
};

@ -56,3 +56,20 @@
text-align: left;
text-transform: none;
}
.gn_list_card_title {
display: flex;
align-items: center;
}
.gn_list_card_title span:nth-child(1) {
width: 2px;
height: 16px;
margin-right: 8px;
background: #154ddd;
}
.gn_list_card_title span:nth-child(2) {
color: #333333;
font-weight: bold;
font-size: 14px;
font-family: PingFang SC, PingFang SC;
line-height: 22px;
}

@ -62,3 +62,21 @@
text-align: left;
text-transform: none;
}
.gn_list_card_title {
display: flex;
align-items: center;
span:nth-child(1) {
width: 2px;
height: 16px;
margin-right: 8px;
background: #154ddd;
}
span:nth-child(2) {
color: #333333;
font-weight: bold;
font-size: 14px;
font-family: PingFang SC, PingFang SC;
line-height: 22px;
}
}

@ -5,18 +5,28 @@ import React from 'react';
export type CommButtonProps = {
title: string | React.ReactNode;
backType?: 'default' | undefined;
backType?: 'default' | 'custom' | undefined;
toBack?: () => void;
size?: number;
};
const InnerPageBack: React.FC<CommButtonProps> = (props) => {
function doBack() {
history.go(-1);
switch (props?.backType) {
case 'custom':
props?.toBack();
break;
default:
history.go(-1);
break;
}
}
return (
<div className="flex items-center innerPageBack_wrap mb-[16px]" onClick={doBack}>
<ArrowLeftOutlined style={{ fontSize: '18px' }} />
<span className="h1 ml-[8px]">{props?.title || '返回'}</span>
<div className="flex items-center innerPageBack_wrap mb-[16px] " onClick={doBack}>
<ArrowLeftOutlined style={{ fontSize: `${props.size || 18}px` }} />
<span className="h1 ml-[8px]" style={{ fontSize: `${props.size || 18}px` }}>
{props?.title || '返回'}
</span>
</div>
);
};

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-10 17:21:34
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-25 15:54:52
* @LastEditTime: 2024-05-06 16:58:50
* @FilePath: \general-ai-manage\src\locales\zh-CN\model.ts
* @Description:
*/
@ -75,13 +75,10 @@ export const model_version: { [key: string]: string } = {
'model_detail.version.form.required.name': '请填写版本号',
'model_detail.version.form.remark': '备注',
'model_detail.version.list.table.form.rule.required.name': '请填写版本号',
// 待启用
'model_detail.version.table.list.startCode': '启动代码',
'model_detail.version.table.rule.required.path': '模型地址为必填项',
};
// 业务模型
export const business_project_list: { [key: string]: string } = {
export const business_model: { [key: string]: string } = {
'business_model.table.title': '业务模型列表',
'business_model.table.list.add.name': '创建模型',
'business_model.table.list.name': '业务模型名称',
@ -96,9 +93,47 @@ export const business_project_list: { [key: string]: string } = {
'business_model.stepForm.config.description': '配置业务参数',
'business_model.form.modelFkId': '业务模型名称',
'business_model.form.remark': '简介',
'business_model.table.list.isEnable': '模型部署',
'business_model.table.list.deployed.isEnable': '已部署',
'business_model.table.list.undeployed.isEnable': '未部署',
};
export const base_model: { [key: string]: string } = {
'base_model.table.title': '模型列表',
'base_model.table.list.name': '基础模型名称',
'base_model.table.list.version': '版本',
'base_model.table.list.runtimeLibFile': '运行库镜像',
'base_model.table.list.action.config': '参数配置',
'base_model.table.list.undeployed.runtimeLibFile': '未部署',
'base_model.stepForm.businessConfig': '业务参数配置',
'base_model.stepForm.businessConfig.description': '配置业务参数',
'base_model.stepForm.add.title': '基础模型配置',
'base_model.stepForm.runtimeLib': '运行库选择',
'base_model.stepForm.runtimeLib.description': '选择运行库文件',
'business_model.form.name': '版本号',
'business_model.form.required.name': '请填写版本号',
'base_model.stepForm.modelConfig': '模型参数配置',
'base_model.stepForm.modelConfig.description': '配置模型参数',
'base_model.stepForm.group': '关联节点',
'base_model.stepForm.group.description': '关联相关设备节点',
'base_model.form.modelFkId': '模型',
'base_model.form.name': '版本号',
'base_model.form.required.name': '请填写版本号',
'base_model.form.remark': '备注',
'base_model.list.table.form.rule.required.name': '请填写版本号',
// 待启用
'base_model.stepForm.base': '基本信息',
'base_model.stepForm.base.description': '填写基础信息',
'base_model.stepForm.baseModel': '关联模型',
'base_model.stepForm.baseModel.description': '关联基础模型',
'base_model.stepForm.project_file': '业务代码',
'base_model.stepForm.project_file.description': '上传业务代码',
'base_model.stepForm.config': '参数配置',
'base_model.stepForm.config.description': '配置业务参数',
'base_model.form.modelFkId': '业务模型名称',
'base_model.form.remark': '简介',
'base_model.form.name': '版本号',
'base_model.form.required.name': '请填写版本号',
};
// 未启用

@ -0,0 +1,510 @@
import { getModelRuntimeLibFilesList } from '@/services/testApi/model';
import {
ProForm,
ProFormInstance,
ProFormList,
ProFormSelect,
ProFormText,
ProFormUploadDragger,
StepsForm,
} from '@ant-design/pro-components';
import { FormListActionType } from '@ant-design/pro-form/lib';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Button, Modal, UploadFile, message } from 'antd';
import yaml from 'js-yaml';
import React, { useEffect, useRef, useState } from 'react';
import {
proFormSmallItemStyleProps,
proFormSmallModelWidth,
proFormStepsFormProps,
} from '../../../../../config/defaultForm';
// import {beforeUploadFile} from "@/utils/common";
// @ts-ignore
export type FormValueType = {
target?: string;
template?: string;
type?: string;
time?: string;
frequency?: string;
} & Partial<API.ModelVersion>;
export type CreateModelParamsProps = {
createModalOpen: boolean;
handleModal: () => void;
values: Partial<API.ModelVersion>;
reload: any;
currentDefaultFieldsData?: Record<string, any>;
};
const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
// interface ProjectConfig {
// params: Array<object>;
// }
const CreateModelParams: React.FC<CreateModelParamsProps> = (props) => {
const actionFormListRef = useRef<
FormListActionType<{
name: string;
}>
>();
const intl = useIntl();
const [isHasModelFkId, setIsHasModelFkId] = useState<boolean>(false);
const [dataFormList] = useState<any>([]);
const dataFormListRef = useRef(dataFormList);
const [fileList, setFileList] = useState<UploadFile<any>[]>([]);
// const [form] = Form.useForm<API.ModelVersion>();
const [current, setCurrent] = useState(0);
const formRef = useRef<ProFormInstance>();
const [filePath] = useState('');
const [openFiles, setOpenFiles] = useState<boolean>(false);
const handleFileChange = ({ file }: { file: UploadFile }) => {
let curFile: any;
switch (file.status) {
case 'uploading':
case 'done':
curFile = [file];
break;
case 'removed':
default:
curFile = [];
break;
}
setFileList([...curFile]);
};
useEffect(() => {
if (props.currentDefaultFieldsData) {
// 如果是在模型详情新增版本ModelFkId不可编辑
console.log(props.values, isHasModelFkId, 'currentDefaultFieldsData');
setIsHasModelFkId(true);
} else {
setIsHasModelFkId(false);
}
}, []);
return (
<StepsForm<{
name: string;
}>
stepsProps={proFormStepsFormProps.stepsProps}
current={current}
onCurrentChange={setCurrent}
formProps={{
validateMessages: {
required: '此项为必填项',
},
}}
stepsFormRender={(dom, submitter) => {
return (
<Modal
className="gn_model_steps_form"
title={
<FormattedMessage id="base_model.stepForm.add.title" defaultMessage="基础模型配置" />
}
width={proFormSmallModelWidth}
onCancel={() => {
setCurrent(0);
formRef.current?.resetFields();
setFileList([]);
props.handleModal();
}}
open={props.createModalOpen}
footer={submitter}
destroyOnClose
>
{dom}
</Modal>
);
}}
>
{/* 参数配置 businessConfig */}
<StepsForm.StepForm<{
config: string;
}>
className="gn_form"
name="businessConfig"
title={
<FormattedMessage id="base_model.stepForm.businessConfig" defaultMessage="参数配置" />
}
stepProps={{
description: (
<FormattedMessage
id="base_model.stepForm.businessConfig.description"
defaultMessage="参数配置"
/>
),
}}
onFinish={async (values: any) => {
setFileList([]);
let formData = formRef.current?.getFieldsValue();
if (formData?.modelFkId) {
await waitTime(500);
formData.modelConfig = { params: values?.params || [] };
if (filePath) {
formData.path = filePath;
}
// postModelVersionCreateModelVersion(formData)
// .then(() => {
// message.success(
// intl.formatMessage({ id: 'common.success', defaultMessage: '$$$' }),
// );
// props.handleModal();
// props.reload();
// })
// .catch(() => {
// message.error(intl.formatMessage({ id: 'common.failure', defaultMessage: '$$$' }));
// return false;
// });
}
return true;
}}
>
{/* // TODO 上传文案需要和UI稿对比 */}
<ProFormUploadDragger
width={proFormSmallItemStyleProps.width}
max={1}
label={<span className="font-bold"></span>}
value={fileList}
name="dragger"
fieldProps={{
onChange: handleFileChange,
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;
});
}
},
}}
/>
{/* // TODO label字重与上面统一, 操作按钮需要与输入框对齐 */}
<ProFormList
name="params"
label={
<div>
{' '}
<span className="font-bold"></span>
<p style={{ color: '#666666' }}>
~
</p>
</div>
}
actionRef={actionFormListRef}
itemContainerRender={(doms) => {
return <ProForm.Group>{doms}</ProForm.Group>;
}}
alwaysShowItemLabel
>
{() => {
return (
<>
<ProFormText key="name" name="name" label="键名" />
<ProFormText key="default" name="default" label="默认值" />
</>
);
}}
</ProFormList>
</StepsForm.StepForm>
{/* 运行库选择 runtimeLib */}
<StepsForm.StepForm<{
name: string;
}>
className="gn_form"
name="runtimeLib"
formRef={formRef}
title={<FormattedMessage id="base_model.stepForm.runtimeLib" defaultMessage="版本信息" />}
stepProps={{
description: (
<FormattedMessage
id="base_model.stepForm.runtimeLib.description"
defaultMessage="填写版本基础信息"
/>
),
}}
onFinish={async () => {
// setFormData(formRef.current?.getFieldsValue());
await waitTime(500);
return true;
}}
>
<ProForm.Group>
<ProFormText
width={proFormSmallItemStyleProps.column2Width - 40}
name="ip"
initialValue={'http://127.0.0.1'}
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 - 40}
name="port"
initialValue={'80'}
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"
/>
),
},
]}
/>
<Button
className="search_ip_port_btn"
style={{ marginTop: '30px' }}
onClick={() => {
const { ip, port } = formRef.current?.getFieldsValue();
if (ip && port) {
// 访问接口拿数据
setOpenFiles(true);
} else {
message.error('请填写IP和端口');
}
console.log(formRef.current?.getFieldsValue(), 'searchIP');
}}
>
</Button>
</ProForm.Group>
{openFiles ? (
<ProFormSelect
width={proFormSmallItemStyleProps.width}
name="fileName"
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 { data } = await getModelRuntimeLibFilesList();
return data?.results?.map((v: Record<string, any>) => {
return { ...v, label: v.name, value: v.id };
});
}}
rules={[
{
required: true,
message: (
<FormattedMessage
id="model_runtimeLib.list.table.form.rule.required.fileName"
defaultMessage="name is required"
/>
),
},
]}
/>
) : (
<></>
)}
</StepsForm.StepForm>
{/* 模型参数配置 modelConfig */}
<StepsForm.StepForm<{
config: string;
}>
className="gn_form"
name="modelConfig"
title={<FormattedMessage id="base_model.stepForm.modelConfig" defaultMessage="参数配置" />}
stepProps={{
description: (
<FormattedMessage
id="base_model.stepForm.modelConfig.description"
defaultMessage="参数配置"
/>
),
}}
onFinish={async (values: any) => {
setFileList([]);
let formData = formRef.current?.getFieldsValue();
if (formData?.modelFkId) {
await waitTime(500);
formData.modelConfig = { params: values?.params || [] };
if (filePath) {
formData.path = filePath;
}
// postModelVersionCreateModelVersion(formData)
// .then(() => {
// message.success(
// intl.formatMessage({ id: 'common.success', defaultMessage: '$$$' }),
// );
// props.handleModal();
// props.reload();
// })
// .catch(() => {
// message.error(intl.formatMessage({ id: 'common.failure', defaultMessage: '$$$' }));
// return false;
// });
}
return true;
}}
>
{/* // TODO 上传文案需要和UI稿对比 */}
<ProFormUploadDragger
width={proFormSmallItemStyleProps.width}
max={1}
label={<span className="font-bold"></span>}
value={fileList}
name="dragger"
fieldProps={{
onChange: handleFileChange,
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;
});
}
},
}}
/>
{/* // TODO label字重与上面统一, 操作按钮需要与输入框对齐 */}
<ProFormList
name="params"
label={
<div>
{' '}
<span className="font-bold"></span>
<p style={{ color: '#666666' }}>~</p>
</div>
}
actionRef={actionFormListRef}
itemContainerRender={(doms) => {
return <ProForm.Group>{doms}</ProForm.Group>;
}}
alwaysShowItemLabel
>
{() => {
return (
<>
<ProFormText key="name" name="name" label="键名" />
<ProFormText key="default" name="default" label="默认值" />
</>
);
}}
</ProFormList>
</StepsForm.StepForm>
</StepsForm>
);
};
export default CreateModelParams;

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-22 15:23:36
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-30 15:00:07
* @LastEditTime: 2024-05-06 17:00:52
* @FilePath: \general-ai-platform-web\src\pages\Business\DeviceGroup\components\deviceList.tsx
* @Description: deviceGroupdg
* @
@ -10,6 +10,7 @@
* 4
* 5122
*/
import InnerPageBack from '@/components/Back/innerPageBack';
import { getDictDeviceType } from '@/services/testApi/dict';
import TableActionCard from '@/components/TableActionCard';
@ -24,19 +25,22 @@ import { proTablePaginationOptions } from '../../../../../config/defaultTable';
import CategorizeUpdate from '@/components/CategorizeUpdate';
import IsDelete from '@/components/TableActionCard/isDelete';
import CreateDeviceForm from './createDeviceForm';
import ModelSetting from './modelSetting';
type DeviceListProps = {
info: Record<string, any>;
};
const DeviceList: React.FC<DeviceListProps> = () => {
/**state */
const access = useAccess();
// eslint-disable-next-line react-hooks/rules-of-hooks
const intl = useIntl();
const actionRef = useRef<ActionType>();
const [createModalOpen, setCreateModalOpen] = useState<boolean>(false);
// const [categoryFkIdIds, setCategoryFkIdIds] = useState([]);
const [currentRow, setCurrentRow] = useState<Record<string, any>>({});
// 动态设置每页数量
const [currentPageSize, setCurrentPageSize] = useState<number>(10);
// 设置分类
@ -46,6 +50,10 @@ const DeviceList: React.FC<DeviceListProps> = () => {
const handleSetDeviceType = () => {
setDeviceTypeOpen(!deviceTypeOpen);
};
// 基础模型设置 isSettingOpen
const [isSettingOpen, setIsSettingOpen] = useState<boolean>(false);
/**新增 编辑 删除 */
// 新增
const handleCreateModal = () => {
@ -125,7 +133,9 @@ const DeviceList: React.FC<DeviceListProps> = () => {
type="link"
size="small"
onClick={() => {
// setCurrentRow(record);
setCurrentRow(record);
setIsSettingOpen(true);
// history.push('/home/model-detail');
// doToDetail(record);
// setShowDetail(true);
@ -177,6 +187,9 @@ const DeviceList: React.FC<DeviceListProps> = () => {
return (
<div className="dg_deviceList_wrap">
<ProTable
style={{
display: isSettingOpen ? 'none' : 'block',
}}
className="gn_pro_table"
cardProps={{
bodyStyle: { padding: 0 },
@ -256,6 +269,22 @@ const DeviceList: React.FC<DeviceListProps> = () => {
}}
columns={columns}
/>
{isSettingOpen ? (
<>
<InnerPageBack
backType="custom"
toBack={() => {
setIsSettingOpen(false);
}}
size={14}
title="模型列表"
></InnerPageBack>
<ModelSetting info={currentRow}></ModelSetting>
</>
) : (
<></>
)}
<CreateDeviceForm
createModalOpen={createModalOpen}
handleModal={handleCreateModal}

@ -0,0 +1,184 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-30 10:02:29
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-05-06 16:58:57
* @FilePath: \general-ai-platform-web\src\pages\Business\DeviceGroup\components\modelDeploy.tsx
* @Description:
* @
* 1
* 2
*/
import TableActionCard from '@/components/TableActionCard';
import { getBusinessModelList } from '@/services/testApi/businessModel';
import { ExclamationCircleFilled } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components';
import { FormattedMessage } from '@umijs/max';
import { Button } from 'antd';
import { useRef, useState } from 'react';
import {
proTableCommonOptions,
proTablePaginationOptions,
} from '../../../../../config/defaultTable';
// import CreateForm from './components/createForm';
const ModelDeploy: React.FC = () => {
// const intl = useIntl();
const actionRef = useRef<ActionType>();
// const [createModalOpen, setCreateModalOpen] = useState<boolean>(false);
// const [categoryFkIdIds, setCategoryFkIdIds] = useState([]);
// 动态设置每页数量
const [currentPageSize, setCurrentPageSize] = useState<number>(10);
// const [currentRow, setCurrentRow] = useState<Record<string, any>>({});
/**配置参数 */
// function reloadList() {
// actionRef.current?.reload();
// }
// 业务模型列表信息
const columns: ProColumns<Record<string, any>>[] = [
{
title: <FormattedMessage id="business_model.table.list.name" defaultMessage="业务模型名称" />,
dataIndex: 'name',
hideInSearch: true,
key: 'fixedName',
fixed: 'left',
},
{
title: <FormattedMessage id="business_model.table.list.isEnable" defaultMessage="部署状态" />,
dataIndex: 'isEnable',
hideInSearch: true,
// width: 80,
render: (dom, record) => {
return (
<div
className={`model_index_type_tag flex items-center justify-center ${
record.isEnable ? 'active1' : 'active2'
}`}
>
<span className="dot"></span>
<span>
{record.isEnable ? (
<FormattedMessage
id="business_model.table.list.deployed.isEnable"
defaultMessage="已部署"
/>
) : (
<FormattedMessage
id="business_model.table.list.undeployed.isEnable"
defaultMessage="未部署"
/>
)}
</span>
</div>
);
},
},
{
title: (
<FormattedMessage id="business_model.table.list.createTime" defaultMessage="创建时间" />
),
dataIndex: 'createTime',
hideInSearch: true,
valueType: 'dateTime',
},
{
title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
key: 'option',
render: () => [
<TableActionCard
key="TableActionCardRef"
renderActions={[
{
key: 'updateDetail',
renderDom: (
<Button
key="updateDetail"
type="link"
size="small"
onClick={() => {
// TODO 编辑在新增联调后实现
// setCurrentRow(record);
}}
>
<FormattedMessage id="pages.searchTable.updateDetail" defaultMessage="配置参数" />
</Button>
),
},
]}
></TableActionCard>,
],
},
];
return (
<div className="dg_modelDeploy_page">
<div
className="pb-[12px]"
style={{
color: '#FAAD14',
}}
>
<ExclamationCircleFilled />
<span className="pl-[8px]"></span>
</div>
<ProTable
className="gn_pro_table"
cardProps={{
bodyStyle: { padding: 0 },
}}
// 标题栏
search={false}
scroll={{ y: proTableCommonOptions.commscrollY }}
options={{ fullScreen: false, setting: false, density: false, reload: false }}
actionRef={actionRef}
rowKey="key"
onDataSourceChange={(data) => {
console.log(data, 'onDataSourceChange_data');
// let CategoryFkIdIds: any = data.map((v) => {
// return v.categoryFkId;
// });
// setCategoryFkIdIds(CategoryFkIdIds);
}}
pagination={{
...proTablePaginationOptions,
pageSize: currentPageSize,
onChange: (page, pageSize) => setCurrentPageSize(pageSize),
}}
columnsState={{
persistenceKey: 'algorithm_model_list',
persistenceType: 'localStorage',
}}
request={async (params = {}) => {
const { current, ...rest } = params;
const reqParams = {
page: current,
...rest,
};
let resp = await getBusinessModelList({ ...reqParams });
console.log(resp, 'getModelVersionList_resp');
return {
data: resp.data?.results.map((v: Record<string, any>) => {
return { ...v, key: v.id };
}),
success: resp.success,
total: resp.data.count,
current: current,
pageSize: currentPageSize,
};
}}
columns={columns}
/>
</div>
);
};
export default ModelDeploy;

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-30 10:02:29
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-30 17:01:59
* @LastEditTime: 2024-05-06 16:18:40
* @FilePath: \general-ai-platform-web\src\pages\Business\DeviceGroup\components\modelSetting.tsx
* @Description:
* @
@ -10,58 +10,77 @@
* 2
*/
import TableActionCard from '@/components/TableActionCard';
import { getBusinessModelList } from '@/services/testApi/businessModel';
import { ExclamationCircleFilled } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components';
import { getModelGroupBaseModelList } from '@/services/testApi/model';
import type { ProColumns } from '@ant-design/pro-components';
import { ProList, ProTable } from '@ant-design/pro-components';
import { FormattedMessage } from '@umijs/max';
import { Button } from 'antd';
import { useRef, useState } from 'react';
import {
proTableCommonOptions,
proTablePaginationOptions,
} from '../../../../../config/defaultTable';
import { useEffect, useState } from 'react';
// import CreateForm from './components/createForm';
import { isSuccessApi } from '@/utils/forApi';
import CreateModelParams from './createModelParams';
type ModelSettingProps = {
info: Record<string, any>;
};
const ModelSetting: React.FC<ModelSettingProps> = () => {
/**state */
const [modelData, setModelData] = useState<Record<string, any>[]>([]); // 列表数据
const ModelSetting: React.FC = () => {
// const intl = useIntl();
const actionRef = useRef<ActionType>();
// const [createModalOpen, setCreateModalOpen] = useState<boolean>(false);
const [createModalOpen, setCreateModalOpen] = useState<boolean>(false);
// const [categoryFkIdIds, setCategoryFkIdIds] = useState([]);
// 动态设置每页数量
const [currentPageSize, setCurrentPageSize] = useState<number>(10);
// const [currentRow, setCurrentRow] = useState<Record<string, any>>({});
/**配置参数 */
// function reloadList() {
// actionRef.current?.reload();
// }
const handleCreateModal = () => {
setCreateModalOpen(!createModalOpen);
};
// 业务模型列表信息
const columns: ProColumns<Record<string, any>>[] = [
{
title: <FormattedMessage id="business_model.table.list.name" defaultMessage="业务模型名称" />,
title: <FormattedMessage id="base_model.table.list.name" defaultMessage="基础模型名称" />,
dataIndex: 'name',
hideInSearch: true,
key: 'fixedName',
fixed: 'left',
},
{
title: <FormattedMessage id="business_model.table.list.status" defaultMessage="部署状态" />,
dataIndex: 'status',
title: <FormattedMessage id="base_model.table.list.version" defaultMessage="版本" />,
dataIndex: 'version',
hideInSearch: true,
width: '20%',
},
{
title: (
<FormattedMessage id="business_model.table.list.createTime" defaultMessage="创建时间" />
<FormattedMessage id="base_model.table.list.runtimeLibFile" defaultMessage="运行库镜像" />
),
dataIndex: 'createTime',
dataIndex: 'runtimeLibFile',
hideInSearch: true,
valueType: 'dateTime',
render: (dom, record) => {
return (
<div>
{record.runtimeLibFile ? (
dom
) : (
<div className={`model_index_type_tag flex items-center justify-center active2`}>
<span className="dot"></span>
<span>
<FormattedMessage
id="base_model.table.list.undeployed.runtimeLibFile"
defaultMessage="未部署"
/>
</span>
</div>
)}
</div>
);
},
},
{
title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
dataIndex: 'option',
@ -73,18 +92,20 @@ const ModelSetting: React.FC = () => {
key="TableActionCardRef"
renderActions={[
{
key: 'updateDetail',
key: 'setConfig',
renderDom: (
<Button
key="updateDetail"
key="setConfig"
type="link"
size="small"
onClick={() => {
// TODO 编辑在新增联调后实现
// setCurrentRow(record);
handleCreateModal();
}}
>
<FormattedMessage id="pages.searchTable.updateDetail" defaultMessage="配置参数" />
<FormattedMessage
id="base_model.table.list.action.config"
defaultMessage="配置参数"
/>
</Button>
),
},
@ -93,63 +114,102 @@ const ModelSetting: React.FC = () => {
],
},
];
// 将数据组装成reactDom
function toListDomByData(record) {
let startList = [...record];
let finalList = startList.map((item) => ({
content: (
<div>
<div className="gn_list_card_title pb-[12px]">
<span></span>
<span>{item.name}</span>
</div>
<ProTable
className="gn_pro_table mb-[16px]"
// headerTitle={
// <div>
// <span></span>
// <span>{item.name}</span>
// </div>
// }
cardProps={{
bodyStyle: { padding: 0, margin: 0 },
}}
// 标题栏
search={false}
options={{ fullScreen: false, setting: false, density: false, reload: false }}
rowKey="key"
onDataSourceChange={(data) => {
console.log(data, 'onDataSourceChange_data');
}}
pagination={false}
columnsState={{
persistenceKey: 'algorithm_model_list',
persistenceType: 'localStorage',
}}
dataSource={item.list}
columns={columns}
/>
</div>
),
}));
setModelData(finalList);
console.log(finalList, 'toListDomByData_finalList');
}
// 基础模型列表数据api
async function fetchData() {
const resp = await getModelGroupBaseModelList();
if (isSuccessApi(resp)) {
toListDomByData(resp.data.results);
}
}
function reloadList() {
fetchData();
}
// 初始化加载
useEffect(() => {
fetchData();
}, []);
return (
<div className="modelSetting_page">
<div
className="pb-[12px]"
style={{
color: '#FAAD14',
<div className="dg_modelSetting_page">
<ProList<{ title: string }>
itemLayout="vertical"
itemCardProps={{
ghost: true,
bodyStyle: { padding: 0, margin: 0 },
style: {
width: '100%',
border: 0,
},
}}
>
<ExclamationCircleFilled />
<span className="pl-[8px]"></span>
</div>
<ProTable
className="gn_pro_table"
cardProps={{
bodyStyle: { padding: 0 },
}}
// 标题栏
search={false}
scroll={{ y: proTableCommonOptions.commscrollY }}
options={{ fullScreen: false, setting: false, density: false, reload: false }}
actionRef={actionRef}
rowKey="key"
onDataSourceChange={(data) => {
console.log(data, 'onDataSourceChange_data');
// let CategoryFkIdIds: any = data.map((v) => {
// return v.categoryFkId;
// });
// setCategoryFkIdIds(CategoryFkIdIds);
style: { padding: 0, margin: 0 }, // 设置卡片的内外边距为 0
bodyStyle: {
padding: 0,
margin: 0,
height: 'calc(100vh - 400px)',
overflow: 'scroll',
},
}}
pagination={{
...proTablePaginationOptions,
pageSize: currentPageSize,
onChange: (page, pageSize) => setCurrentPageSize(pageSize),
rowKey="id"
dataSource={modelData}
pagination={false}
rowSelection={false}
metas={{
content: {
style: { margin: 0, padding: 0 },
},
}}
columnsState={{
persistenceKey: 'algorithm_model_list',
persistenceType: 'localStorage',
}}
request={async (params = {}) => {
const { current, ...rest } = params;
const reqParams = {
page: current,
...rest,
};
let resp = await getBusinessModelList({ ...reqParams });
console.log(resp, 'getModelVersionList_resp');
return {
data: resp.data?.results.map((v: Record<string, any>) => {
return { ...v, key: v.id };
}),
success: resp.success,
total: resp.data.count,
current: current,
pageSize: currentPageSize,
};
}}
columns={columns}
/>
<CreateModelParams
createModalOpen={createModalOpen}
handleModal={handleCreateModal}
reload={reloadList}
/>
</div>
);

@ -12,26 +12,35 @@
.deviceGroup_page .node_info {
padding-left: 0;
}
.dg_deviceList_wrap .model_index_type_tag {
.model_index_type_tag {
width: 82px;
height: 24px;
color: #52C41A;
background: #E8F7E6;
border: 1px solid #BAEEA1;
color: #52c41a;
background: #e8f7e6;
border: 1px solid #baeea1;
border-radius: 12px;
}
.dg_deviceList_wrap .model_index_type_tag .dot {
.model_index_type_tag .dot {
width: 6px;
height: 6px;
margin-right: 4px;
background: #52C41A;
background: #52c41a;
border-radius: 50%;
}
.dg_deviceList_wrap .model_index_type_tag.active2 {
color: #E80D0D;
background: #FEEFEE;
border: 1px solid #F9B2AE;
.model_index_type_tag.active2 {
color: #e80d0d;
background: #feefee;
border: 1px solid #f9b2ae;
}
.dg_deviceList_wrap .model_index_type_tag.active2 .dot {
background: #E80D0D;
.model_index_type_tag.active2 .dot {
background: #e80d0d;
}
.dg_modelSetting_page .ant-list-items .ant-list-item {
margin: 0;
padding: 0;
border: none;
}
.dg_modelSetting_page .ant-list-items .ant-list-item .ant-pro-list-row-content {
margin: 0;
padding: 0;
}

@ -17,28 +17,42 @@
}
}
.dg_deviceList_wrap {
.model_index_type_tag {
width: 82px;
height: 24px;
color: #52c41a;
background: #e8f7e6;
border: 1px solid #baeea1;
border-radius: 12px;
.model_index_type_tag {
width: 82px;
height: 24px;
color: #52c41a;
background: #e8f7e6;
border: 1px solid #baeea1;
border-radius: 12px;
.dot {
width: 6px;
height: 6px;
margin-right: 4px;
background: #52c41a;
border-radius: 50%;
}
&.active2 {
color: #e80d0d;
background: #feefee;
border: 1px solid #f9b2ae;
.dot {
width: 6px;
height: 6px;
margin-right: 4px;
background: #52c41a;
border-radius: 50%;
background: #e80d0d;
}
&.active2 {
color: #e80d0d;
background: #feefee;
border: 1px solid #f9b2ae;
.dot {
background: #e80d0d;
}
}
// 基础模型设置
.dg_modelSetting_page {
.ant-list-items {
.ant-list-item {
margin: 0;
padding: 0;
border: none;
.ant-pro-list-row-content {
margin: 0;
padding: 0;
}
}
}

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-22 15:23:36
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-30 14:01:32
* @LastEditTime: 2024-05-06 10:27:14
* @FilePath: \general-ai-platform-web\src\pages\Business\DeviceGroup\index.tsx
* @Description: deviceGroupdg
* @
@ -22,7 +22,7 @@ import AlarmSetForm from './components/alarmSetForm';
import BaseInfo from './components/baseInfo';
import CreateForm from './components/createForm';
import DeviceList from './components/deviceList';
import ModelSetting from './components/modelSetting';
import ModelDeploy from './components/modelDeploy';
import './index.less';
@ -119,7 +119,7 @@ const DeviceGroup: React.FC = () => {
}}
></Tabs>
{tabKey === '1' && <DeviceList info={nodeInfo}></DeviceList>}
{tabKey === '2' && <ModelSetting info={nodeInfo}></ModelSetting>}
{tabKey === '2' && <ModelDeploy info={nodeInfo}></ModelDeploy>}
{tabKey === '3' && (
<AlarmSetForm
currentRow={{

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-08 10:36:06
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-18 15:39:42
* @LastEditTime: 2024-05-06 10:30:32
* @FilePath: \general-ai-manage\src\pages\Model\ModelDetail\index.tsx
* @Description:
* @

@ -147,6 +147,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
]}
/>
<Button
className="search_ip_port_btn"
style={{ marginTop: '30px' }}
onClick={() => {
const { ip, port } = form.getFieldsValue();

@ -2,18 +2,19 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2024-04-10 16:58:27
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2024-04-17 17:56:48
* @LastEditTime: 2024-05-06 13:55:21
* @FilePath: \general-ai-manage\src\services\testApi\model.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
// 模型列表分页
export async function getModelList(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(`/api/model/list/`, {
return request<API.Response & { data?: API.PageResult; msg?: string }>(`/api/model/list`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
@ -24,13 +25,29 @@ export async function getModelList(
...(options || {}),
});
}
// 模型详情
export async function getModelDetail(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(`/api/model/detail`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
params: {
...body,
},
...(options || {}),
});
}
// 模型版本列表
export async function getModelVersionList(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(
`/api/model/versions/list/`,
`/api/model/versions/list`,
{
method: 'GET',
headers: {
@ -43,29 +60,32 @@ export async function getModelVersionList(
},
);
}
export async function getModelDetail(
// 模型运行库列表分页
export async function getModelRuntimeLibList(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(`/api/model/detail/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
params: {
...body,
return request<API.Response & { data?: API.PageResult; msg?: string }>(
`/api/model/runtimeLib/list`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
params: {
...body,
},
...(options || {}),
},
...(options || {}),
});
);
}
export async function getModelRuntimeLibList(
// 模型运行库文件列表
export async function getModelRuntimeLibFilesList(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(
`/api/model/runtimeLib/list/`,
`/api/model/runtimeLib/filesList`,
{
method: 'GET',
headers: {
@ -78,13 +98,13 @@ export async function getModelRuntimeLibList(
},
);
}
export async function getModelRuntimeLibFilesList(
// 节点下基础模型列表
export async function getModelGroupBaseModelList(
body: Record<string, any>, //
options?: { [key: string]: any },
) {
return request<API.Response & { data?: API.PageResult; msg?: string }>(
`/api/model/runtimeLib/filesList/`,
`/api/model/groupBaseModel/list`,
{
method: 'GET',
headers: {

Loading…
Cancel
Save