Przeglądaj źródła

fix: 合并冲突

sun-chaochao 3 lat temu
rodzic
commit
629b2895e4

+ 9 - 1
src/pages/device/Instance/Detail/index.tsx

@@ -26,6 +26,7 @@ import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { PermissionButton } from '@/components';
 import { QuestionCircleOutlined } from '@ant-design/icons';
 import Service from '@/pages/device/Instance/service';
+import useLocation from '@/hooks/route/useLocation';
 
 export const deviceStatus = new Map();
 deviceStatus.set('online', <Badge status="success" text={'在线'} />);
@@ -38,6 +39,7 @@ const InstanceDetail = observer(() => {
   const params = useParams<{ id: string }>();
   const service = new Service('device-instance');
   const { permission } = PermissionButton.usePermission('device/Instance');
+  const location = useLocation();
 
   // const resetMetadata = async () => {
   //   const resp = await service.deleteMetadata(params.id);
@@ -155,7 +157,6 @@ const InstanceDetail = observer(() => {
 
   const getDetail = (id: string) => {
     service.detail(id).then((response) => {
-      console.log(response.result);
       InstanceModel.detail = response?.result;
       const datalist = [...baseList];
       if (response.result.protocol === 'modbus-tcp') {
@@ -244,6 +245,13 @@ const InstanceDetail = observer(() => {
     return () => subscription.unsubscribe();
   }, []);
 
+  useEffect(() => {
+    const { state } = location;
+    if (state && state?.tab) {
+      setTab(state?.tab);
+    }
+  }, [location]);
+
   return (
     <PageContainer
       className={'page-title-show'}

+ 2 - 0
src/pages/device/Instance/index.tsx

@@ -93,6 +93,8 @@ const Instance = () => {
       if (location.state && location.state?.save) {
         setVisible(true);
         setCurrent({});
+      } else if (location.state && location.state?.import) {
+        setImportVisible(true);
       }
     }
   }, [location]);

+ 9 - 2
src/pages/device/Product/Detail/index.tsx

@@ -1,11 +1,11 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { useIntl, useLocation, useParams } from 'umi';
+import { useIntl, useParams } from 'umi';
 import { Badge, Card, Descriptions, message, Popconfirm, Space, Spin, Switch, Tooltip } from 'antd';
 import BaseInfo from '@/pages/device/Product/Detail/BaseInfo';
 import { observer } from '@formily/react';
 import { productModel, service } from '@/pages/device/Product';
 import { useCallback, useEffect, useState } from 'react';
-import { useHistory } from '@/hooks';
+import { useHistory, useLocation } from '@/hooks';
 import Metadata from '@/pages/device/components/Metadata';
 import Access from '@/pages/device/Product/Detail/Access';
 import type { DeviceMetadata } from '@/pages/device/Product/typings';
@@ -217,6 +217,13 @@ const ProductDetail = observer(() => {
     return () => subscription.unsubscribe();
   }, []);
 
+  useEffect(() => {
+    const { state } = location;
+    if (state && state?.tab) {
+      setMode(state?.tab);
+    }
+  }, [location]);
+
   return (
     <PageContainer
       className={'page-title-show'}

+ 163 - 0
src/pages/home/components/DeviceChoose.tsx

@@ -0,0 +1,163 @@
+import { message, Modal } from 'antd';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
+import { service } from '@/pages/device/Instance';
+import SearchComponent from '@/components/SearchComponent';
+import type { DeviceItem } from '@/pages/media/Home/typings';
+import { useEffect, useRef, useState } from 'react';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { BadgeStatus, PermissionButton } from '@/components';
+import { StatusColorEnum } from '@/components/BadgeStatus';
+import useHistory from '@/hooks/route/useHistory';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+
+interface DeviceModalProps {
+  visible: boolean;
+  url?: string;
+  onCancel: () => void;
+}
+
+export default (props: DeviceModalProps) => {
+  const intl = useIntl();
+  const history = useHistory();
+  const permission = PermissionButton.usePermission('device/Instance').permission;
+
+  const actionRef = useRef<ActionType>();
+  const [searchParam, setSearchParam] = useState({});
+  const [deviceItem, setDeviceItem] = useState<any>({});
+
+  useEffect(() => {
+    if (!props.visible) {
+      setDeviceItem({});
+      setSearchParam({});
+    }
+  }, [props.visible]);
+
+  const cancel = () => {
+    if (props.onCancel) {
+      props.onCancel();
+    }
+  };
+
+  const columns: ProColumns<DeviceItem>[] = [
+    {
+      dataIndex: 'id',
+      title: '设备ID',
+      width: 220,
+    },
+    {
+      dataIndex: 'name',
+      title: '设备名称',
+    },
+    {
+      dataIndex: 'productName',
+      title: '产品名称',
+    },
+    {
+      dataIndex: 'modifyTime',
+      title: '注册时间',
+      valueType: 'dateTime',
+      width: 200,
+    },
+    {
+      dataIndex: 'state',
+      title: intl.formatMessage({
+        id: 'pages.searchTable.titleStatus',
+        defaultMessage: '状态',
+      }),
+      render: (_, record) => (
+        <BadgeStatus
+          status={record.state.value}
+          statusNames={{
+            online: StatusColorEnum.success,
+            offline: StatusColorEnum.error,
+            notActive: StatusColorEnum.processing,
+          }}
+          text={record.state.text}
+        />
+      ),
+      valueType: 'select',
+      valueEnum: {
+        offline: {
+          text: intl.formatMessage({
+            id: 'pages.device.instance.status.offLine',
+            defaultMessage: '离线',
+          }),
+          status: 'offline',
+        },
+        online: {
+          text: intl.formatMessage({
+            id: 'pages.device.instance.status.onLine',
+            defaultMessage: '在线',
+          }),
+          status: 'online',
+        },
+      },
+      filterMultiple: false,
+    },
+  ];
+
+  return (
+    <Modal
+      title={'选择设备'}
+      onCancel={cancel}
+      onOk={() => {
+        if (deviceItem?.id) {
+          if (!!permission.update) {
+            history.push(
+              `${getMenuPathByParams(MENUS_CODE['device/Instance/Detail'], deviceItem.id)}`,
+              {
+                tab: 'diagnose',
+              },
+            );
+          } else {
+            message.warning('暂无权限,请联系管理员');
+            cancel();
+          }
+        } else {
+          message.warning('请选择设备');
+        }
+      }}
+      destroyOnClose={true}
+      maskClosable={false}
+      visible={props.visible}
+      width={1000}
+    >
+      <SearchComponent<DeviceItem>
+        field={columns}
+        enableSave={false}
+        model="simple"
+        onSearch={async (data) => {
+          setSearchParam(data);
+        }}
+        target="choose-device"
+      />
+      <ProTable<DeviceItem>
+        actionRef={actionRef}
+        columns={columns}
+        rowKey={'id'}
+        search={false}
+        request={(params) =>
+          service.query({
+            ...params,
+            sorts: [
+              {
+                name: 'createTime',
+                order: 'desc',
+              },
+            ],
+          })
+        }
+        rowSelection={{
+          type: 'radio',
+          selectedRowKeys: deviceItem.id ? [deviceItem.id] : undefined,
+          onSelect: (record) => {
+            setDeviceItem(record);
+          },
+        }}
+        tableAlertOptionRender={() => false}
+        params={searchParam}
+      />
+    </Modal>
+  );
+};

+ 119 - 0
src/pages/home/components/ProductChoose.tsx

@@ -0,0 +1,119 @@
+import { FormItem, FormLayout, Select } from '@formily/antd';
+import { createForm } from '@formily/core';
+import { createSchemaField, FormProvider } from '@formily/react';
+import { Button, message, Modal } from 'antd';
+import 'antd/lib/tree-select/style/index.less';
+import { useEffect, useState } from 'react';
+import { service } from '@/pages/device/Instance';
+import encodeQuery from '@/utils/encodeQuery';
+import { PermissionButton } from '@/components';
+import useHistory from '@/hooks/route/useHistory';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+
+interface Props {
+  visible: boolean;
+  close: () => void;
+}
+
+const ProductChoose = (props: Props) => {
+  const productPermission = PermissionButton.usePermission('device/Product').permission;
+  const { visible, close } = props;
+  const [productList, setProductList] = useState<any[]>([]);
+
+  const SchemaField = createSchemaField({
+    components: {
+      Select,
+      FormItem,
+      FormLayout,
+    },
+  });
+
+  useEffect(() => {
+    service.getProductList(encodeQuery({ paging: false, terms: { state: 1 } })).then((resp) => {
+      if (resp.status === 200) {
+        const list = resp.result.map((item: { name: any; id: any }) => ({
+          label: item.name,
+          value: item.id,
+        }));
+        setProductList(list);
+      }
+    });
+  }, []);
+
+  const form = createForm({});
+
+  const schema = {
+    type: 'object',
+    properties: {
+      layout: {
+        type: 'void',
+        'x-component': 'FormLayout',
+        'x-component-props': {
+          layout: 'vertical',
+        },
+        properties: {
+          product: {
+            type: 'string',
+            title: '产品',
+            required: true,
+            'x-decorator': 'FormItem',
+            'x-component': 'Select',
+            enum: [...productList],
+            'x-component-props': {
+              showSearch: true,
+              showArrow: true,
+              filterOption: (input: string, option: any) =>
+                option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
+            },
+          },
+        },
+      },
+    },
+  };
+
+  const history = useHistory();
+
+  return (
+    <Modal
+      maskClosable={false}
+      visible={visible}
+      onCancel={() => close()}
+      width="35vw"
+      title="选择产品"
+      onOk={() => close()}
+      footer={[
+        <Button key="cancel" onClick={() => close()}>
+          取消
+        </Button>,
+        <Button
+          key="ok"
+          type="primary"
+          onClick={async () => {
+            const data: any = await form.submit();
+            const path = getMenuPathByParams(`device/Product/Detail`);
+            if (path && !!productPermission.update) {
+              history.push(
+                `${getMenuPathByParams(MENUS_CODE['device/Product/Detail'], data.product)}`,
+                {
+                  tab: 'access',
+                },
+              );
+            } else {
+              message.warning('暂无权限,请联系管理员');
+              close();
+            }
+          }}
+        >
+          确认
+        </Button>,
+      ]}
+    >
+      <div style={{ marginTop: '20px' }}>
+        <FormProvider form={form}>
+          <SchemaField schema={schema} />
+        </FormProvider>
+      </div>
+    </Modal>
+  );
+};
+export default ProductChoose;

+ 1 - 1
src/pages/home/components/Steps.tsx

@@ -17,7 +17,7 @@ interface StepsProps {
 const ItemDefaultImg = require('/public/images/home/bottom-1.png');
 const StepsItem = (props: StepItemProps) => {
   return (
-    <div className={'step-item step-bar arrow-1'}>
+    <div className={'step-item step-bar arrow-1'} onClick={props.onClick}>
       <div className={'step-item-title'}>
         <div className={'step-item-img'}>
           <img src={props.url || ItemDefaultImg} />

+ 130 - 6
src/pages/home/comprehensive/index.tsx

@@ -1,16 +1,19 @@
 import { PermissionButton } from '@/components';
 import useHistory from '@/hooks/route/useHistory';
 import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
-import { Col, message, Row } from 'antd';
+import { Col, message, Row, Tooltip } from 'antd';
 import Body from '../components/Body';
 import Guide from '../components/Guide';
 import Statistics from '../components/Statistics';
-// import Steps from '../components/Steps';
+import Steps from '../components/Steps';
 import { service } from '..';
 import { useEffect, useState } from 'react';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { map } from 'rxjs';
 import Pie from '../components/Pie';
+import { QuestionCircleOutlined } from '@ant-design/icons';
+import ProductChoose from '../components/ProductChoose';
+import DeviceChoose from '../components/DeviceChoose';
 
 const Comprehensive = () => {
   const [subscribeTopic] = useSendWebsocketMessage();
@@ -25,6 +28,8 @@ const Comprehensive = () => {
   const [deviceCount, setDeviceCount] = useState<number>(0);
   const [cpuValue, setCpuValue] = useState<number>(0);
   const [jvmValue, setJvmValue] = useState<number>(0);
+  const [productVisible, setProductVisible] = useState<boolean>(false);
+  const [deviceVisible, setDeviceVisible] = useState<boolean>(false);
 
   const getProductCount = async () => {
     const resp = await service.productCount({});
@@ -222,12 +227,131 @@ const Comprehensive = () => {
       <Col span={24}>
         <Body title={'平台架构图'} english={'PLATFORM ARCHITECTURE DIAGRAM'} />
       </Col>
-      {/* <Col span={24}>
-        <Steps />
+      <Col span={24}>
+        <Steps
+          title={
+            <span>
+              设备接入推荐步骤
+              <Tooltip title={'不同的设备因为通信协议的不用,存在接入步骤的差异'}>
+                <QuestionCircleOutlined style={{ paddingLeft: 12 }} />
+              </Tooltip>
+            </span>
+          }
+          data={[
+            {
+              title: '创建产品',
+              content:
+                '产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
+              onClick: () => {
+                const path = getMenuPathByCode('device/Product');
+                if (path && !!productPermission.add) {
+                  history.push(`${path}`, {
+                    save: true,
+                  });
+                } else {
+                  message.warning('暂无权限,请联系管理员');
+                }
+              },
+            },
+            {
+              title: '配置产品接入方式',
+              content:
+                '通过产品对同一类型的所有设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
+              onClick: () => {
+                setProductVisible(true);
+              },
+            },
+            {
+              title: '添加测试设备',
+              content: '添加单个设备,用于验证产品模型是否配置正确。',
+              onClick: () => {
+                const path = getMenuPathByCode('device/Instance');
+                if (path && !!devicePermission.add) {
+                  history.push(`${path}`, {
+                    save: true,
+                  });
+                } else {
+                  message.warning('暂无权限,请联系管理员');
+                }
+              },
+            },
+            {
+              title: '功能调试',
+              content: '对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
+              onClick: () => {
+                setDeviceVisible(true);
+              },
+            },
+            {
+              title: '批量添加设备',
+              content: '批量添加同一产品下的设备',
+              onClick: () => {
+                const path = getMenuPathByCode('device/Instance');
+                if (path && !!devicePermission.import) {
+                  history.push(`${path}`, {
+                    import: true,
+                  });
+                } else {
+                  message.warning('暂无权限,请联系管理员');
+                }
+              },
+            },
+          ]}
+        />
       </Col>
       <Col span={24}>
-        <Steps />
-      </Col> */}
+        <Steps
+          title={
+            <span>
+              设备接入推荐步骤
+              <Tooltip title={'不同的设备因为通信协议的不用,存在接入步骤的差异'}>
+                <QuestionCircleOutlined style={{ paddingLeft: 12 }} />
+              </Tooltip>
+            </span>
+          }
+          data={[
+            {
+              title: '创建产品',
+              content:
+                '产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
+              onClick: () => {},
+            },
+            {
+              title: '配置产品接入方式',
+              content:
+                '通过产品对同一类型的所有设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
+              onClick: () => {},
+            },
+            {
+              title: '添加测试设备',
+              content: '添加单个设备,用于验证产品模型是否配置正确。',
+              onClick: () => {},
+            },
+            {
+              title: '功能调试',
+              content: '对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
+              onClick: () => {},
+            },
+            {
+              title: '批量添加设备',
+              content: '批量添加同一产品下的设备',
+              onClick: () => {},
+            },
+          ]}
+        />
+      </Col>
+      <ProductChoose
+        visible={productVisible}
+        close={() => {
+          setProductVisible(false);
+        }}
+      />
+      <DeviceChoose
+        visible={deviceVisible}
+        onCancel={() => {
+          setDeviceVisible(false);
+        }}
+      />
     </Row>
   );
 };

+ 53 - 5
src/pages/home/device/index.tsx

@@ -8,6 +8,8 @@ import { useHistory } from '@/hooks';
 import { service } from '..';
 import { useEffect, useState } from 'react';
 import { QuestionCircleOutlined } from '@ant-design/icons';
+import ProductChoose from '../components/ProductChoose';
+import DeviceChoose from '../components/DeviceChoose';
 
 const Device = () => {
   const productPermission = PermissionButton.usePermission('device/Product').permission;
@@ -17,6 +19,9 @@ const Device = () => {
   const [productCount, setProductCount] = useState<number>(0);
   const [deviceCount, setDeviceCount] = useState<number>(0);
 
+  const [productVisible, setProductVisible] = useState<boolean>(false);
+  const [deviceVisible, setDeviceVisible] = useState<boolean>(false);
+
   const getProductCount = async () => {
     const resp = await service.productCount({});
     if (resp.status === 200) {
@@ -134,32 +139,75 @@ const Device = () => {
               title: '创建产品',
               content:
                 '产品是设备的集合,通常指一组具有相同功能的设备。物联设备必须通过产品进行接入方式配置。',
-              onClick: () => {},
+              onClick: () => {
+                const path = getMenuPathByCode('device/Product');
+                if (path && !!productPermission.add) {
+                  history.push(`${path}`, {
+                    save: true,
+                  });
+                } else {
+                  message.warning('暂无权限,请联系管理员');
+                }
+              },
             },
             {
               title: '配置产品接入方式',
               content:
                 '通过产品对同一类型的所有设备进行统一的接入方式配置。请参照设备铭牌说明选择匹配的接入方式。',
-              onClick: () => {},
+              onClick: () => {
+                setProductVisible(true);
+              },
             },
             {
               title: '添加测试设备',
               content: '添加单个设备,用于验证产品模型是否配置正确。',
-              onClick: () => {},
+              onClick: () => {
+                const path = getMenuPathByCode('device/Instance');
+                if (path && !!devicePermission.add) {
+                  history.push(`${path}`, {
+                    save: true,
+                  });
+                } else {
+                  message.warning('暂无权限,请联系管理员');
+                }
+              },
             },
             {
               title: '功能调试',
               content: '对添加的测试设备进行功能调试,验证能否连接到平台,设备功能是否配置正确。',
-              onClick: () => {},
+              onClick: () => {
+                setDeviceVisible(true);
+              },
             },
             {
               title: '批量添加设备',
               content: '批量添加同一产品下的设备',
-              onClick: () => {},
+              onClick: () => {
+                const path = getMenuPathByCode('device/Instance');
+                if (path && !!devicePermission.import) {
+                  history.push(`${path}`, {
+                    import: true,
+                  });
+                } else {
+                  message.warning('暂无权限,请联系管理员');
+                }
+              },
             },
           ]}
         />
       </Col>
+      <ProductChoose
+        visible={productVisible}
+        close={() => {
+          setProductVisible(false);
+        }}
+      />
+      <DeviceChoose
+        visible={deviceVisible}
+        onCancel={() => {
+          setDeviceVisible(false);
+        }}
+      />
     </Row>
   );
 };