feat: 完善告警模块完成,告警图文展示功能优化完成

develop
zhoux 1 year ago
parent e3307046be
commit 367df37cda

@ -2,15 +2,26 @@
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-11-13 14:19:57
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-11-22 13:15:04
* @LastEditTime: 2023-12-27 16:09:07
* @FilePath: \general-ai-platform-web\config\defaultForm.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE2
*/
import { CloseCircleOutlined, CopyOutlined, DeleteOutlined } from "@ant-design/icons";
import { StepsFormProps } from "@ant-design/pro-components";
import { ReactNode } from "react";
export type IconConfig = {
/**
* icon
* Icon: ()=> <div/>
*/
Icon?: React.FC<any>;
/**
* tooltip
*/
tooltipText?: string;
};
// 通用表单配置
export const proFormCommonOptions: Record<string,any> = {
}
@ -22,6 +33,8 @@ export const proFormSmallModelWidth: number = 560;
export const proFormSmallItemStyleProps: Record<string, any> = {
width: proFormSmallModelWidth - formBoxMargin, // 一列
// column2Width: (proFormSmallModelWidth - 2 * formBoxMargin)/2 , // 两列
};
// normal 804
@ -46,9 +59,28 @@ export const proFormMaxItemStyleProps: Record<string, any> = {
export const proFormListCreatorButtonProps : {
creatorButtonText?: ReactNode;
position?: 'top' | 'bottom';
deleteIconProps?: IconConfig | false;
} = {
position: 'bottom',
creatorButtonText: '添加参数字段', // 设置新增一项数据的文案
deleteIconProps: {
Icon: CloseCircleOutlined,
tooltipText: '不需要这行了',
}
}
export const proFormListActionButtonProps : {
CopyableIconProps?: IconConfig | false;
deleteIconProps?: IconConfig | false;
} = {
CopyableIconProps: {
Icon: CopyOutlined,
tooltipText: '复制',
},
deleteIconProps: {
Icon: DeleteOutlined,
tooltipText: '删除',
}
}

@ -0,0 +1,16 @@
/*
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-12-26 15:51:09
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-26 15:51:18
* @FilePath: \general-ai-platform-web\config\defaultStyle.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export const flex: React.CSSProperties = {
display: 'flex'
}
export const flexRA: React.CSSProperties = {
...flex,
}

@ -0,0 +1,332 @@
/**
* @name umi
* @description path,components,routes,redirect,wrappers,name,icon
* @param path path :id *
* @param components location path React src/pages
* @param routes layout 使
* @param redirect
* @param wrappers
* @param name menu.ts menu.xxxx name login menu.ts menu.login
* @param icon https://ant.design/components/icon-cn 注意去除风格后缀和大小写,如想要配置图标为 <StepBackwardOutlined /> 则取值应为 stepBackward 或 StepBackward如想要配置图标为 <UserOutlined /> 则取值应为 user 或者 User
* @doc https://umijs.org/docs/guides/routes
*/
export default [
{
path: '/user',
layout: false,
routes: [
{
name: 'login',
path: '/user/login',
component: './User/Login/index1',
},
],
},
{
path: '/welcome',
name: 'welcome',
component: 'Welcome/Welcome',
},
{
path: '/admin',
name: 'admin',
access: 'canAdmin',
routes: [
{
path: '/admin',
redirect: '/admin/sub-page',
},
{
path: '/admin/sub-page',
name: 'sub-page',
component: './Admin',
},
],
},
{
path: '/system',
name: 'system',
routes: [
{
name: 'api-list',
path: '/system/api-list',
component: 'System/ApiList',
access: 'canReadMenu',
},
{
name: 'menu-list',
path: '/system/menu-list',
component: 'System/MenuList',
access: 'canReadMenu',
},
{
name: 'role-list',
path: '/system/role-list',
component: 'System/RoleList',
access: 'canReadMenu',
},
{
name: 'user-list',
path: '/system/user-list',
component: 'System/UserList',
access: 'canReadMenu',
},
{
name: 'post-list',
path: '/system/post-list',
component: 'System/PostList',
access: 'canReadMenu',
},
{
name: 'department-list',
path: '/system/department-list',
component: 'System/DepartmentList',
access: 'canReadMenu',
},
{
name: 'operation_record-list',
path: '/system/operation_record-list',
component: 'System/OperationRecordList',
access: 'canReadMenu',
},
],
},
{
name: 'device',
path: '/device',
routes: [
{
name: 'device-list',
path: '/device/device-list',
component: 'Device/DeviceList',
access: 'canReadMenu',
},
{
name: 'device-category-list',
path: '/device/device-category-list',
component: 'Device/DeviceCategoryList',
access: 'canReadMenu',
},
{
name: 'device-group-list',
path: '/device/device-group-list',
component: 'Device/DeviceGroupList',
access: 'canReadMenu',
},
{
name: 'device-relation-list',
path: '/device/device-relation-list',
component: 'Device/DeviceRelationList',
access: 'canReadMenu',
},
],
},
{
name: 'DCSDevice',
path: '/DCSDevice',
routes: [
{
name: 'DCSDevice-device-group-list',
path: '/DCSDevice/device-group-list',
component: 'DCSDevice/DeviceGroupList',
access: 'canReadMenu',
},
{
name: 'DCSDevice-device-category-list',
path: '/DCSDevice/device-category-list',
component: 'DCSDevice/DeviceCategoryList',
access: 'canReadMenu',
},
{
name: 'DCSDevice-device-list',
path: '/DCSDevice/device-list',
component: 'DCSDevice/DeviceList',
access: 'canReadMenu',
},
{
name: 'DCSDevice-device-status',
path: '/DCSDevice/device-status',
component: 'DCSDevice/DeviceStatus',
access: 'canReadMenu',
},
],
},
{
name: 'resource',
path: '/resource',
routes: [
{
name: 'algorithm-model-list',
path: '/resource/algorithm-model-list',
component: 'Resource/AlgorithmModelList',
access: 'canReadMenu',
},
{
name: 'algorithm-model-detail',
path: '/resource/algorithm-model-detail/:id',
component: 'Resource/AlgorithmModelList/detail',
access: 'canReadMenu',
isHideTab: true,
},
{
name: 'business-image-list',
path: '/resource/business-image-list',
component: 'Resource/BusinessImageList',
access: 'canReadMenu',
},
{
name: 'model-category-list',
path: '/resource/model-category-list',
component: 'Resource/ModelCategoryList',
access: 'canReadMenu',
},
{
name: 'model-image-list',
path: '/resource/model-image-list',
component: 'Resource/ModelImageList',
access: 'canReadMenu',
},
{
name: 'model-version-list',
path: '/resource/model-version-list',
component: 'Resource/ModelVersionList',
access: 'canReadMenu',
},
{
name: 'resource-device-status',
path: '/resource/resource-device-status',
component: 'Resource/ResourceDeviceStatus',
access: 'canReadMenu',
},
],
},
{
name: 'analysis',
path: '/analysis',
routes: [
{
name: 'action-detection-list',
path: '/analysis/action-detection-list',
component: 'Analysis/ActionDetectionList',
access: 'canReadMenu',
},
],
},
{
name: 'project',
path: '/project',
routes: [
{
name: 'project-list',
path: '/project/project-list',
component: 'Project/ProjectList',
access: 'canReadMenu',
},
],
},
{
name: 'Contact',
path: '/Contact',
routes: [
{
name: 'Contact-contact-list',
path: '/Contact/contact-list',
component: 'Contact/ContactList',
access: 'canReadMenu',
},
],
},
{
name: 'task',
path: '/task',
component: 'Hidden',
},
{
name: 'compute_power',
path: '/compute_power',
component: 'ComputePowerAllocation/ComputePowerAllocation',
},
{
name: 'data_screen',
path: '/data_screen',
},
{
name: 'algorithm_setting',
path: '/algorithm_setting',
component: 'Setting/AlgorithmSetting',
},
{
name: 'alarm',
path: '/alarm',
routes: [
{
name: 'alarm-list',
path: '/alarm/alarm-list',
component: 'Alarm/AlarmList',
access: 'canReadMenu',
},
{
name: 'alarm-setting',
path: '/alarm/alarm-setting',
component: 'Alarm/AlarmSetting',
access: 'canReadMenu',
},
{
name: 'alarm-ways',
path: '/alarm/alarm-ways',
component: 'Alarm/AlarmWays',
access: 'canReadMenu',
},
],
},
{
name: 'logging',
path: 'http://192.168.10.96:5601/app/r/s/uDpRg',
},
{
path: '/',
redirect: '/welcome',
},
{
path: '*',
layout: false,
component: './404',
},
{
name: 'fabricView',
path: '/fabricView',
routes: [
{
name: 'fabricView-room',
path: '/fabricView/room',
component: 'FabricView/Room',
access: 'canReadMenu',
},
],
},
{
name: 'notice',
path: '/notice',
component: 'Notice',
},
{
name: 'account',
path: '/account',
routes: [
{
name: 'center',
path: '/account/center',
component: 'Account/Center',
access: 'canReadMenu',
},
],
},
];

@ -286,6 +286,12 @@ export default [
component: 'Alarm/AlarmSetting',
access: 'canReadMenu',
},
{
name: 'alarm-ways',
path: '/alarm/alarm-ways',
component: 'Alarm/AlarmWays',
access: 'canReadMenu',
},
],
},
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

@ -0,0 +1,65 @@
/*
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-12-29 14:19:46
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-29 15:31:14
* @FilePath: \general-ai-platform-web\src\components\Loading\loadingSpin.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
type LoadingSpinProps = {
spinning: boolean;
tip?: string | React.ReactNode;
indicator?: React.ReactNode;
};
/**
* @
* @param props
* @returns
*/
const LoadingSpin: React.FC<LoadingSpinProps> = (props) => {
return props.spinning ? (
<div
style={{
background: 'transparent',
position: 'fixed',
left: 0,
top: 0,
width: '100vw',
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 999,
backgroundColor: 'rgba(0,0,0, 0.3)',
color: 'white',
}}
>
<div
style={{
backgroundColor: 'white',
borderRadius: 12,
width: 361,
height: 216,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
color: '#154DDD'
}}
>
{props.indicator ? (
props.indicator
) : (
<img style={{ width: 120 }} src="/loading1.gif" alt="" />
)}
<p style={{padding: 8, margin: 0}}>{props.tip || '请稍候...'}</p>
</div>
</div>
) : (
<></>
);
};
export default LoadingSpin;

@ -0,0 +1,45 @@
/*
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-12-27 10:30:10
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-27 11:06:00
* @FilePath: \general-ai-platform-web\src\components\TableActionCard\isModalDelete.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import modal from 'antd/es/modal';
type IsModalDeleteProps = {
deleteButton: React.ReactNode;
deleteApi: () => void;
titleText?: string;
contentText?: string;
};
const IsModalDelete: React.FC<IsModalDeleteProps> = (props) => {
const intl = useIntl();
const confirm = () => {
modal.confirm({
title: props.titleText || `确认删除吗?`,
icon: <ExclamationCircleOutlined />,
content: props.contentText || '确认删除吗?删除后将无法找回,请谨慎操作。',
okText: intl.formatMessage({ id: 'common.okText', defaultMessage: '$$$' }),
cancelText: intl.formatMessage({ id: 'common.cancelText', defaultMessage: '$$$' }),
onOk() {
props.deleteApi();
},
onCancel() {
console.log('Cancel');
},
});
};
return (
<>
<span onClick={() => confirm()}>{props.deleteButton}</span>
</>
);
};
export default IsModalDelete;

@ -166,7 +166,7 @@ a.ant-dropdown-trigger {
.ant-modal-body .ant-pro-steps-form-steps-container {
max-width: 1900px !important;
}
/* 列表table && proTable */
/* 列表 ProTable && ProList */
.ant-pro-table .ant-pro-table-list-toolbar-left {
flex: 0.3;
}
@ -181,6 +181,22 @@ a.ant-dropdown-trigger {
.ant-pro-list .ant-pro-table-list-toolbar-left {
flex: 1;
}
/*表单 ProFrom */
.ant-btn-dashed {
border-radius: 4px;
border: 1px dashed #154DDD;
background: rgba(21, 91, 212, 0.05);
color: #154DDD;
}
.ant-pro-form-list-action {
margin-block-end: 16px;
}
.ant-pro-form-list .ant-pro-form-list-action .action-copy {
color: #154DDD;
}
.ant-pro-form-list .ant-pro-form-list-action .action-remove {
color: #E80D0D;
}
.ant-popover .ant-popover-content {
min-width: 200px;
}
@ -360,6 +376,11 @@ a.ant-dropdown-trigger {
background: rgba(21, 77, 221, 0.1);
color: #154DDD;
}
.gn.tabAction_box_wrap .ant-btn-primary {
border: 1px solid #154DDD;
background: rgba(21, 77, 221, 0.1);
color: #154DDD;
}
.ant-pro-global-header-logo img {
height: 48px;
}

@ -204,7 +204,7 @@ a.ant-dropdown-trigger{
.ant-modal-body .ant-pro-steps-form-steps-container{
max-width: 1900px !important;
}
/* 列表table && proTable */
/* 列表 ProTable && ProList */
// proTable
.ant-pro-table{
// 标题栏左侧文字最大弹性占比
@ -221,8 +221,7 @@ a.ant-dropdown-trigger{
}
}
// proList
// proList
.ant-pro-list{
.ant-pro-query-filter.ant-pro-query-filter {
background-color: white;
@ -233,7 +232,29 @@ a.ant-dropdown-trigger{
.ant-pro-table-list-toolbar-left{
flex: 1
}
}
/*表单 ProFrom */
.ant-btn-dashed{
border-radius: 4px;
border: 1px dashed #154DDD;
background: rgba(21, 91, 212, 0.05);
color: #154DDD;
}
.ant-pro-form-list-action {
margin-block-end: 16px;
}
// ProFormList
.ant-pro-form-list{
.ant-pro-form-list-action{
.action-copy{
color: #154DDD;
}
.action-remove {
color: #E80D0D;
}
}
}
// 气泡框
@ -475,6 +496,15 @@ a.ant-dropdown-trigger{
// }
// }
}
// 自定义切换按钮样式
&.tabAction_box_wrap{
.ant-btn-primary{
border: 1px solid #154DDD;
background: rgba(21, 77, 221, 0.10);
color: #154DDD;
}
}
}
.ant-pro-global-header-logo img {
height: 48px;
@ -508,3 +538,6 @@ a.ant-dropdown-trigger{

@ -2,11 +2,10 @@
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-12-18 16:36:36
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-22 16:58:28
* @LastEditTime: 2023-12-27 09:30:24
* @FilePath: \general-ai-platform-web\src\locales\zh-CN\alarm.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 设备列表
export const alarm_list: { [key: string]: string } = {
'alarm.list.table.list.id': 'ID',
@ -29,6 +28,23 @@ export const alarm_setting: { [key: string]: string } = {
'alarm.setting.table.model.alarmStatusSetting': '告警等级',
'alarm.list.table.list.deviceGroup': '设备组',
'alarm.list.table.list.alarmTime': '告警时间',
'alarm.setting.table.list.rule.required.name': '请填写告警名称',
}
// 设备状态
export const alarm_ways: { [key: string]: string } = {
'alarm.ways.page.name': '告警方式',
'alarm.ways.page.title': '告警方式设置',
'alarm.ways.page.add': '新建告警方式',
'alarm.ways.page.empty.des': '暂无告警方式',
'alarm.ways.page.model.name': '告警方式名称',
'alarm.ways.page.form.serviceName': '第三方服务商',
'alarm.ways.page.form.params': '服务商参数',
'alarm.ways.page.form.template': '通知模板',
'alarm.ways.page.model.rule.required.name': '请填写告警方式名称',
}

@ -2,15 +2,15 @@
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-11-01 13:56:33
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-11-03 16:33:59
* @LastEditTime: 2023-12-27 11:06:30
* @FilePath: \general-ai-platform-web\src\locales\zh-CN\common.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export default {
'common.modal.table.update.title': '更新表单',
'common.modal.table.create.title': '新增表单',
'common.modal.table.delete.title': '确要删除吗?',
'common.modal.table.delete.content': '确删除吗,删除后将无法找回,请谨慎操作',
'common.modal.table.delete.title': '确要删除吗?',
'common.modal.table.delete.content': '确删除吗,删除后将无法找回,请谨慎操作',
'common.enable': '开启',
'common.disable': '禁用',
'common.yes': '是',
@ -46,4 +46,8 @@ export default {
'common.open_failure': '打开失败',
'common.start_process': '开始运行',
'common.finish_process': '关闭运行',
'common.save': '保存',
'common.delete': '删除',
'common.okText': '确认',
'common.cancelText': '取消',
}

@ -1,41 +0,0 @@
import React from "react";
import {Drawer} from "antd";
import {ProColumns, ProDescriptions, ProDescriptionsItemProps} from "@ant-design/pro-components";
export type ColumnDrawProps = {
handleDrawer: (id?: any)=>void;
isShowDetail: boolean;
columns: ProColumns<API.DeviceCategory>[];
currentRow: API.DeviceCategory | undefined;
};
const ColumnDrawer: React.FC<ColumnDrawProps> = (props) => {
return (
<Drawer
width={500}
open={props.isShowDetail}
onClose={() => {
props.handleDrawer();
}}
closable={true}
>
{props.currentRow?.id && (
<ProDescriptions<API.DeviceCategory>
column={2}
title={props.currentRow?.id}
request={async () => ({
data: props.currentRow || {},
})}
params={{
id: props.currentRow?.id,
}}
columns={props.columns as ProDescriptionsItemProps<API.DeviceCategory>[]}
/>
)}
</Drawer>
)
}
export {ColumnDrawer}

@ -1,32 +0,0 @@
import { FormattedMessage} from '@umijs/max';
export const DeviceCategoryColumns = [{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.id"
defaultMessage="$$$"/>),
dataIndex: "id",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.name"
defaultMessage="$$$"/>),
dataIndex: "name",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.code"
defaultMessage="$$$"/>),
dataIndex: "code",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.remark"
defaultMessage="$$$"/>),
dataIndex: "remark",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.createTime"
defaultMessage="$$$"/>),
dataIndex: "create_time",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.updateTime"
defaultMessage="$$$"/>),
dataIndex: "update_time",
},]

@ -1,143 +0,0 @@
import { postDeviceCategoryCreateDeviceCategory } from '@/services/device/DeviceCategory';
import { ModalForm, ProForm, ProFormText } from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, Switch, message } from 'antd';
import React, { useState } from 'react';
import {
proFormSmallItemStyleProps,
proFormSmallModelWidth,
} from '../../../../../config/defaultForm';
export type FormValueType = {
target?: string;
template?: string;
type?: string;
time?: string;
frequency?: string;
} & Partial<API.DeviceCategory>;
export type CreateFormProps = {
createModalOpen: boolean;
handleModal: () => void;
values: Partial<API.DeviceCategory>;
reload: any;
};
const CreateForm: React.FC<CreateFormProps> = (props) => {
const intl = useIntl();
const [isAuto, setIsAuto] = useState(true);
const [form] = Form.useForm<API.DeviceCategory>();
return (
<ModalForm<API.DeviceCategory>
width={proFormSmallModelWidth}
title={intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.add',
defaultMessage: '$$$',
})}
open={props.createModalOpen}
form={form}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => props.handleModal(),
}}
submitTimeout={2000}
onFinish={async (values) => {
postDeviceCategoryCreateDeviceCategory(values)
.then(() => {
message.success(intl.formatMessage({ id: 'common.success', defaultMessage: '$$$' }));
props.reload();
})
.catch(() => {
message.error(intl.formatMessage({ id: 'common.failure', defaultMessage: '$$$' }));
});
props.handleModal();
return true;
}}
>
<ProForm.Group>
<ProFormText
width={proFormSmallItemStyleProps.width}
name="name"
label={
<FormattedMessage id="DCSDeviceList.device_category.table.list.name" defaultMessage="$$$" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.name',
defaultMessage: '$$$',
})}`}
required={true}
rules={[
{
required: true,
message: (
<FormattedMessage
id="DCSDeviceList.device_category.table.rule.required.name"
defaultMessage="name is required"
/>
),
},
]}
/>
<ProFormText
width="lg"
name="code"
label={
<FormattedMessage id="DCSDeviceList.device_category.table.list.code" defaultMessage="$$$" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.code',
defaultMessage: '$$$',
})}`}
required={!isAuto}
initialValue=""
disabled={isAuto}
rules={
isAuto
? []
: [
{
required: true,
message: (
<FormattedMessage
id="DCSDeviceList.device_category.table.rule.required.code"
defaultMessage="code is required"
/>
),
},
]
}
addonAfter={
<Switch
checked={isAuto}
checkedChildren={<FormattedMessage id="common.auto" defaultMessage="$$$" />}
unCheckedChildren={<FormattedMessage id="common.edit" defaultMessage="$$$" />}
onChange={setIsAuto}
/>
}
/>
<ProFormText
width={proFormSmallItemStyleProps.width}
name="remark"
label={
<FormattedMessage id="DCSDeviceList.device_category.table.list.remark" defaultMessage="$$$" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.remark',
defaultMessage: '$$$',
})}`}
required={false}
/>
</ProForm.Group>
</ModalForm>
);
};
export default CreateForm;

@ -1,187 +0,0 @@
/*
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-11-01 13:56:33
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-11-17 10:12:53
* @FilePath: \general-ai-platform-web\src\pages\Device\DeviceCategoryList\components\UpdateForm.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { putDeviceCategoryUpdateDeviceCategory } from '@/services/device/DeviceCategory';
import { ModalForm, ProForm, ProFormDateTimePicker, ProFormText } from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form, message } from 'antd';
import { proFormItemStyleProps, proFormModelWidth } from '../../../../../config/defaultForm';
import React from 'react';
export type FormValueType = {
target?: string;
template?: string;
type?: string;
time?: string;
frequency?: string;
} & Partial<API.DeviceCategory>;
export type UpdateFormProps = {
updateModalOpen: boolean;
handleModal: () => void;
values: Partial<API.DeviceCategory>;
reload: any;
};
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
const intl = useIntl();
const [form] = Form.useForm<API.DeviceCategory>();
return (
<ModalForm<API.DeviceCategory>
width={proFormModelWidth}
title={intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.update',
defaultMessage: '$$$',
})}
open={props.updateModalOpen}
form={form}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => props.handleModal(),
}}
submitTimeout={2000}
onFinish={async (values) => {
putDeviceCategoryUpdateDeviceCategory(values)
.then(() => {
message.success(intl.formatMessage({ id: 'common.success', defaultMessage: '$$$' }));
props.reload();
})
.catch(() => {
message.error(intl.formatMessage({ id: 'common.failure', defaultMessage: '$$$' }));
});
props.handleModal();
return true;
}}
>
<ProForm.Group>
<ProFormText
width={proFormItemStyleProps.column2Width}
name="id"
label="id"
disabled={true}
initialValue={props.values.id}
/>
<ProFormText
width={proFormItemStyleProps.column2Width}
name="name"
label={
<FormattedMessage id="DCSDeviceList.device_category.table.list.name" defaultMessage="$$$" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.name',
defaultMessage: '$$$',
})}`}
required={true}
initialValue={props.values.name}
disabled={false}
rules={[
{
required: true,
message: (
<FormattedMessage
id="DCSDeviceList.device_category.table.rule.required.name"
defaultMessage="name is required"
/>
),
},
]}
/>
<ProFormText
width={proFormItemStyleProps.column2Width}
name="code"
label={
<FormattedMessage id="DCSDeviceList.device_category.table.list.code" defaultMessage="$$$" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.code',
defaultMessage: '$$$',
})}`}
required={true}
initialValue={props.values.code}
disabled={false}
rules={[
{
required: true,
message: (
<FormattedMessage
id="DCSDeviceList.device_category.table.rule.required.code"
defaultMessage="code is required"
/>
),
},
]}
/>
<ProFormText
width={proFormItemStyleProps.column2Width}
name="remark"
label={
<FormattedMessage id="DCSDeviceList.device_category.table.list.remark" defaultMessage="$$$" />
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.remark',
defaultMessage: '$$$',
})}`}
required={false}
initialValue={props.values.remark}
disabled={false}
/>
<ProFormDateTimePicker
width={proFormItemStyleProps.column2Width}
name="createTime"
label={
<FormattedMessage
id="DCSDeviceList.device_category.table.list.createTime"
defaultMessage="$$$"
/>
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.createTime',
defaultMessage: '$$$',
})}`}
required={false}
initialValue={props.values.createTime}
disabled={true}
/>
<ProFormDateTimePicker
width={proFormItemStyleProps.column2Width}
name="updateTime"
label={
<FormattedMessage
id="DCSDeviceList.device_category.table.list.updateTime"
defaultMessage="$$$"
/>
}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'DCSDeviceList.device_category.table.list.updateTime',
defaultMessage: '$$$',
})}`}
required={false}
initialValue={props.values.updateTime}
disabled={true}
/>
</ProForm.Group>
</ModalForm>
);
};
export default UpdateForm;

@ -1,32 +0,0 @@
import { FormattedMessage} from '@umijs/max';
export const DeviceCategoryColumns = [{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.id"
defaultMessage="$$$"/>),
dataIndex: "id",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.name"
defaultMessage="$$$"/>),
dataIndex: "name",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.code"
defaultMessage="$$$"/>),
dataIndex: "code",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.remark"
defaultMessage="$$$"/>),
dataIndex: "remark",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.createTime"
defaultMessage="$$$"/>),
dataIndex: "create_time",
},{
title: (<FormattedMessage
id="DCSDeviceList.device_category.table.list.updateTime"
defaultMessage="$$$"/>),
dataIndex: "update_time",
},]

@ -74,7 +74,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => {
required: true,
message: (
<FormattedMessage
id="DCSDeviceList.device_category.table.rule.required.name"
id="alarm.setting.table.list.rule.required.name"
defaultMessage="name is required"
/>
),

@ -2,7 +2,7 @@
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-11-01 13:56:33
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-19 13:46:07
* @LastEditTime: 2023-12-26 15:01:08
* @FilePath: \general-ai-platform-web\src\pages\Device\DeviceCategoryList\components\UpdateForm.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
@ -83,7 +83,7 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => {
required: true,
message: (
<FormattedMessage
id="DCSDeviceList.device_category.table.rule.required.name"
id="alarm.setting.table.list.rule.required.name"
defaultMessage="name is required"
/>
),

@ -141,6 +141,7 @@ const AlarmStatusForm: React.FC<CreateFormProps> = (props) => {
<DeleteOutlined
style={{
color: 'rgba(232, 13, 13, 1)',
fontSize: 16
}}
/>
</div>

@ -219,26 +219,26 @@ const DeviceCategoryList: React.FC = () => {
persistenceKey: 'device_category_list',
persistenceType: 'localStorage',
}}
tableAlertOptionRender={() => {
return (
<>
{selectedRowsState?.length > 0 && (
<IsBatchDelete
deleteApi={() => {
// TODO 需要;联调删除接口
deleteDeviceCategoryDeleteDeviceCategoryByIds({
ids: selectedRowsState.map((v: API.DeviceCategory) => {
return v.id as number;
}),
}).then(() => {
actionRef.current?.reloadAndRest?.();
});
}}
/>
)}
</>
);
}}
// tableAlertOptionRender={() => {
// return (
// <>
// {selectedRowsState?.length > 0 && (
// <IsBatchDelete
// deleteApi={() => {
// // TODO 需要;联调删除接口
// deleteDeviceCategoryDeleteDeviceCategoryByIds({
// ids: selectedRowsState.map((v: API.DeviceCategory) => {
// return v.id as number;
// }),
// }).then(() => {
// actionRef.current?.reloadAndRest?.();
// });
// }}
// />
// )}
// </>
// );
// }}
toolBarRender={() => [
<Access
accessible={access.canUpdate(history.location.pathname)}

@ -0,0 +1,95 @@
/*
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-12-26 14:46:35
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-26 14:58:36
* @FilePath: \general-ai-platform-web\src\pages\Alarm\AlarmWays\components\CreateForm.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { ModalForm, ProForm, ProFormText } from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import {
proFormSmallItemStyleProps,
proFormSmallModelWidth,
} from '../../../../../config/defaultForm';
// TODO 需要根据接口替换API.DeviceCategory
export type FormValueType = {
target?: string;
template?: string;
type?: string;
time?: string;
frequency?: string;
} & Partial<API.DeviceCategory>;
export type CreateFormProps = {
createModalOpen: boolean;
handleModal: () => void;
reload: any;
};
const CreateForm: React.FC<CreateFormProps> = (props) => {
const intl = useIntl();
// const [isAuto, setIsAuto] = useState(true);
const [form] = Form.useForm<API.DeviceCategory>();
return (
<ModalForm<API.DeviceCategory>
width={proFormSmallModelWidth}
title={intl.formatMessage({
id: 'alarm.ways.page.add',
defaultMessage: '$$$',
})}
open={props.createModalOpen}
form={form}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => props.handleModal(),
}}
submitTimeout={2000}
onFinish={async (values) => {
// TODO 需要对接新增告警方式接口
console.log('onFinish_values', values);
// postDeviceCategoryCreateDeviceCategory(values)
// .then(() => {
// message.success(intl.formatMessage({ id: 'common.success', defaultMessage: '$$$' }));
// props.reload();
// })
// .catch(() => {
// message.error(intl.formatMessage({ id: 'common.failure', defaultMessage: '$$$' }));
// });
props.handleModal();
return true;
}}
>
<ProForm.Group>
<ProFormText
width={proFormSmallItemStyleProps.width}
name="name"
label={<FormattedMessage id="alarm.ways.page.model.name" defaultMessage="$$$" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'alarm.ways.page.model.name',
defaultMessage: '$$$',
})}`}
required={true}
rules={[
{
required: true,
message: (
<FormattedMessage
id="alarm.ways.page.model.rule.required.name"
defaultMessage="name is required"
/>
),
},
]}
/>
</ProForm.Group>
</ModalForm>
);
};
export default CreateForm;

@ -0,0 +1,169 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/*
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-11-01 13:56:33
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-28 14:38:52
* @FilePath: \general-ai-platform-web\src\pages\Device\DeviceCategoryList\components\UpdateForm.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { postDeviceGroupGetDeviceGroupFkSelect } from '@/services/device/DeviceGroup';
import { CloseOutlined, SnippetsOutlined } from '@ant-design/icons';
import {
ProForm,
ProFormList,
ProFormSelect,
ProFormText,
ProFormTextArea,
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Form } from 'antd';
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
import {
proFormListActionButtonProps,
proFormListCreatorButtonProps,
proFormSmallItemStyleProps,
} from '../../../../../config/defaultForm';
export type FormValueType = {
target?: string;
template?: string;
type?: string;
time?: string;
frequency?: string;
} & Partial<API.DeviceCategory>;
export type UpdateFormProps = {
ref: any;
values: Record<string, any>;
reload: any;
};
const UpdateForm = forwardRef((props: UpdateFormProps, ref: React.Ref<any> | undefined) => {
const intl = useIntl();
const [form] = Form.useForm<API.DeviceCategory>();
// modalProps={{
// destroyOnClose: true,
// onCancel: () => props.handleModal(),
// }}
// submitTimeout={2000}
// 最关键的代码: 第二个参数返回一个对象 —— 定义 父组件可以调用这个组件 的方法
const initSubmitForm = () => {
console.log('调用子组件方法', form);
form.submit();
};
useImperativeHandle(
ref,
() => ({
initSubmitForm,
}),
[],
);
// 数据变更给表单设置初始值
useEffect(() => {
form.setFieldsValue({
...props.values,
});
}, [props.values]);
return (
<div>
<ProForm
form={form}
autoFocusFirstInput
submitter={{
render: false, // 隐藏提交按钮
}}
onFinish={async (values) => {
// TODO 需要对接更新告警方式接口
console.log('onFinish_values', values);
// putDeviceCategoryUpdateDeviceCategory(values)
// .then(() => {
// message.success(intl.formatMessage({ id: 'common.success', defaultMessage: '$$$' }));
// props.reload();
// })
// .catch(() => {
// message.error(intl.formatMessage({ id: 'common.failure', defaultMessage: '$$$' }));
// });
return true;
}}
>
<ProFormSelect
width={proFormSmallItemStyleProps.width}
name="serviceName"
label={<FormattedMessage id="alarm.ways.page.form.serviceName" defaultMessage="$$$" />}
placeholder={`${intl.formatMessage({
id: 'common.please_select',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'alarm.ways.page.form.serviceName',
defaultMessage: '$$$',
})}`}
required={false}
// TODO 在types中增加类型注释
initialValue={props.values?.serviceName}
debounceTime={1000}
request={async () => {
// TODO 此处需要使用告警级别接口联调
const resp = await postDeviceGroupGetDeviceGroupFkSelect({});
return resp.data.list.map((v: any) => {
return {
label: v.name,
value: v.id,
};
});
}}
/>
<ProFormList
style={{
width: proFormSmallItemStyleProps.width,
}}
copyIconProps={{
Icon: SnippetsOutlined,
}}
deleteIconProps={{
Icon: CloseOutlined,
}}
initialValue={props.values?.params}
name="params"
label= {<span className='h3 gn'><FormattedMessage id="alarm.ways.page.form.params" defaultMessage="$$$" /></span>}
creatorButtonProps={proFormListCreatorButtonProps}
{
...proFormListActionButtonProps
}
>
{(f, index, action) => {
return (
<div
style={{
display: 'flex',
justifyContent: 'space-between',
width: proFormSmallItemStyleProps.width - 44,
}}
>
<ProFormText width={229} key="name" name="name" label="键名" />
<ProFormText width={229} key="default" name="default" label="默认值" />
</div>
);
}}
</ProFormList>
<ProFormTextArea
name="template"
label={<FormattedMessage id="alarm.ways.page.form.template" defaultMessage="$$$" />}
placeholder={`${intl.formatMessage({
id: 'common.please_input',
defaultMessage: '$$$',
})}${intl.formatMessage({
id: 'alarm.ways.page.form.template',
defaultMessage: '$$$',
})}`}
required={false}
initialValue={props.values?.template}
disabled={false}
/>
</ProForm>
</div>
);
});
export default UpdateForm;

@ -0,0 +1,222 @@
import { FormattedMessage } from '@/.umi/plugin-locale';
import IsModalDelete from '@/components/TableActionCard/isModalDelete';
import { PageContainer, ProCard } from '@ant-design/pro-components';
import { Button, Empty } from 'antd';
import { useEffect, useRef, useState } from 'react';
import CreateForm from './components/CreateForm';
import UpdateForm from './components/UpdateForm';
/**
* @
* 1.
* 2.
* 3.
* 4.
*
* *
* *&
*/
const pageStyle: React.CSSProperties = {
minHeight: '50vh',
};
const AlarmWays: React.FC = () => {
const updateFormRef = useRef(null);
const [createModalOpen, setCreateModalOpen] = useState<boolean>(false);
// const [updateModalOpen, setUpdateModalOpen] = useState<boolean>(false);
const [activeTabId, setActiveTabId] = useState<string>('0');
const [waysList, setWaysList] = useState<Record<string, any>[]>([
{
name: '短信通知',
id: '0',
serviceName: 4,
params: [
{
name: 'name1',
default: 'default1',
},
],
template: '123',
},
{
name: '电话通知',
id: '1',
serviceName: 2,
params: [
{
name: 'name11',
default: 'default11',
},
{
name: 'name12',
default: 'default12',
},
],
template: '456',
},
{
name: '邮件通知',
id: '2',
serviceName: '',
params: [],
template: '',
},
{
name: '微信通知',
id: '3',
serviceName: '',
params: [],
template: '',
},
]);
const [currentRow, setCurrentRow] = useState<Record<string, any> | undefined>({});
// const handleUpdateModal = () => {
// if (updateModalOpen) {
// setUpdateModalOpen(false);
// setCurrentRow(undefined);
// } else {
// setUpdateModalOpen(true);
// }
// };
const handleCreateModal = () => {
if (createModalOpen) {
setCreateModalOpen(false);
setCurrentRow(undefined);
} else {
setCreateModalOpen(true);
}
};
// 加载告警方式详情
function loadDetail() {
const currRow = waysList.find((item) => item.id === activeTabId);
setCurrentRow(() => currRow);
console.log(currRow, 'loadDetail', currentRow);
}
// 更新告警方式
function handleUpdate() {
updateFormRef.current.initSubmitForm();
console.log(updateFormRef.current, 'updateFormRef');
}
// 删除告警方式
function handleDelete() {
const waysData = waysList.filter((item) => item.id !== activeTabId);
setWaysList(waysData);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
waysData.length && setActiveTabId(waysData[0].id);
}
function handleReload() {
// TODO 需要调用刷新告警方式列表接口
// loadDetail();
}
useEffect(() => {
loadDetail();
}, [activeTabId]);
useEffect(() => {
handleReload();
}, []);
return (
<PageContainer>
<ProCard
title={<FormattedMessage id="alarm.ways.page.title" defaultMessage="$$$" />}
extra={
<Button type="primary" onClick={handleCreateModal}>
<FormattedMessage id="alarm.ways.page.add" defaultMessage="$$$" />
</Button>
}
headStyle={{
padding: '16px',
borderBottom: '1px solid #E0E0E0',
}}
bodyStyle={{
...pageStyle,
padding: '16px',
margin: 0,
}}
>
{waysList?.length ? (
<div>
<div className="gn tabAction_box_wrap" style={{ paddingTop: 4, marginBottom: 16 }}>
{waysList.map((item, index) => {
// eslint-disable-next-line react/jsx-key
return (
<Button
style={{ marginRight: 12 }}
type={activeTabId === item.id ? 'primary' : 'default'}
key={index}
onClick={() => {
setActiveTabId(item.id);
}}
>
{item.name || ''}
</Button>
);
})}
</div>
<UpdateForm ref={updateFormRef} values={currentRow || {}} reload={handleReload} />
<div
style={{
display: 'flex',
justifyContent: 'space-between',
paddingTop: 8,
paddingBottom: 8,
}}
>
<Button type="primary" onClick={handleUpdate}>
<FormattedMessage id="common.save" defaultMessage="$$$" />
</Button>
<IsModalDelete
titleText={`确认删除${currentRow?.name}吗?`}
contentText={`确认删除${currentRow?.name}吗?删除后将无法通过${currentRow?.name}发送告警,请谨慎操作。`}
deleteButton={
<Button type="default" danger>
<FormattedMessage id="common.delete" defaultMessage="$$$" />
</Button>
}
deleteApi={handleDelete}
></IsModalDelete>
</div>
</div>
) : (
<Empty
style={{
minHeight: pageStyle.minHeight,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
image="https://gw.alipayobjects.com/zos/antfincdn/ZHrcdLPrvN/empty.svg"
imageStyle={{ height: 160 }}
description={
<span>
<FormattedMessage id="alarm.ways.page.empty.des" defaultMessage="$$$" />
</span>
}
>
<Button type="primary" onClick={handleCreateModal}>
<FormattedMessage id="alarm.ways.page.add" defaultMessage="$$$" />
</Button>
</Empty>
)}
</ProCard>
<CreateForm
createModalOpen={createModalOpen}
handleModal={handleCreateModal}
reload={handleReload}
/>
</PageContainer>
);
};
export default AlarmWays;

@ -2,7 +2,7 @@
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-11-01 13:56:33
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-15 16:58:50
* @LastEditTime: 2023-12-28 14:25:08
* @FilePath: \general-ai-platform-web\src\pages\Resource\ModelCategoryList\components\ColumnDrawer.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
@ -16,18 +16,21 @@ export type ColumnDrawProps = {
columns: ProColumns<API.ModelCategory>[];
currentRow: Record<string, any>;
clipPathData: Record<string, any>;
canvasObject: any;
};
const ColumnDrawer: React.FC<ColumnDrawProps> = (props) => {
const [positionStyle, setPositionStyle] = useState<React.CSSProperties>({});
// 生成模型详情卡片位置
function initPointPosition() {
const zoomIndex = 1 * props.canvasObject?.getZoom();
type Point = { x: number; y: number };
type Position = 'left' | 'right' | 'top' | 'bottom';
let currPositionStyle: React.CSSProperties = {};
const modelInfoPosition: Position[] = ['right'];
const maxX = props.clipPathData?.width; // 画布宽度
const maxY = props.clipPathData?.height; // 画布高度
const maxX = props.clipPathData?.width * zoomIndex; // 画布宽度
const maxY = props.clipPathData?.height * zoomIndex; // 画布高度
const x = props.currentRow.baseInfo?.left; // 模型x轴位置
const y = props.currentRow.baseInfo?.top; // 模型y轴位置
const modelWidth: number = 120; // 模型自身宽度
@ -54,24 +57,24 @@ const ColumnDrawer: React.FC<ColumnDrawProps> = (props) => {
}
console.log(props.currentRow, 'currentRow');
if (modelInfoPosition.includes('left')) {
let topValue = y - modelInfoHeight / 2 + modelHeight / 2
if(topValue> maxY - modelInfoHeight){
topValue = maxY - modelInfoHeight
let topValue = y - modelInfoHeight / 2 + modelHeight / 2;
if (topValue > maxY - modelInfoHeight) {
topValue = maxY - modelInfoHeight;
}
if(topValue<0){
topValue = 0
if (topValue < 0) {
topValue = 0;
}
currPositionStyle = {
top: topValue,
left: x - modelInfoWidth - 10,
};
} else {
let topValue = y - modelInfoHeight / 2 + modelHeight / 2
if(topValue> maxY - modelInfoHeight){
topValue = maxY - modelInfoHeight
let topValue = y - modelInfoHeight / 2 + modelHeight / 2;
if (topValue > maxY - modelInfoHeight) {
topValue = maxY - modelInfoHeight;
}
if(topValue<0){
topValue = 0
if (topValue < 0) {
topValue = 0;
}
currPositionStyle = {
top: topValue,
@ -85,7 +88,7 @@ const ColumnDrawer: React.FC<ColumnDrawProps> = (props) => {
if (props.currentRow) {
initPointPosition();
}
}, [props.currentRow, props.clipPathData]);
}, [props.currentRow, props.clipPathData, props.canvasObject]);
return props.isShowDetail ? (
<div
style={{

@ -0,0 +1,112 @@
/*
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-12-25 16:14:04
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-29 15:36:39
* @FilePath: \general-ai-platform-web\src\pages\FabricView\Room\components\ModelTipBox.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { useState } from 'react';
const modelOptionList: Record<string, any>[] = [
{
id: '0',
label: '在线',
status: 'watchOnline',
color: '#52C41A',
},
{
id: '1',
label: '离线',
status: 'watchOutline',
color: '#CCCCCC',
},
{
id: '2',
label: '故障',
status: 'watchWarn',
color: '#FAAD14',
},
{
id: '3',
label: '告警',
status: 'watchError',
color: '#E80D0D',
},
];
type ModelTipBoxProps = {
options?: Record<string, any>[];
changeStatusSelected: (arr: string[]) => void
};
const ModelTipBox: React.FC<ModelTipBoxProps> = (props) => {
const [selected, setSelected] = useState<string[]>([]);
function doSelected(options: Record<string, any>) {
let currSelectedArr: string[] = []
if (selected.includes(options.status)) {
currSelectedArr = selected.filter((item) => options.status != item)
} else {
currSelectedArr = [...selected, options.status]
}
setSelected(currSelectedArr);
props.changeStatusSelected(currSelectedArr)
}
return (
<ul
style={{
display: 'flex',
width: 100,
height: 200,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
background: 'white'
}}
>
{modelOptionList.map((item) => {
const activeStyle: React.CSSProperties = selected.includes(item.status)
? {
border: '1px solid #154DDD',
background: '#154DDD',
color: 'white',
}
: {
border: '1px solid #DCDCDC',
color: '#333',
};
return (
<li
key={item.id}
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 2,
padding: '4px 6px',
marginBottom: 12,
width: 50,
cursor: 'pointer',
...activeStyle,
}}
onClick={() => doSelected(item)}
>
<span
style={{
width: 6,
height: 6,
borderRadius: '50%',
background: item.color,
}}
></span>
<span style={{ paddingLeft: 2 }}>{item.label}</span>
</li>
);
})}
</ul>
);
};
export default ModelTipBox;

@ -2,12 +2,11 @@
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-12-12 10:57:54
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-15 17:06:36
* @LastEditTime: 2023-12-29 15:44:16
* @FilePath: \general-ai-platform-web\src\pages\FabricView\Room\index.tsx
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// import { useUuid } from '@/hooks/useUuid';
import { currJson } from '@/testData/fabricRoomList';
import { bgImageData } from '@/testData/fabricRoomSvg';
import { ProCard } from '@ant-design/pro-components';
@ -16,6 +15,8 @@ import { Button } from 'antd';
import { fabric } from 'fabric';
import React, { useEffect, useRef, useState } from 'react';
import { ColumnDrawer } from './components/ColumnDrawer';
import ModelTipBox from './components/ModelTipBox';
import LoadingSpin from '@/components/Loading/loadingSpin';
export const ModelCategoryColumns = [
{
@ -51,6 +52,7 @@ export const ModelCategoryColumns = [
dataIndex: 'deviceName',
},
];
const FabricViewRoom: React.FC = () => {
const intl = useIntl();
const canvasRef = useRef(null);
@ -59,6 +61,7 @@ const FabricViewRoom: React.FC = () => {
const [descriptors, setDescriptors] = useState<Record<string, any>>({});
const [category_fk_id_open, set_category_fk_id_open] = useState(false);
const [currentModelUserProperty, setCurrentModelUserProperty] = useState<Record<string, any>>({});
const [isLoading, setisLoading] = useState<boolean>(true);
const [changeId, setChangeId] = useState<string[]>([]);
const [currentTargetObject, setCurrentTargetObject] = useState<any>(null);
@ -68,12 +71,15 @@ const FabricViewRoom: React.FC = () => {
height: 900, // 高
});
const [jsonData, setJsonData] = useState<Record<string, any>>({});
/*test */
// const { fetchUuid } = useUuid();
function changeModelStatus() {
setChangeId(['2', '3']);
function changeZoom(isPlus: boolean) {
const rate = isPlus ? 1.1 : 0.9;
// plus minus
const currentZoom = cvs.getZoom();
const newZoom = currentZoom * rate; // 缩小 10%
cvs.setZoom(newZoom);
}
const handle_category_fk_id = () => {
@ -100,13 +106,33 @@ const FabricViewRoom: React.FC = () => {
});
groupObject.item(1).set({
fill: 'rgb(0,0,0)',
})
});
finalCvs.renderAll();
// 遍历子对象找到要修改的子对象
// }
});
}
// 筛选显示告警状态
function changeStatusSelected(selecteds: string[]) {
console.log(cvs.getObjects(), 'changeStatusSelected_selected', selecteds);
if(selecteds.length){
cvs.getObjects().map((element) => {
element.set({
visible: selecteds.includes(element?.userProperty?.status),
});
});
} else {
cvs.getObjects().map((element) => {
element.set({
visible: true,
});
});
}
cvs.renderAll();
}
// 初始化加载画布&渲染模型
useEffect(() => {
const canvasObject = new fabric.Canvas(canvasRef.current);
@ -129,7 +155,6 @@ const FabricViewRoom: React.FC = () => {
// 获取事件的目标对象
const targetObject = event.target;
// set_category_fk_id_open(false);
// 检查目标对象是否为组合对象
if (targetObject && targetObject?.userProperty) {
// canvasObject.setActiveObject(targetObject);
@ -142,6 +167,7 @@ const FabricViewRoom: React.FC = () => {
if (targetObject) {
setCurrentTargetObject(targetObject);
closeSelectedModel(canvasObject);
canvasObject.bringToFront(targetObject);
// console.log('mouse:down:', );
targetObject.item(0).item(1).set({
@ -149,19 +175,21 @@ const FabricViewRoom: React.FC = () => {
});
targetObject.item(1).set({
fill: 'blue',
})
});
}
canvasObject.renderAll();
});
});
setCanvas(canvasObject);
// setTimeout(()=>{
setisLoading(false)
// },1000)
return () => {
canvasObject.dispose();
};
}, []);
useEffect(() => {
// TODO_3 需要加个loading效果
// import('./Descriptors.json').then((descriptors) => {
// setDescriptors(descriptors);
// });
@ -172,32 +200,44 @@ const FabricViewRoom: React.FC = () => {
}, [changeId, category_fk_id_open]);
return (
<ProCard
gutter={24}
extra={
<>
<Button type="primary" onClick={changeModelStatus}>
</Button>
</>
}
>
{/* <ProCard colSpan={4}>
<ImageMapItems ref={itemsRef} canvasRef={canvasRef} descriptors={descriptors} />
</ProCard> */}
<ProCard colSpan={24} style={{ padding: 0, margin: 0 }} bodyStyle={{ padding: 0, margin: 0 }}>
<canvas ref={canvasRef} {...clipPathData} style={{ border: '1px dashed #eee' }}></canvas>
<ColumnDrawer
handleDrawer={handle_category_fk_id}
isShowDetail={category_fk_id_open}
columns={ModelCategoryColumns}
currentRow={currentModelUserProperty}
clipPathData={clipPathData}
/>
<div>
<LoadingSpin spinning={isLoading}></LoadingSpin>
<ProCard
gutter={24}
extra={
<>
<Button type="primary" style={{ marginRight: 20 }} onClick={() => changeZoom(false)}>
</Button>
<Button type="primary" onClick={() => changeZoom(true)}>
</Button>
</>
}
>
<ProCard
colSpan={24}
style={{ padding: 0, margin: 0 }}
bodyStyle={{ padding: 0, margin: 0 }}
>
<canvas ref={canvasRef} {...clipPathData} style={{ border: '1px dashed #eee' }}></canvas>
<div style={{ position: 'absolute', bottom: 0, right: 0 }}>
<ModelTipBox
changeStatusSelected={(selected: string[]) => changeStatusSelected(selected)}
></ModelTipBox>
</div>
<ColumnDrawer
handleDrawer={handle_category_fk_id}
isShowDetail={category_fk_id_open}
columns={ModelCategoryColumns}
currentRow={currentModelUserProperty}
clipPathData={clipPathData}
canvasObject={cvs}
/>
</ProCard>
</ProCard>
</ProCard>
// 属性预览
</div>
);
};

@ -294,6 +294,7 @@ const AlgorithmSetting: React.FC = () => {
style={{ display: 'flex', padding: '0 16px', margin: 0 }}
>
{tabModeList.map((item) => {
// TODO 统一使用button默认效果 'primary' : 'default'
return (
<li
key={item.value}

@ -2,7 +2,7 @@
* @Author: zhoux zhouxia@supervision.ltd
* @Date: 2023-12-12 15:41:56
* @LastEditors: zhoux zhouxia@supervision.ltd
* @LastEditTime: 2023-12-15 17:22:03
* @LastEditTime: 2023-12-29 15:29:58
* @FilePath: \general-ai-platform-web\src\testData\fabricRoomList.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
@ -89,6 +89,34 @@ const startData: Record<string, any> = {
],
};
// 生成100到1100之间的随机整数
function generateRandomNumber(minNum, maxNum) {
// 生成0到1之间的随机小数
const randomFraction = Math.random();
// 将随机小数映射到100到1100之间的范围
const randomNumber = Math.floor(randomFraction * (maxNum - minNum + 1) + minNum);
return randomNumber;
}
function initFabricRoomData() {
for (let i = 0; i < 5; i++) {
let currObjects = JSON.parse(JSON.stringify(startData.modelsList));
currObjects = currObjects.map((item) => {
item.baseInfo = {
left: generateRandomNumber(100, 1100),
top: generateRandomNumber(100, 800),
};
return item;
});
startData.modelsList = startData.modelsList.concat(currObjects);
}
}
initFabricRoomData();
const fabricRoomListData: Record<string, any>[] = [];
startData.modelsList.forEach((item: Record<string, any>) => {
const { baseInfo, ...restInfo } = item;
@ -104,6 +132,7 @@ startData.modelsList.forEach((item: Record<string, any>) => {
);
});
export const currJson = {
version: '5.3.0',
objects: fabricRoomListData,

@ -1,3 +1,8 @@
/**
* @FixMenuItemIcon
*
*
*/
import React from 'react';
import { MenuDataItem } from '@ant-design/pro-layout';
import {CarOutlined, UserOutlined, TableOutlined,
@ -5,7 +10,7 @@ import {CarOutlined, UserOutlined, TableOutlined,
HomeOutlined, SettingOutlined, TeamOutlined, DotChartOutlined,
BlockOutlined, DesktopOutlined, DatabaseOutlined,
WarningOutlined, CalendarOutlined, ExperimentOutlined,
ThunderboltOutlined, BugOutlined, AreaChartOutlined,
ThunderboltOutlined, BugOutlined, AreaChartOutlined,ContactsOutlined, GatewayOutlined, BellOutlined
} from '@ant-design/icons';
const iconMap:any = {
@ -28,6 +33,9 @@ const iconMap:any = {
'ThunderboltOutlined': <ThunderboltOutlined/>,
'BugOutlined': <BugOutlined/>,
'AreaChartOutlined': <AreaChartOutlined/>,
'ContactsOutlined': <ContactsOutlined/>,
'GatewayOutlined': <GatewayOutlined/>,
'BellOutlined': <BellOutlined/>
}
// FIX从接口获取菜单时icon为string类型
const fixMenuItemIcon = (menus: MenuDataItem[], iconType = 'Outlined'): MenuDataItem[] => {

Loading…
Cancel
Save