diff --git a/src/components/DictionaryBox/alarmLevel.tsx b/src/components/DictionaryBox/alarmLevel.tsx new file mode 100644 index 0000000..e543c45 --- /dev/null +++ b/src/components/DictionaryBox/alarmLevel.tsx @@ -0,0 +1,39 @@ +/* + * @Author: zhoux zhouxia@supervision.ltd + * @Date: 2023-11-15 15:01:34 + * @LastEditors: zhoux zhouxia@supervision.ltd + * @LastEditTime: 2023-12-20 11:25:15 + * @FilePath: \general-ai-platform-web\src\components\DictionaryBox\isEnable.tsx + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +import { ArrowUpOutlined, DownOutlined } from '@ant-design/icons'; + +type AlarmLevelBoxProps = { + icon?: 'ArrowUpOutlined' | 'default' | undefined; + color?: string; + size?: number; +}; + +const AlarmLevelBox: React.FC<AlarmLevelBoxProps> = (props) => { + const { icon, color, size } = props; + const finalSize = size || 14; + return ( + <div + style={{ + backgroundColor: color, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: finalSize, + height: finalSize, + borderRadius: '50%', + color: 'white', + fontSize: finalSize * 0.55, + }} + > + {icon === 'ArrowUpOutlined' ? <ArrowUpOutlined /> : <DownOutlined />} + </div> + ); +}; + +export default AlarmLevelBox; diff --git a/src/components/TableActionCard/isDelete.tsx b/src/components/TableActionCard/isDelete.tsx index 649cb9c..b6bde41 100644 --- a/src/components/TableActionCard/isDelete.tsx +++ b/src/components/TableActionCard/isDelete.tsx @@ -2,18 +2,20 @@ * @Author: zhoux zhouxia@supervision.ltd * @Date: 2023-11-16 14:30:15 * @LastEditors: zhoux zhouxia@supervision.ltd - * @LastEditTime: 2023-11-16 14:56:27 + * @LastEditTime: 2023-12-19 16:30:51 * @FilePath: \general-ai-platform-web\src\components\BatchOperation\isBatchDelete.tsx * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ // import { useIntl } from '@ant-design/pro-components'; +import { DeleteOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Button, Popconfirm } from 'antd'; import { FormattedMessage } from 'react-intl'; type IsDeleteProps = { // eslint-disable-next-line @typescript-eslint/ban-types - deleteApi: Function; + buttonType?: 'defalut' | 'deleteIcon'; + deleteApi: () => void; }; const IsDelete: React.FC<IsDeleteProps> = (props) => { @@ -34,9 +36,17 @@ const IsDelete: React.FC<IsDeleteProps> = (props) => { props.deleteApi(); }} > - <Button type="link" size="small" danger> - <FormattedMessage id="pages.searchTable.destroy" defaultMessage="Destroy" /> - </Button> + {props.buttonType === 'deleteIcon' ? ( + <> + <DeleteOutlined style={{ + color: "rgba(232, 13, 13, 1)" + }} /> + </> + ) : ( + <Button type="link" size="small" danger> + <FormattedMessage id="pages.searchTable.destroy" defaultMessage="Destroy" /> + </Button> + )} </Popconfirm> ); }; diff --git a/src/enums/status.ts b/src/enums/status.ts index af51370..d803349 100644 --- a/src/enums/status.ts +++ b/src/enums/status.ts @@ -1,3 +1,15 @@ +/* + * @Author: zhoux zhouxia@supervision.ltd + * @Date: 2023-12-08 14:50:08 + * @LastEditors: zhoux zhouxia@supervision.ltd + * @LastEditTime: 2023-12-20 13:25:59 + * @FilePath: \general-ai-platform-web\src\enums\status.ts + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ + +import { DownOutlined } from "@ant-design/icons"; + +// 分布式设备状态 export const deviceStatusEnums: Record<string, any> = { allStatus: { miniName: '全部', @@ -25,7 +37,7 @@ export const deviceStatusEnums: Record<string, any> = { color: 'error', }, }; - +// 资源设备~ export const resourceDeviceStatusEnums: Record<string, any> = { allStatus: { miniName: '全部异常', @@ -70,3 +82,49 @@ export const resourceDeviceStatusEnums: Record<string, any> = { renderType: 'dot', }, }; +// 告警等级~ +export const alarmLevelStatusEnum: Record<string, any>[] = [ + { + color: 'rgba(232, 13, 13, 1)', + defaultValue: '1', + defaultLabel: '紧急', + isDelete: false, + icon: 'ArrowUpOutlined', + id: '1', + }, + { + color: 'rgba(255, 136, 0, 1)', + defaultValue: '2', + defaultLabel: '较高', + isDelete: false, + id: '2', + }, + { + color: 'rgba(68, 139, 245, 1)', + defaultValue: '3', + defaultLabel: '一般', + isDelete: false, + id: '3', + }, + { + color: 'rgba(179, 214, 0, 1)', + defaultValue: '4', + defaultLabel: '低', + isDelete: true, + id: '4', + }, + { + color: 'rgba(81, 177, 6, 1)', + defaultValue: '5', + defaultLabel: '较低', + isDelete: true, + id: '5', + }, + { + color: 'rgba(43, 183, 136, 1)', + defaultValue: '6', + defaultLabel: '非常低', + isDelete: true, + id: '6', + }, +]; diff --git a/src/enums/storage.ts b/src/enums/storage.ts new file mode 100644 index 0000000..f2ff216 --- /dev/null +++ b/src/enums/storage.ts @@ -0,0 +1,3 @@ +export const localStorageKeyEnums = { + alarmSetting_model_alarmStatusSetting: 'alarmSetting_model_alarmStatusSetting_key', +}; diff --git a/src/global.css b/src/global.css index b0bcb37..a2ddb35 100644 --- a/src/global.css +++ b/src/global.css @@ -268,11 +268,14 @@ a.ant-dropdown-trigger { padding-left: 12px; } .gn .ant-tabs > .ant-tabs-nav .ant-tabs-nav-wrap .ant-tabs-nav-list { - padding: 0 24px; + padding: 0 16px; margin-block-start: 0; } .gn .ant-tabs > .ant-tabs-nav .ant-tabs-nav-wrap .ant-tabs-nav-list .ant-tabs-tab-active { - font-weight: 700; + font-weight: 400; +} +.gn .ant-tabs .ant-tabs-tab .anticon { + margin-right: 4px; } .gn .ant-pro-table-list-toolbar-container { padding-block: 8px; @@ -347,6 +350,27 @@ a.ant-dropdown-trigger { .gn.themeBgHover:hover { background-color: #155BD4; } +.gn.algorithmSetting_project_box.ant-tabs-top > .ant-tabs-nav::before { + position: absolute; + right: 0; + left: 0; + border-bottom: 1px solid transparent; + content: ''; +} +.gn.algorithmSetting_project_box.ant-tabs .ant-tabs-ink-bar { + background: transparent; +} +.gn.algorithmSetting_project_box.ant-tabs .ant-tabs-tab { + padding: 16px 0 0px; +} +.gn.algorithmSetting_project_box.ant-tabs .ant-tabs-tab + .ant-tabs-tab { + margin: 0 0 0 12px; +} +.gn.algorithmSetting_project_box .ant-tabs-tab-active .ant-btn-default { + border: 1px solid #154DDD; + background: rgba(21, 77, 221, 0.1); + color: #154DDD; +} .ant-pro-global-header-logo img { height: 48px; } diff --git a/src/global.less b/src/global.less index f4313b2..00715f9 100644 --- a/src/global.less +++ b/src/global.less @@ -313,11 +313,11 @@ a.ant-dropdown-trigger{ margin-inline: 0; padding-block: 0; padding-inline: 6px; -} + } -.ant-list .ant-list-pagination { - margin-block-start: 10px; -} + .ant-list .ant-list-pagination { + margin-block-start: 10px; + } .ant-table-wrapper table tr th.ant-table-selection-column, .ant-table-wrapper table tr td.ant-table-selection-column, @@ -330,14 +330,18 @@ a.ant-dropdown-trigger{ .ant-tabs >.ant-tabs-nav .ant-tabs-nav-wrap{ .ant-tabs-nav-list{ - padding: 0 24px; + padding: 0 16px; margin-block-start: 0; .ant-tabs-tab-active{ - font-weight: 700 + font-weight: 400 } } } + .ant-tabs .ant-tabs-tab .anticon { + margin-right: 4px; + } + .ant-pro-table-list-toolbar-container { padding-block: 8px; } @@ -431,6 +435,35 @@ a.ant-dropdown-trigger{ &.themeBgHover:hover{ background-color: #155BD4; } + + + // 项目部署模块 + &.algorithmSetting_project_box{ + &.ant-tabs-top >.ant-tabs-nav::before { + position: absolute; + right: 0; + left: 0; + border-bottom: 1px solid transparent; + content: ''; + } + &.ant-tabs .ant-tabs-ink-bar{ + background: transparent; + } + &.ant-tabs .ant-tabs-tab{ + padding: 16px 0 0px; + } + &.ant-tabs .ant-tabs-tab+.ant-tabs-tab{ + // margin: 0; + margin:0 0 0 12px; + } + & .ant-tabs-tab-active{ + .ant-btn-default{ + border: 1px solid #154DDD; + background: rgba(21, 77, 221, 0.10); + color: #154DDD; + } + } + } } .ant-pro-global-header-logo img { height: 48px; @@ -444,6 +477,8 @@ a.ant-dropdown-trigger{ border-radius: 2px; } } + + // .p1{ // } diff --git a/src/layouts/treeAndTableList.tsx b/src/layouts/treeAndTableList.tsx index 0e7fd7a..b79f167 100644 --- a/src/layouts/treeAndTableList.tsx +++ b/src/layouts/treeAndTableList.tsx @@ -2,7 +2,7 @@ * @Author: zhoux zhouxia@supervision.ltd * @Date: 2023-11-21 13:42:31 * @LastEditors: zhoux zhouxia@supervision.ltd - * @LastEditTime: 2023-11-24 16:01:20 + * @LastEditTime: 2023-12-19 15:27:06 * @FilePath: \general-ai-platform-web\src\layouts\treeAndTableList.tsx * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ @@ -29,7 +29,7 @@ const TreeAndTableList: React.FC<TreeAndTableListProps> = (props) => { // 统一配置 let finalLeftCard: ProCardTypeProps = { // 左侧卡片 - headStyle: { paddingTop: 20, paddingLeft: 20, paddingRight: 20, paddingBottom: 20,borderBottom: '1px solid #E0E0E0' }, + headStyle: { paddingTop: 16, paddingLeft: 20, paddingRight: 20, paddingBottom: 16,borderBottom: '1px solid #E0E0E0' }, bodyStyle: {paddingLeft: 20, paddingRight: 20}, colSpan: '22%', }; diff --git a/src/locales/zh-CN/alarm.ts b/src/locales/zh-CN/alarm.ts index c604e0f..c77003f 100644 --- a/src/locales/zh-CN/alarm.ts +++ b/src/locales/zh-CN/alarm.ts @@ -2,7 +2,7 @@ * @Author: zhoux zhouxia@supervision.ltd * @Date: 2023-12-18 16:36:36 * @LastEditors: zhoux zhouxia@supervision.ltd - * @LastEditTime: 2023-12-18 17:46:05 + * @LastEditTime: 2023-12-19 13:48:54 * @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 */ @@ -25,5 +25,5 @@ export const alarm_setting: { [key: string]: string } = { 'alarm.setting.table.model.title': '告警等级', 'alarm.setting.table.list.update': '编辑告警项', 'alarm.setting.table.rule.required.code': '类别代码为必填项', - + 'alarm.setting.table.model.alarmStatusSetting': '告警等级' } \ No newline at end of file diff --git a/src/locales/zh-CN/menu.ts b/src/locales/zh-CN/menu.ts index 3042e8d..086c8a2 100644 --- a/src/locales/zh-CN/menu.ts +++ b/src/locales/zh-CN/menu.ts @@ -7,6 +7,8 @@ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ export default { + 'menu.fabricView': '监控预览', + 'menu.fabricView.fabricView-room': '监控预览', 'menu.welcome': '欢迎', 'menu.more-blocks': '更多区块', 'menu.home': '首页', diff --git a/src/pages/Alarm/AlarmSetting/components/ColumnDrawer.tsx b/src/pages/Alarm/AlarmSetting/components/ColumnDrawer.tsx deleted file mode 100644 index 208d446..0000000 --- a/src/pages/Alarm/AlarmSetting/components/ColumnDrawer.tsx +++ /dev/null @@ -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} - diff --git a/src/pages/Alarm/AlarmSetting/components/CreateForm.tsx b/src/pages/Alarm/AlarmSetting/components/CreateForm.tsx index c04ccd6..4fbffd3 100644 --- a/src/pages/Alarm/AlarmSetting/components/CreateForm.tsx +++ b/src/pages/Alarm/AlarmSetting/components/CreateForm.tsx @@ -1,13 +1,13 @@ import { postDeviceCategoryCreateDeviceCategory } from '@/services/device/DeviceCategory'; +import { postDeviceGroupGetDeviceGroupFkSelect } from '@/services/device/DeviceGroup'; import { ModalForm, ProForm, ProFormSelect, ProFormText } from '@ant-design/pro-components'; import { FormattedMessage, useIntl } from '@umijs/max'; import { Form, message } from 'antd'; -import React, { useState } from 'react'; +import React from 'react'; import { proFormSmallItemStyleProps, proFormSmallModelWidth, } from '../../../../../config/defaultForm'; -import { postDeviceGroupGetDeviceGroupFkSelect } from '@/services/device/DeviceGroup'; // TODO 需要根据接口替换API.DeviceCategory export type FormValueType = { target?: string; @@ -25,7 +25,7 @@ export type CreateFormProps = { }; const CreateForm: React.FC<CreateFormProps> = (props) => { const intl = useIntl(); - const [isAuto, setIsAuto] = useState(true); + // const [isAuto, setIsAuto] = useState(true); const [form] = Form.useForm<API.DeviceCategory>(); return ( @@ -92,7 +92,6 @@ const CreateForm: React.FC<CreateFormProps> = (props) => { id: 'alarm.setting.table.list.code', defaultMessage: '$$$', })}`} - required={!isAuto} initialValue="" rules={[ { @@ -128,7 +127,7 @@ const CreateForm: React.FC<CreateFormProps> = (props) => { })}`} required={false} debounceTime={1000} - request={async (keyWord) => { + request={async () => { // TODO 此处需要使用告警级别接口联调 const resp = await postDeviceGroupGetDeviceGroupFkSelect({}); return resp.data.list.map((v: any) => { diff --git a/src/pages/Alarm/AlarmSetting/components/UpdateForm.tsx b/src/pages/Alarm/AlarmSetting/components/UpdateForm.tsx index a275039..1c022a6 100644 --- a/src/pages/Alarm/AlarmSetting/components/UpdateForm.tsx +++ b/src/pages/Alarm/AlarmSetting/components/UpdateForm.tsx @@ -2,18 +2,13 @@ * @Author: zhoux zhouxia@supervision.ltd * @Date: 2023-11-01 13:56:33 * @LastEditors: zhoux zhouxia@supervision.ltd - * @LastEditTime: 2023-12-18 17:47:49 + * @LastEditTime: 2023-12-19 13:46:07 * @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, - ProFormSelect, - ProFormText, -} from '@ant-design/pro-components'; +import { postDeviceGroupGetDeviceGroupFkSelect } from '@/services/device/DeviceGroup'; +import { ModalForm, ProForm, ProFormSelect, ProFormText } from '@ant-design/pro-components'; import { FormattedMessage, useIntl } from '@umijs/max'; import { Form, message } from 'antd'; import React from 'react'; @@ -21,7 +16,6 @@ import { proFormSmallItemStyleProps, proFormSmallModelWidth, } from '../../../../../config/defaultForm'; -import { postDeviceGroupGetDeviceGroupFkSelect } from '@/services/device/DeviceGroup'; export type FormValueType = { target?: string; template?: string; @@ -134,9 +128,10 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => { defaultMessage: '$$$', })}`} required={false} - initialValue={props.values.level} + // TODO 在types中增加类型注释 + initialValue={props.values?.level} debounceTime={1000} - request={async (keyWord) => { + request={async () => { // TODO 此处需要使用告警级别接口联调 const resp = await postDeviceGroupGetDeviceGroupFkSelect({}); return resp.data.list.map((v: any) => { @@ -162,7 +157,6 @@ const UpdateForm: React.FC<UpdateFormProps> = (props) => { initialValue={props.values.remark} disabled={false} /> - </ProForm.Group> </ModalForm> ); diff --git a/src/pages/Alarm/AlarmSetting/components/alarmStatusForm.tsx b/src/pages/Alarm/AlarmSetting/components/alarmStatusForm.tsx new file mode 100644 index 0000000..bfd7c1a --- /dev/null +++ b/src/pages/Alarm/AlarmSetting/components/alarmStatusForm.tsx @@ -0,0 +1,170 @@ +import AlarmLevelBox from '@/components/DictionaryBox/alarmLevel'; +import { alarmLevelStatusEnum } from '@/enums/status'; +import { localStorageKeyEnums } from '@/enums/storage'; +import { DeleteOutlined, ExclamationCircleOutlined, PlusCircleOutlined } from '@ant-design/icons'; +import { ModalForm, ProFormText } from '@ant-design/pro-components'; +import { useIntl } from '@umijs/max'; +import { Form } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { 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 = { + alarmStatusModalOpen: boolean; + handleModal: () => void; + values: Partial<API.DeviceCategory>; + reload: any; +}; + +/**style */ + +const tipStyle: React.CSSProperties = { + display: 'flex', + width: '100%', + cursor: 'pointer', + paddingBottom: 20, +}; +const AlarmStatusForm: React.FC<CreateFormProps> = (props) => { + const intl = useIntl(); + const [form] = Form.useForm<API.DeviceCategory>(); + const [alarmDict, setAlarmDict] = useState<Record<string, any>[]>([ + alarmLevelStatusEnum[0], + alarmLevelStatusEnum[1], + alarmLevelStatusEnum[2], + ]); + + // 新增一项 + function doAddItem() { + if (alarmDict.length >= 6) { + return; + } + setAlarmDict((data) => [...data, alarmLevelStatusEnum[alarmDict.length]]); + } + // 删除一项 + function doDeleteItem(delItem: Record<string, any>) { + setAlarmDict((data) => data.filter((item) => item.id !== delItem.id)); + } + + useEffect(() => { + const currData = localStorage.getItem( + localStorageKeyEnums.alarmSetting_model_alarmStatusSetting, + ); + if (currData) { + const finalData = alarmLevelStatusEnum.filter((item) => + Object.keys(JSON.parse(currData)).includes(item.id), + ); + setAlarmDict(finalData); + } + }, []); + + return ( + <ModalForm<API.DeviceCategory> + layout={'horizontal'} + width={proFormSmallModelWidth} + title={intl.formatMessage({ + id: 'alarm.setting.table.model.alarmStatusSetting', + defaultMessage: '$$$', + })} + open={props.alarmStatusModalOpen} + form={form} + autoFocusFirstInput + modalProps={{ + destroyOnClose: true, + onCancel: () => props.handleModal(), + }} + submitTimeout={2000} + onFinish={async (values) => { + console.log(values, 'values'); + localStorage.setItem( + localStorageKeyEnums.alarmSetting_model_alarmStatusSetting, + JSON.stringify(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; + }} + > + {/* TODO 待讨论是否需要动态添加状态字段 */} + + <ul> + {alarmDict.map((item, index) => { + const currVal = index % 6; + const currLevel = alarmLevelStatusEnum[currVal]; + return ( + <li key={item.id} style={{ display: 'flex', width: '100%' }}> + <div style={{ paddingTop: 5, paddingRight: 12 }}> + <AlarmLevelBox size={20} {...currLevel}></AlarmLevelBox> + </div> + <div style={{ width: '100%', flex: 1 }}> + <ProFormText name={item.id} initialValue={item.defaultLabel}></ProFormText> + </div> + {index === alarmDict.length - 1 && index > 2 ? ( + <div + style={{ paddingTop: 5, paddingLeft: 12 }} + onClick={() => { + doDeleteItem(item); + }} + > + <DeleteOutlined + style={{ + color: 'rgba(232, 13, 13, 1)', + }} + /> + </div> + ) : ( + <></> + )} + </li> + ); + })} + {alarmDict.length < 6 ? ( + <li + style={{ color: '#154DDD', ...tipStyle }} + onClick={() => { + doAddItem(); + }} + > + <div style={{ paddingRight: 12, fontSize: 16 }}> + <PlusCircleOutlined /> + </div> + <span> + 添加 + {intl.formatMessage({ + id: 'alarm.setting.table.model.alarmStatusSetting', + defaultMessage: '$$$', + })} + </span> + </li> + ) : ( + <li style={{ ...tipStyle }}> + <div style={{ paddingRight: 12, color: '#FAAD14' }}> + <ExclamationCircleOutlined /> + </div> + <span> + {intl.formatMessage({ + id: 'alarm.setting.table.model.alarmStatusSetting', + defaultMessage: '$$$', + })} + 设置数量已达到极限,不能再添加了 + </span> + </li> + )} + </ul> + </ModalForm> + ); +}; +export default AlarmStatusForm; diff --git a/src/pages/Alarm/AlarmSetting/index.tsx b/src/pages/Alarm/AlarmSetting/index.tsx index 3553a3a..f3de489 100644 --- a/src/pages/Alarm/AlarmSetting/index.tsx +++ b/src/pages/Alarm/AlarmSetting/index.tsx @@ -1,33 +1,37 @@ import IsBatchDelete from '@/components/BatchOperation/isBatchDelete'; +import AlarmLevelBox from '@/components/DictionaryBox/alarmLevel'; import TableActionCard from '@/components/TableActionCard'; import IsDelete from '@/components/TableActionCard/isDelete'; +import { alarmLevelStatusEnum } from '@/enums/status'; import { deleteDeviceCategoryDeleteDeviceCategory, deleteDeviceCategoryDeleteDeviceCategoryByIds, postDeviceCategoryGetDeviceCategoryList, } from '@/services/device/DeviceCategory'; -import { PlusOutlined, SettingOutlined } from '@ant-design/icons'; +import { EditOutlined, PlusOutlined, SettingOutlined } from '@ant-design/icons'; import type { ActionType, ProColumns } from '@ant-design/pro-components'; import { PageContainer, ProTable } from '@ant-design/pro-components'; import { Access, FormattedMessage, history, useAccess, useIntl } from '@umijs/max'; -import { Button, message } from 'antd'; +import { Button, Space, message } from 'antd'; import React, { useRef, useState } from 'react'; import { proTableCommonOptions, proTablePaginationOptions } from '../../../../config/defaultTable'; -import { ColumnDrawer } from './components/ColumnDrawer'; import CreateForm from './components/CreateForm'; import UpdateForm from './components/UpdateForm'; +import AlarmStatusForm from './components/alarmStatusForm'; const DeviceCategoryList: React.FC = () => { /** * @en-US Pop-up window of new window * @zh-CN 新建窗口的弹窗 * */ const [createModalOpen, setCreateModalOpen] = useState<boolean>(false); + // 告警状态设置 + const [alarmStatusModalOpen, setAlarmStatusModalOpen] = useState<boolean>(true); + /** * @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); /** * @en-US International configuration * @zh-CN 国际化配置 @@ -40,12 +44,12 @@ const DeviceCategoryList: React.FC = () => { const [currentRow, setCurrentRow] = useState<API.DeviceCategory>(); const [selectedRowsState, setSelectedRows] = useState<API.DeviceCategory[]>([]); - /** // TODO - 1. 告警级别设置 - 2. 告警列表-告警级别渲染 - 3. 项目部署-告警模块 + 1. 告警列表-告警级别渲染-操作按钮 2h + // TODO 讨论 + 1. 告警状态设置 + 2. 项目部署告警添加 */ const handleUpdateModal = () => { @@ -64,12 +68,11 @@ const DeviceCategoryList: React.FC = () => { setCreateModalOpen(true); } }; - const handleColumnDrawer = () => { - if (showDetail) { - setShowDetail(false); - setCurrentRow(undefined); + const handleAlarmStatusModal = () => { + if (alarmStatusModalOpen) { + setAlarmStatusModalOpen(false); } else { - setShowDetail(true); + setAlarmStatusModalOpen(true); } }; const handleDestroy = async (selectedRow: API.DeviceCategory) => { @@ -96,23 +99,28 @@ const DeviceCategoryList: React.FC = () => { hideInSearch: true, }, { - title: ( - <FormattedMessage id="alarm.setting.table.list.category" defaultMessage="$$$" /> - ), + title: <FormattedMessage id="alarm.setting.table.list.category" defaultMessage="$$$" />, dataIndex: 'category', hideInSearch: true, }, { - title: ( - <FormattedMessage id="alarm.setting.table.list.level" defaultMessage="$$$" /> - ), - dataIndex: 'level', - hideInSearch: true, + title: <FormattedMessage id="alarm.setting.table.list.level" defaultMessage="$$$" />, + dataIndex: 'level', + hideInSearch: true, + render: (text, record, index) => { + console.log(text, record, index, 'level_record'); + const currVal = index % 6; + const currLevel = alarmLevelStatusEnum[currVal]; + return ( + <Space> + <AlarmLevelBox {...currLevel}></AlarmLevelBox> + <span style={{ color: currLevel.color }}>{currLevel.defaultLabel}</span> + </Space> + ); }, + }, { - title: ( - <FormattedMessage id="alarm.setting.table.list.createTime" defaultMessage="$$$" /> - ), + title: <FormattedMessage id="alarm.setting.table.list.createTime" defaultMessage="$$$" />, dataIndex: 'createTime', sorter: true, hideInSearch: true, @@ -130,23 +138,34 @@ const DeviceCategoryList: React.FC = () => { { key: 'update', renderDom: ( - <Button + // <Button + // key="update" + // type="link" + // size="small" + // onClick={() => { + // setUpdateModalOpen(true); + // setCurrentRow(record); + // }} + // > + // <FormattedMessage id="common.edit" defaultMessage="Update" /> + // </Button> + <EditOutlined key="update" - type="link" - size="small" + style={{ + color: 'rgba(21, 77, 221, 1)', + }} onClick={() => { setUpdateModalOpen(true); setCurrentRow(record); }} - > - <FormattedMessage id="common.edit" defaultMessage="Update" /> - </Button> + /> ), }, { key: 'destroy', renderDom: ( <IsDelete + buttonType="deleteIcon" deleteApi={() => { handleDestroy(record).then(() => {}); }} @@ -206,14 +225,15 @@ const DeviceCategoryList: React.FC = () => { accessible={access.canUpdate(history.location.pathname)} key={`${history.location.pathname}-add`} > - <Button + <Button type="primary" - key="primary" + key="setting" onClick={() => { - setCreateModalOpen(true); + setAlarmStatusModalOpen(true); }} > - <SettingOutlined /> <FormattedMessage id="alarm.setting.table.list.setting" defaultMessage="New" /> + <SettingOutlined />{' '} + <FormattedMessage id="alarm.setting.table.list.setting" defaultMessage="New" /> </Button> <Button type="primary" @@ -222,7 +242,8 @@ const DeviceCategoryList: React.FC = () => { setCreateModalOpen(true); }} > - <PlusOutlined /> <FormattedMessage id="alarm.setting.table.list.add" defaultMessage="New" /> + <PlusOutlined />{' '} + <FormattedMessage id="alarm.setting.table.list.add" defaultMessage="New" /> </Button> </Access>, ]} @@ -257,7 +278,12 @@ const DeviceCategoryList: React.FC = () => { }, }} /> - + <AlarmStatusForm + alarmStatusModalOpen={alarmStatusModalOpen} + values={currentRow || {}} + handleModal={handleAlarmStatusModal} + reload={actionRef.current?.reload} + /> <CreateForm createModalOpen={createModalOpen} values={currentRow || {}} @@ -270,13 +296,6 @@ const DeviceCategoryList: React.FC = () => { handleModal={handleUpdateModal} reload={actionRef.current?.reload} /> - - <ColumnDrawer - handleDrawer={handleColumnDrawer} - isShowDetail={showDetail} - columns={columns} - currentRow={currentRow} - /> </PageContainer> ); }; diff --git a/src/pages/ComputePowerAllocation/ComputePowerAllocation.tsx b/src/pages/ComputePowerAllocation/ComputePowerAllocation.tsx index 1fa19f0..876e094 100644 --- a/src/pages/ComputePowerAllocation/ComputePowerAllocation.tsx +++ b/src/pages/ComputePowerAllocation/ComputePowerAllocation.tsx @@ -2,7 +2,8 @@ /* eslint-disable react/no-unknown-property */ import { PageContainer, ProCard } from '@ant-design/pro-components'; import { Image } from 'antd'; -import React from 'react'; +import { fabric } from 'fabric'; +import React, { useEffect, useRef, useState } from 'react'; // type import { ComputePowerPoolItem } from './typing'; @@ -61,9 +62,104 @@ const InfoPreview: React.FC<{ const OccupyProportion: React.FC<{ pools: ComputePowerPoolItem[]; }> = ({ pools }) => { + + const poolDefault = { + + buttonOptions: { + width: 20, + height: 40, + fill: 'white', // 填充颜色 + strokeWidth: 2, // 边框宽度 + left: 0, + top: 0, + selectable: false, + } + } + const [clipPathData, setClipPathData] = useState({ + width: 1000, // 宽 + height: 80, // 高 + }); + const canvasRef = useRef(null); + const [cvs, setCanvas] = useState(null); + const [rect1Witdh, setRect1Witdh] = useState(pools[0].proportion/100 * clipPathData.width); + + useEffect(() => { + const canvasObject = new fabric.Canvas(canvasRef.current); + const rect1 = new fabric.Rect({ + top: 3, // 矩形左上角在y轴的位置 + left: 0, // 矩形左上角在x轴的位置 + width: rect1Witdh, // 矩形的宽 + height: 32, // 矩形的高 + fill: '#FAA90B', // 填充色 + stroke: 'transparent', // 边框颜色 + strokeWidth: 2, // 边框宽度 + rx: 15, // 圆角的横向半径 + ry: 15, // 圆角的纵向半径 + selectable: false, + }); + // 创建矩形对象 + const bgRect1 = new fabric.Rect({ + width: 20, + height: 40, + fill: 'white', // 填充颜色 + strokeWidth: 2, // 边框宽度 + left: 0, + top: 0, + selectable: false, + hasControls: false, + }); + // 添加背景图片到矩形 + fabric.Image.fromURL('/images/computePowerAllocation/slideBtn1.png', function (img) { + // 设置图片的宽度和高度为矩形的宽度和高度 + img.set({ width: bgRect1.width, height: bgRect1.height, selectable: false ,hasControls: false}); + // 将图片置于矩形的底部 + // bgRect1.set({ originX: 'center', originY: 'center' }); + canvasObject.add( + new fabric.Group([bgRect1, img], { + width: 20, + height: 40, + left: rect1Witdh - 10, + top: 0, + lockMovementY: true, + hasControls: false, + // selectable: false, + }), + ); + // 更新Canvas以应用更改 + canvasObject.renderAll(); + + }); + canvasObject.add(rect1); + canvasObject.on('object:moving', (event) => { + // 获取事件的目标对象 + const targetObject = event.target; + console.log(targetObject,'targetObject', canvasObject.item(0)) + canvasObject.item(0).set({ + width: targetObject?.left + 10 + }) + // set_category_fk_id_open(false); + + // 检查目标对象是否为组合对象 + setRect1Witdh(targetObject?.left) + canvasObject.renderAll(); + }); + canvasObject.renderAll(); + + setCanvas(canvasObject); + return () => { + canvasObject.dispose(); + }; + }, []); + return ( <div className="occupyProportion_box"> - <div className="occupy_up" style={{ marginTop: 24, marginBottom: 20, padding: 0, position: 'relative', width: '100%' }}> + <div + className="occupy_up" + style={{ marginTop: 24, marginBottom: 20, padding: 0, position: 'relative', width: '100%' }} + > + {/* <canvas ref={canvasRef} {...clipPathData} style={{ border: '1px dashed red' }}></canvas> */} + + <ul className="occupy_progress" style={{ @@ -100,7 +196,11 @@ const OccupyProportion: React.FC<{ width: 20, }} > - <Image width={20} src={'/images/computePowerAllocation/slideBtn1.png'} preview={false}></Image> + <Image + width={20} + src={'/images/computePowerAllocation/slideBtn1.png'} + preview={false} + ></Image> </li> <li style={{ @@ -110,7 +210,11 @@ const OccupyProportion: React.FC<{ width: 20, }} > - <Image width={20} src={'/images/computePowerAllocation/slideBtn2.png'} preview={false}></Image> + <Image + width={20} + src={'/images/computePowerAllocation/slideBtn2.png'} + preview={false} + ></Image> </li> </ul> </div> @@ -121,12 +225,12 @@ const OccupyProportion: React.FC<{ display: 'flex', paddingLeft: 0, paddingBottom: 24, - margin: 0 + margin: 0, }} > {pools.map((item, index) => { return ( - <li key={index} style={{marginRight: 35}}> + <li key={index} style={{ marginRight: 35 }}> <span style={{ width: 12, @@ -140,7 +244,8 @@ const OccupyProportion: React.FC<{ </span> {item.type != 2 ? ( <span style={{ paddingLeft: 8 }}> - 预计处理效率:<span style={{ fontWeight: 700 }} >{item.PretreatmentEfficiency} 帧/秒</span> + 预计处理效率: + <span style={{ fontWeight: 700 }}>{item.PretreatmentEfficiency} 帧/秒</span> </span> ) : ( <></> @@ -171,8 +276,6 @@ const ComputePowerAllocation: React.FC = () => { paddingTop: 0, paddingBottom: 0, margin: 0, - - }} gutter={24} wrap @@ -196,7 +299,9 @@ const ComputePowerAllocation: React.FC = () => { <Image width={62} src={'/images/computePowerAllocation/icon1.png'} - placeholder={<Image preview={false} src={'/images/computePowerAllocation/icon1.png'} width={62} />} + placeholder={ + <Image preview={false} src={'/images/computePowerAllocation/icon1.png'} width={62} /> + } /> <div style={{ diff --git a/src/pages/Setting/AlgorithmSetting.tsx b/src/pages/Setting/AlgorithmSetting.tsx index e82b48e..2096e89 100644 --- a/src/pages/Setting/AlgorithmSetting.tsx +++ b/src/pages/Setting/AlgorithmSetting.tsx @@ -7,11 +7,16 @@ import TreeAndTableList, { ProCardTypeProps } from '@/layouts/treeAndTableList'; import { postProjectGetProjectByGroupId } from '@/services/project/Project'; import { postModelVersionGetModelVersionListByIds } from '@/services/resource/ModelVersion'; import { FormattedMessage } from '@@/exports'; -import { CaretRightOutlined, PauseOutlined } from '@ant-design/icons'; +import { + CaretRightOutlined, + DatabaseOutlined, + PauseOutlined, + WarningOutlined, +} from '@ant-design/icons'; import type { ActionType } from '@ant-design/pro-components'; import { PageContainer, ProCard, ProDescriptions, ProList } from '@ant-design/pro-components'; import { useAccess, useIntl } from '@umijs/max'; -import { Button, Tag, message } from 'antd'; +import { Button, Tabs, Tag, message } from 'antd'; import Tree, { DataNode } from 'antd/es/tree'; import React, { useEffect, useRef, useState } from 'react'; import ProjectCard from './components/ProjectCard'; @@ -42,7 +47,7 @@ const AlgorithmSetting: React.FC = () => { const [tabs, setTabs] = useState([]); const [cardActionProps, setCardActionProps] = useState<'actions' | 'extra'>('extra'); const [modelVersionData, setModelVersionData] = useState<any[]>([]); - const [projectData, setProjectData] = useState<Record<string,any>>({}); + const [projectData, setProjectData] = useState<Record<string, any>>({}); const [targetKeys, setTargetKeys] = useState<string[]>([]); const [projectConfigId, setProjectConfigId] = useState<number>(0); const handleUpdateModal = () => { @@ -197,8 +202,8 @@ const AlgorithmSetting: React.FC = () => { key={item.configId} style={{ padding: 0, fontWeight: 700 }} onClick={() => { - setProjectData(item) - console.log(item,'setProjectData_item', projectData) + setProjectData(item); + console.log(item, 'setProjectData_item', projectData); setProjectConfigId(item.configId); handleUpdateModal(); }} @@ -213,10 +218,13 @@ const AlgorithmSetting: React.FC = () => { // console.log(88, model_data); // setProjectData({...projectData, [v.id]: v}) return { - label: `项目:${v.name}`, + label: <Button type="default">{v.name}</Button>, key: v.id, children: ( <ProList<any> + style={{ + padding: '0px 16px 16px' + }} className="gn" ghost={true} itemCardProps={{ @@ -241,7 +249,7 @@ const AlgorithmSetting: React.FC = () => { console.log(action, rows, 'toolBarRender'); // TODO 需要对接接口 const isProcess: boolean = false; - return isProcess ? ( + const currNode = isProcess ? ( <Button type="primary" danger> <PauseOutlined style={{ fontSize: 14, lineHeight: 1.5 }} /> 结束运行 @@ -252,6 +260,7 @@ const AlgorithmSetting: React.FC = () => { 开始运行 </Button> ); + return [currNode]; }} dataSource={model_data} /> @@ -354,9 +363,9 @@ const AlgorithmSetting: React.FC = () => { <div className="gn"> <ProCard title="网点详细信息" - headStyle={{borderBottom: '1px solid #E0E0E0', paddingTop: 0, paddingBottom:12 }} - style={{ background: 'transparent' , paddingTop: 24 }} - bodyStyle={{ paddingBottom: 0 }} + headStyle={{ borderBottom: '1px solid #E0E0E0', padding: '0px 16px 16px'}} + style={{ background: 'transparent', paddingTop: 16 }} + bodyStyle={{ padding: '16px 16px 0px' }} colSpan="80%" > <ProDescriptions @@ -365,16 +374,47 @@ const AlgorithmSetting: React.FC = () => { columns={columns} ></ProDescriptions> </ProCard> - <ProCard - wrap - tabs={{ + {/* tabs={{ activeKey: tab, items: tabs, onChange: (key) => { changeProjectTab(key); }, - }} - ></ProCard> + }} */} + + <Tabs + items={[ + { + key: '1', + label: ( + <> + <DatabaseOutlined></DatabaseOutlined> <span>项目</span> + </> + ), + children: ( + <Tabs + className='gn algorithmSetting_project_box' + type={'line'} + activeKey={tab} + items={tabs} + onChange={(key) => { + changeProjectTab(key); + }} + ></Tabs> + ), + }, + { + key: '2', + label: ( + <> + <WarningOutlined></WarningOutlined> + <span>告警</span> + </> + ), + children: (<ProCard style={{padding: 0, margin: 0}} bodyStyle={{padding: 16}}>告警部分</ProCard>), + }, + ]} + /> </div> } ></TreeAndTableList> diff --git a/src/utils/storage.js b/src/utils/storage.js new file mode 100644 index 0000000..602378a --- /dev/null +++ b/src/utils/storage.js @@ -0,0 +1,78 @@ +// TODO 改为ts格式 +/** + * Set storage + * + * @param name + * @param content + * @param maxAge + */ +export const setStore = (name, content, maxAge = null) => { + if (!global.window || !name) { + return + } + + if (typeof content !== 'string') { + content = JSON.stringify(content) + } + + const storage = global.window.localStorage + + storage.setItem(name, content) + if (maxAge && !isNaN(parseInt(maxAge))) { + const timeout = parseInt(new Date().getTime() / 1000) + storage.setItem(`${name}_expire`, timeout + maxAge) + } + } + + /** + * Get storage + * + * @param name + * @returns {*} + */ + export const getStore = name => { + if (!global.window || !name) { + return + } + + const content = window.localStorage.getItem(name) + const _expire = window.localStorage.getItem(`${name}_expire`) + + if (_expire) { + const now = parseInt(new Date().getTime() / 1000) + if (now > _expire) { + return + } + } + + try { + return JSON.parse(content) + } catch (e) { + return content + } + } + + /** + * Clear storage + * + * @param name + */ + export const clearStore = name => { + if (!global.window || !name) { + return + } + + window.localStorage.removeItem(name) + window.localStorage.removeItem(`${name}_expire`) + } + + /** + * Clear all storage + */ + export const clearAll = () => { + if (!global.window || !name) { + return + } + + window.localStorage.clear() + } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index a9ad888..9fe8cd8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,5 +23,5 @@ } }, - "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"] + "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx", "src/utils/storage.js"] }