瀏覽代碼

feat: 设备运行状态-事件

sun-chaochao 3 年之前
父節點
當前提交
fc8f1de5b8

+ 0 - 5
src/pages/device/Instance/Detail/MetadataLog/columns.ts

@@ -3,11 +3,6 @@ import moment from 'moment';
 
 const columns: ProColumns<MetadataLogData>[] = [
   {
-    dataIndex: 'index',
-    valueType: 'indexBorder',
-    width: 48,
-  },
-  {
     dataIndex: 'timestamp',
     title: '时间',
     sorter: true,

+ 0 - 82
src/pages/device/Instance/Detail/Running/Event.tsx

@@ -1,82 +0,0 @@
-import { SyncOutlined, UnorderedListOutlined } from '@ant-design/icons';
-import { Badge, Divider, Tooltip } from 'antd';
-import ProCard from '@ant-design/pro-card';
-import type { EventMetadata, ObserverMetadata } from '@/pages/device/Product/typings';
-import { useEffect, useRef, useState } from 'react';
-import { service } from '@/pages/device/Instance';
-import { useParams } from 'umi';
-import EventLog from '@/pages/device/Instance/Detail/MetadataLog/Event';
-
-interface Props {
-  data: Partial<EventMetadata> & ObserverMetadata;
-}
-
-const eventLevel = new Map();
-eventLevel.set('ordinary', <Badge status="processing" text="普通" />);
-eventLevel.set('warn', <Badge status="warning" text="警告" />);
-eventLevel.set('urgent', <Badge status="error" text="紧急" />);
-
-const Event = (props: Props) => {
-  const { data } = props;
-  const params = useParams<{ id: string }>();
-
-  const [count, setCount] = useState<number>(0);
-  const cacheCount = useRef<number>(count);
-
-  const [loading, setLoading] = useState<boolean>(false);
-  const initCount = () => {
-    setLoading(true);
-    if (data.id) {
-      service
-        .getEventCount(params.id, data.id, {
-          format: true,
-          pageSize: 1,
-        })
-        .then((resp) => {
-          if (resp.status === 200) {
-            setCount(resp.result?.total);
-            cacheCount.current = resp.result?.total;
-          }
-        })
-        .finally(() => setLoading(false));
-    }
-  };
-  useEffect(() => {
-    initCount();
-    data.subscribe((payload: unknown) => {
-      if (payload) {
-        cacheCount.current = cacheCount.current + 1;
-        setCount(cacheCount.current);
-      }
-    });
-  }, [data.id, params.id]);
-  const [visible, setVisible] = useState<boolean>(false);
-
-  return (
-    <ProCard
-      title={`${data.name}: ${count}`}
-      extra={
-        <>
-          <SyncOutlined onClick={initCount} />
-          <Divider type="vertical" />
-          <Tooltip placement="top" title="详情">
-            <UnorderedListOutlined
-              onClick={() => {
-                setVisible(true);
-              }}
-            />
-          </Tooltip>
-        </>
-      }
-      loading={loading}
-      layout="center"
-      bordered
-      headerBordered
-      colSpan={{ xs: 12, sm: 8, md: 6, lg: 6, xl: 6 }}
-    >
-      <div style={{ height: 60 }}>{eventLevel.get(data.expands?.level || 'warn')}</div>
-      <EventLog visible={visible} close={() => setVisible(false)} data={data} />
-    </ProCard>
-  );
-};
-export default Event;

+ 131 - 0
src/pages/device/Instance/Detail/Running/Event/index.tsx

@@ -0,0 +1,131 @@
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
+import { service } from '@/pages/device/Instance';
+import { useParams } from 'umi';
+import type { EventMetadata } from '@/pages/device/Product/typings';
+import SearchComponent from '@/components/SearchComponent';
+import moment from 'moment';
+import { Form, Modal } from 'antd';
+import { SearchOutlined } from '@ant-design/icons';
+import { useRef, useState } from 'react';
+import MonacoEditor from 'react-monaco-editor';
+import encodeQuery from '@/utils/encodeQuery';
+
+interface Props {
+  data: Partial<EventMetadata>;
+}
+
+const EventLog = (props: Props) => {
+  const params = useParams<{ id: string }>();
+  const { data } = props;
+  const actionRef = useRef<ActionType>();
+  const [searchParams, setSearchParams] = useState<any>({});
+
+  const columns: ProColumns<MetadataLogData>[] = [
+    {
+      dataIndex: 'timestamp',
+      title: '时间',
+      sorter: true,
+      renderText: (text: string) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
+    },
+    {
+      dataIndex: 'option',
+      title: '操作',
+      render: (text, record) => [
+        <a
+          key={'option'}
+          onClick={() => {
+            for (const i in record) {
+              if (i.indexOf('_format') != -1) {
+                delete record[i];
+              }
+            }
+            Modal.info({
+              title: '详情',
+              width: 850,
+              content: (
+                <Form.Item wrapperCol={{ span: 22 }} labelCol={{ span: 2 }} label={data.name}>
+                  <MonacoEditor
+                    height={350}
+                    theme="vs"
+                    language="json"
+                    value={JSON.stringify(record, null, 2)}
+                  />
+                </Form.Item>
+              ),
+              okText: '关闭',
+              onOk() {},
+            });
+          }}
+        >
+          <SearchOutlined />
+        </a>,
+      ],
+    },
+  ];
+
+  const createColumn = (): ProColumns[] =>
+    data.valueType?.type === 'object'
+      ? data.valueType.properties.map(
+          (i: any) =>
+            ({
+              key: i.id,
+              title: i.name,
+              dataIndex: i.id,
+              renderText: (text) => (typeof text === 'object' ? JSON.stringify(text) : text),
+            } as ProColumns),
+        )
+      : [
+          {
+            title: '数据',
+            dataIndex: 'value',
+            ellipsis: true,
+            // render: (text) => text ? JSON.stringify(text) : ''
+          },
+        ];
+
+  return (
+    <div>
+      <SearchComponent<any>
+        field={[...createColumn(), ...columns]}
+        target="events"
+        pattern={'simple'}
+        onSearch={(param) => {
+          // 重置分页数据
+          actionRef.current?.reset?.();
+          setSearchParams(param);
+        }}
+        onReset={() => {
+          // 重置分页及搜索参数
+          actionRef.current?.reset?.();
+          setSearchParams({});
+        }}
+      />
+      <ProTable
+        size="small"
+        rowKey="id"
+        actionRef={actionRef}
+        search={false}
+        params={searchParams}
+        toolBarRender={false}
+        request={async (param) => {
+          param.pageIndex = param.current - 1;
+          delete param.current;
+          delete param.total;
+          return service.getEventCount(
+            params.id,
+            data.id!,
+            encodeQuery({
+              ...param,
+            }),
+          );
+        }}
+        pagination={{
+          pageSize: 10,
+        }}
+        columns={[...createColumn(), ...columns]}
+      />
+    </div>
+  );
+};
+export default EventLog;

+ 13 - 7
src/pages/device/Instance/Detail/Running/Property/index.tsx

@@ -79,25 +79,31 @@ const Property = (props: Props) => {
       title: '操作',
       key: 'action',
       render: (text: any, record: any) => (
-        <Space size="middle" style={{ color: '#1d39c4' }}>
+        <Space size="middle">
           {(record.expands?.readOnly === false || record.expands?.readOnly === 'false') && (
-            <EditOutlined
+            <a
               onClick={() => {
                 setVisible(true);
               }}
-            />
+            >
+              <EditOutlined />
+            </a>
           )}
-          <SyncOutlined
+          <a
             onClick={() => {
               refreshProperty(record?.id);
             }}
-          />
-          <UnorderedListOutlined
+          >
+            <SyncOutlined />
+          </a>
+          <a
             onClick={() => {
               setCurrentInfo(record);
               setInfoVisible(true);
             }}
-          />
+          >
+            <UnorderedListOutlined />
+          </a>
         </Space>
       ),
     },

+ 7 - 169
src/pages/device/Instance/Detail/Running/index.tsx

@@ -1,185 +1,23 @@
 import { InstanceModel } from '@/pages/device/Instance';
 import { Card, Tabs } from 'antd';
 import type { DeviceMetadata } from '@/pages/device/Product/typings';
-// import { useIntl } from '@@/plugin-locale/localeExports';
-import { useEffect } from 'react';
 import Property from '@/pages/device/Instance/Detail/Running/Property';
-// import Event from '@/pages/device/Instance/Detail/Running/Event';
-// import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
-// import { map } from 'rxjs/operators';
-// import moment from 'moment';
-// import { deviceStatus } from '@/pages/device/Instance/Detail';
-
-// const ColResponsiveProps = {
-//   xs: 24,
-//   sm: 12,
-//   md: 12,
-//   lg: 12,
-//   xl: 6,
-//   style: { marginBottom: 24 },
-// };
+import Event from '@/pages/device/Instance/Detail/Running/Event';
 
 const Running = () => {
-  // const intl = useIntl();
   const metadata = JSON.parse(InstanceModel.detail.metadata as string) as DeviceMetadata;
 
-  // const device = InstanceModel.detail;
-  // const [subscribeTopic] = useSendWebsocketMessage();
-
-  // const addObserver = (item: Record<string, any>) => {
-  //   item.listener = [];
-  //   item.subscribe = (callback: () => void) => {
-  //     item.listener.push(callback);
-  //   };
-  //   item.next = (data: any) => {
-  //     item.listener.forEach((element: any) => {
-  //       element(data);
-  //     });
-  //   };
-  //   return item;
-  // };
-  // metadata.events = metadata.events.map(addObserver);
-  // metadata.properties = metadata.properties.map(addObserver);
-  // const [propertiesList, setPropertiesList] = useState<string[]>(
-  //   metadata.properties.map((item: any) => item.id),
-  // );
-
-  /**
-   * 订阅属性数据
-   */
-  // const subscribeProperty = () => {
-  //   const id = `instance-info-property-${device.id}-${device.productId}-${propertiesList.join(
-  //     '-',
-  //   )}`;
-  //   const topic = `/dashboard/device/${device.productId}/properties/realTime`;
-  //   subscribeTopic!(id, topic, {
-  //     deviceId: device.id,
-  //     properties: propertiesList,
-  //     history: 0,
-  //   })
-  //     ?.pipe(map((res) => res.payload))
-  //     .subscribe((payload: any) => {
-  //       const property = metadata.properties.find(
-  //         (i) => i.id === payload.value.property,
-  //       ) as PropertyMetadata & ObserverMetadata;
-  //       if (property) {
-  //         property.next(payload);
-  //       }
-  //     });
-  // };
-
-  // const getDashboard = () => {
-  //   const params = [
-  //     {
-  //       dashboard: 'device',
-  //       object: device.productId,
-  //       measurement: 'properties',
-  //       dimension: 'history',
-  //       params: {
-  //         deviceId: device.id,
-  //         history: 15,
-  //         properties: propertiesList,
-  //       },
-  //     },
-  //   ];
-
-  //   service.propertyRealTime(params).subscribe({
-  //     next: (data) => {
-  //       const index = metadata.properties.findIndex((i) => i.id === data.property);
-  //       if (index > -1) {
-  //         const property = metadata.properties[index] as PropertyMetadata & ObserverMetadata;
-  //         property.list = data.list as Record<string, unknown>[];
-  //         property.next(data.list);
-  //       }
-  //     },
-  //   });
-  // };
-
-  // /**
-  //  * 订阅事件数据
-  //  */
-  // const subscribeEvent = () => {
-  //   const id = `instance-info-event-${device.id}-${device.productId}`;
-  //   const topic = `/dashboard/device/${device.productId}/events/realTime`;
-  //   subscribeTopic!(id, topic, { deviceId: device.id })
-  //     ?.pipe(map((res) => res.payload))
-  //     .subscribe((payload: any) => {
-  //       const event = metadata.events.find((i) => i.id === payload.value.event) as EventMetadata &
-  //         ObserverMetadata;
-  //       if (event) {
-  //         event.next(payload);
-  //       }
-  //     });
-  // };
-  useEffect(() => {
-    // subscribeProperty();
-    // subscribeEvent();
-    // getDashboard();
-  }, []);
-
-  // const [renderCount, setRenderCount] = useState<number>(15);
-  // window.onscroll = () => {
-  //   const a = document.documentElement.scrollTop;
-  //   const c = document.documentElement.scrollHeight;
-  //   const b = document.body.clientHeight;
-  //   if (a + b >= c - 50) {
-  //     const list: any = [];
-  //     metadata.properties.slice(renderCount, renderCount + 15).map((item) => {
-  //       list.push(item.id);
-  //     });
-  //     setPropertiesList([...list]);
-  //     setRenderCount(renderCount + 15);
-  //   }
-  // };
-
-  // const renderCard = useCallback(() => {
-  //   return [
-  //     ...metadata.properties.map((item) => (
-  //       <Col {...ColResponsiveProps} key={item.id}>
-  //         <Property data={item as Partial<PropertyMetadata> & ObserverMetadata} />
-  //       </Col>
-  //     )),
-  //     ...metadata.events.map((item) => (
-  //       <Col {...ColResponsiveProps} key={item.id}>
-  //         <Event data={item as Partial<EventMetadata> & ObserverMetadata} />
-  //       </Col>
-  //     )),
-  //   ].splice(0, renderCount);
-  // }, [device, renderCount]);
-
   return (
-    // <Row gutter={24}>
-    //   <Col {...ColResponsiveProps}>
-    //     <Card
-    //       title={intl.formatMessage({
-    //         id: 'pages.device.instanceDetail.running.status',
-    //         defaultMessage: '设备状态',
-    //       })}
-    //     >
-    //       <div style={{ height: 60 }}>
-    //         <Row gutter={[16, 16]}>
-    //           <Col span={24}>{deviceStatus.get(InstanceModel.detail.state?.value)}</Col>
-    //           <Col span={24}>
-    //             {device.state?.value === 'online' ? (
-    //               <span>上线时间:{moment(device?.onlineTime).format('YYYY-MM-DD HH:mm:ss')}</span>
-    //             ) : (
-    //               <span>离线时间:{moment(device?.offlineTime).format('YYYY-MM-DD HH:mm:ss')}</span>
-    //             )}
-    //           </Col>
-    //         </Row>
-    //       </div>
-    //     </Card>
-    //   </Col>
-    //   {renderCard()}
-    // </Row>
     <Card>
-      <Tabs defaultActiveKey="1" tabPosition="left">
+      <Tabs defaultActiveKey="1" tabPosition="left" style={{ height: 600 }}>
         <Tabs.TabPane tab="属性" key="1">
           <Property data={metadata?.properties || {}} />
         </Tabs.TabPane>
-        <Tabs.TabPane tab="事件1" key="2">
-          Content of Tab Pane 2
-        </Tabs.TabPane>
+        {metadata.events.map((item) => (
+          <Tabs.TabPane tab={item.name} key={item.id}>
+            <Event data={item} />
+          </Tabs.TabPane>
+        ))}
       </Tabs>
     </Card>
   );