sun-chaochao 3 лет назад
Родитель
Сommit
c795b9268c
30 измененных файлов с 184 добавлено и 126 удалено
  1. BIN
      public/images/running/doc.png
  2. BIN
      public/images/running/docx.png
  3. BIN
      public/images/running/error.png
  4. BIN
      public/images/running/flv.png
  5. BIN
      public/images/running/img.png
  6. BIN
      public/images/running/jpg.png
  7. BIN
      public/images/running/mp3.png
  8. BIN
      public/images/running/mp4.png
  9. BIN
      public/images/running/mvb.png
  10. BIN
      public/images/running/other.png
  11. BIN
      public/images/running/pdf.png
  12. BIN
      public/images/running/png.png
  13. BIN
      public/images/running/ppt.png
  14. BIN
      public/images/running/pptx.png
  15. BIN
      public/images/running/rmvb.png
  16. BIN
      public/images/running/swf.png
  17. BIN
      public/images/running/tiff.png
  18. BIN
      public/images/running/txt.png
  19. BIN
      public/images/running/video.png
  20. BIN
      public/images/running/wma.png
  21. BIN
      public/images/running/xls.png
  22. BIN
      public/images/running/xlsx.png
  23. 8 1
      src/pages/device/Instance/Detail/MetadataLog/Property/Detail.tsx
  24. 85 82
      src/pages/device/Instance/Detail/MetadataLog/Property/index.tsx
  25. 3 5
      src/pages/device/Instance/Detail/Running/Property/FileComponent/Detail.tsx
  26. 3 1
      src/pages/device/Instance/Detail/Running/Property/FileComponent/index.less
  27. 82 34
      src/pages/device/Instance/Detail/Running/Property/FileComponent/index.tsx
  28. 1 1
      src/pages/device/Instance/Detail/Running/Property/PropertyCard.tsx
  29. 1 1
      src/pages/device/Instance/Detail/Running/Property/index.tsx
  30. 1 1
      src/pages/device/Instance/Detail/Running/index.tsx

BIN
public/images/running/doc.png


BIN
public/images/running/docx.png


BIN
public/images/running/error.png


BIN
public/images/running/flv.png


BIN
public/images/running/img.png


BIN
public/images/running/jpg.png


BIN
public/images/running/mp3.png


BIN
public/images/running/mp4.png


BIN
public/images/running/mvb.png


BIN
public/images/running/other.png


BIN
public/images/running/pdf.png


BIN
public/images/running/png.png


BIN
public/images/running/ppt.png


BIN
public/images/running/pptx.png


BIN
public/images/running/rmvb.png


BIN
public/images/running/swf.png


BIN
public/images/running/tiff.png


BIN
public/images/running/txt.png


BIN
public/images/running/video.png


BIN
public/images/running/wma.png


BIN
public/images/running/xls.png


BIN
public/images/running/xlsx.png


+ 8 - 1
src/pages/device/Instance/Detail/MetadataLog/Property/Detail.tsx

@@ -11,7 +11,7 @@ const Detail = (props: Props) => {
   const { value, type } = props;
 
   const renderValue = () => {
-    if (type === 'object') {
+    if (type === 'object' || type === 'array') {
       return (
         <div>
           <div>自定义属性</div>
@@ -29,6 +29,13 @@ const Detail = (props: Props) => {
           </div>
         </div>
       );
+    } else if (type === 'file') {
+      return (
+        <div>
+          <div>自定义属性</div>
+          <Input.TextArea value={value} rows={3} />
+        </div>
+      );
     } else {
       return (
         <div>

+ 85 - 82
src/pages/device/Instance/Detail/MetadataLog/Property/index.tsx

@@ -1,16 +1,6 @@
 import { InstanceModel, service } from '@/pages/device/Instance';
 import { useParams } from 'umi';
-import {
-  DatePicker,
-  Modal,
-  Popconfirm,
-  Radio,
-  Select,
-  Space,
-  Table,
-  Tabs,
-  Tooltip as ATooltip,
-} from 'antd';
+import { DatePicker, Modal, Radio, Select, Space, Table, Tabs, Tooltip as ATooltip } from 'antd';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import encodeQuery from '@/utils/encodeQuery';
 import { useEffect, useState } from 'react';
@@ -22,14 +12,13 @@ import Detail from './Detail';
 import AMap from './AMap';
 
 interface Props {
-  visible: boolean;
   close: () => void;
   data: Partial<PropertyMetadata>;
 }
 
 const PropertyLog = (props: Props) => {
   const params = useParams<{ id: string }>();
-  const { visible, close, data } = props;
+  const { close, data } = props;
   const list = ['int', 'float', 'double', 'long'];
   const [dataSource, setDataSource] = useState<any>({});
   const [start, setStart] = useState<number>(moment().startOf('day').valueOf());
@@ -45,19 +34,21 @@ const PropertyLog = (props: Props) => {
   const [detailVisible, setDetailVisible] = useState<boolean>(false);
   const [current, setCurrent] = useState<any>('');
 
-  const [geoList, setGeoList] = useState<any[]>([]);
+  const [geoList, setGeoList] = useState<any>({});
 
   const columns = [
     {
       title: '时间',
       dataIndex: 'timestamp',
       key: 'timestamp',
+      ellipsis: true,
       render: (text: any) => <span>{text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : ''}</span>,
     },
     {
       title: <span>{data.valueType?.type !== 'file' ? '自定义属性' : '文件内容'}</span>,
       dataIndex: 'value',
       key: 'value',
+      ellipsis: true,
       render: (text: any, record: any) => (
         <FileComponent type="table" value={{ formatValue: record.value }} data={data} />
       ),
@@ -68,22 +59,15 @@ const PropertyLog = (props: Props) => {
       key: 'action',
       render: (text: any, record: any) => (
         <a>
-          {data.valueType?.type !== 'file' ? (
-            <SearchOutlined
-              onClick={() => {
-                setDetailVisible(true);
-                setCurrent(record.value);
-              }}
-            />
-          ) : (
+          {data.valueType?.type === 'file' && data?.valueType?.fileType == 'url' ? (
             <ATooltip title="下载">
-              <Popconfirm
-                title="确认下载"
-                onConfirm={() => {
+              <DownloadOutlined
+                onClick={() => {
                   const type = (record?.value || '').split('.').pop();
                   const downloadUrl = record.value;
                   const downNode = document.createElement('a');
                   downNode.href = downloadUrl;
+                  downNode.target = '_blank';
                   downNode.download = `${InstanceModel.detail.name}-${data.name}${moment(
                     new Date().getTime(),
                   ).format('YYYY-MM-DD-HH-mm-ss')}.${type}`;
@@ -92,10 +76,15 @@ const PropertyLog = (props: Props) => {
                   downNode.click();
                   document.body.removeChild(downNode);
                 }}
-              >
-                <DownloadOutlined />
-              </Popconfirm>
+              />
             </ATooltip>
+          ) : (
+            <SearchOutlined
+              onClick={() => {
+                setDetailVisible(true);
+                setCurrent(record.value);
+              }}
+            />
           )}
         </a>
       ),
@@ -190,23 +179,18 @@ const PropertyLog = (props: Props) => {
   };
 
   useEffect(() => {
-    if (visible) {
-      handleSearch(
-        {
-          pageSize: 10,
-          pageIndex: 0,
-        },
-        start,
-        new Date().getTime(),
-      );
-    }
-  }, [visible]);
+    setRadioValue('today');
+    setTab('table');
+    setStart(moment().startOf('day').valueOf());
+    setEnd(new Date().getTime());
+  }, []);
 
   const scale = {
     value: { min: 0 },
     year: {
-      range: [0, 0.96],
+      range: [0, 1],
       type: 'timeCat',
+      mask: 'YYYY-MM-DD HH:mm:ss',
     },
   };
 
@@ -358,15 +342,62 @@ const PropertyLog = (props: Props) => {
     }
   };
 
+  useEffect(() => {
+    if (tab === 'table') {
+      handleSearch(
+        {
+          pageSize: 10,
+          pageIndex: 0,
+        },
+        start,
+        end,
+      );
+    } else if (tab === 'charts') {
+      if (list.includes(data.valueType?.type || '')) {
+        queryChartsList(start, end);
+      } else {
+        queryChartsAggList({
+          columns: [
+            {
+              property: data.id,
+              alias: data.id,
+              agg,
+            },
+          ],
+          query: {
+            interval: cycle,
+            format: 'yyyy-MM-dd HH:mm:ss',
+            from: start,
+            to: end,
+          },
+        });
+      }
+    } else if (tab === 'geo') {
+      service
+        .getPropertyData(
+          params.id,
+          encodeQuery({
+            paging: false,
+            terms: { property: data?.id, timestamp$BTW: start && start ? [start, end] : [] },
+            sorts: { timestamp: 'asc' },
+          }),
+        )
+        .then((resp) => {
+          if (resp.status === 200) {
+            setGeoList(resp.result);
+          }
+        });
+    }
+  }, [start, end]);
+
   // @ts-ignore
   return (
     <Modal
       maskClosable={false}
       title="详情"
-      visible={visible}
+      visible
       onCancel={() => close()}
       onOk={() => close()}
-      destroyOnClose={true}
       width="50vw"
     >
       <div style={{ marginBottom: '20px' }}>
@@ -389,36 +420,6 @@ const PropertyLog = (props: Props) => {
               setDateValue(undefined);
               setStart(st);
               setEnd(et);
-              if (tab === 'charts') {
-                if (list.includes(data.valueType?.type || '')) {
-                  queryChartsList(st, et);
-                } else {
-                  queryChartsAggList({
-                    columns: [
-                      {
-                        property: data.id,
-                        alias: data.id,
-                        agg,
-                      },
-                    ],
-                    query: {
-                      interval: cycle,
-                      format: 'yyyy-MM-dd HH:mm:ss',
-                      from: st,
-                      to: et,
-                    },
-                  });
-                }
-              } else {
-                handleSearch(
-                  {
-                    pageSize: 10,
-                    pageIndex: 0,
-                  },
-                  st,
-                  et,
-                );
-              }
             }}
             style={{ minWidth: 220 }}
           >
@@ -439,14 +440,6 @@ const PropertyLog = (props: Props) => {
                   const et = dates[1]?.valueOf();
                   setStart(st);
                   setEnd(et);
-                  handleSearch(
-                    {
-                      pageSize: 10,
-                      pageIndex: 0,
-                    },
-                    st,
-                    et,
-                  );
                 }
               }}
             />
@@ -487,7 +480,7 @@ const PropertyLog = (props: Props) => {
                 encodeQuery({
                   paging: false,
                   terms: { property: data.id, timestamp$BTW: start && end ? [start, end] : [] },
-                  sorts: { timestamp: 'desc' },
+                  sorts: { timestamp: 'asc' },
                 }),
               )
               .then((resp) => {
@@ -496,6 +489,16 @@ const PropertyLog = (props: Props) => {
                 }
               });
           }
+          if (key === 'table') {
+            handleSearch(
+              {
+                pageSize: 10,
+                pageIndex: 0,
+              },
+              start,
+              end,
+            );
+          }
         }}
       >
         {tabList.map((item) => (
@@ -505,7 +508,7 @@ const PropertyLog = (props: Props) => {
         ))}
         {data?.valueType?.type === 'geoPoint' && (
           <Tabs.TabPane tab="轨迹" key="geo">
-            <AMap value={geoList} name={data.name} />
+            <AMap value={geoList} name={data?.name || ''} />
           </Tabs.TabPane>
         )}
       </Tabs>

+ 3 - 5
src/pages/device/Instance/Detail/Running/Property/FileComponent/Detail.tsx

@@ -11,14 +11,12 @@ const Detail = (props: Props) => {
   const { value, type } = props;
 
   const renderValue = () => {
-    if (['jpg', 'png', 'tiff'].includes(type)) {
+    if (['.jpg', '.png'].includes(type)) {
       return <Image src={value?.formatValue} />;
-    } else if (value?.formatValue.indexOf('https') !== -1) {
-      return <p>域名为https时,不支持访问http地址</p>;
-    } else if (['flv', 'm3u8', 'mp4'].includes(type)) {
+    } else if (['.flv', '.m3u8', '.mp4'].includes(type)) {
       return <LivePlayer live={false} url={value?.formatValue} />;
     }
-    return <p>当前仅支持播放.mp4,.flv,.m3u8格式的视频</p>;
+    return null;
   };
 
   return (

+ 3 - 1
src/pages/device/Instance/Detail/Running/Property/FileComponent/index.less

@@ -20,6 +20,8 @@
     justify-content: center;
     width: 60px;
     height: 100%;
-    border: 1px solid rgba(0, 0, 0, 0.08);
+    img {
+      width: 100%;
+    }
   }
 }

+ 82 - 34
src/pages/device/Instance/Detail/Running/Property/FileComponent/index.tsx

@@ -2,6 +2,7 @@ import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import styles from './index.less';
 import Detail from './Detail';
 import { useState } from 'react';
+import { message, Tooltip } from 'antd';
 
 interface Props {
   data: Partial<PropertyMetadata>;
@@ -17,69 +18,116 @@ imgMap.set('ppt', require('/public/images/running/ppt.png'));
 imgMap.set('docx', require('/public/images/running/docx.png'));
 imgMap.set('xlsx', require('/public/images/running/xlsx.png'));
 imgMap.set('pptx', require('/public/images/running/pptx.png'));
-imgMap.set('jpg', require('/public/images/running/jpg.png'));
-imgMap.set('png', require('/public/images/running/png.png'));
 imgMap.set('pdf', require('/public/images/running/pdf.png'));
-imgMap.set('tiff', require('/public/images/running/tiff.png'));
-imgMap.set('swf', require('/public/images/running/swf.png'));
-imgMap.set('flv', require('/public/images/running/flv.png'));
-imgMap.set('rmvb', require('/public/images/running/rmvb.png'));
-imgMap.set('mp4', require('/public/images/running/mp4.png'));
-imgMap.set('mvb', require('/public/images/running/mvb.png'));
-imgMap.set('wma', require('/public/images/running/wma.png'));
-imgMap.set('mp3', require('/public/images/running/mp3.png'));
+imgMap.set('img', require('/public/images/running/img.png'));
+imgMap.set('error', require('/public/images/running/error.png'));
+imgMap.set('video', require('/public/images/running/video.png'));
 imgMap.set('other', require('/public/images/running/other.png'));
 
 const FileComponent = (props: Props) => {
   const { data, value } = props;
   const [type, setType] = useState<string>('other');
   const [visible, setVisible] = useState<boolean>(false);
+  const isHttps = document.location.protocol === 'https:';
 
   const renderValue = () => {
     if (!value?.formatValue) {
       return <div className={props.type === 'card' ? styles.other : {}}>--</div>;
     } else if (data?.valueType?.type === 'file') {
-      const flag: string = value?.formatValue.split('.').pop() || 'other';
-      if (['jpg', 'png'].includes(flag)) {
+      if (
+        data?.valueType?.fileType === 'base64' ||
+        data?.valueType?.fileType === 'Binary(二进制)'
+      ) {
+        return (
+          <div className={props.type === 'card' ? styles.other : {}}>
+            <Tooltip placement="topLeft" title={String(value?.formatValue)}>
+              {String(value?.formatValue)}
+            </Tooltip>
+          </div>
+        );
+      }
+      if (['.jpg', '.png'].some((item) => value?.formatValue.includes(item))) {
+        // 图片
         return (
           <div
             className={styles.img}
             onClick={() => {
-              setType(flag);
-              setVisible(true);
+              if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
+                message.error('域名为https时,不支持访问http地址');
+              } else {
+                const flag =
+                  ['.jpg', '.png'].find((item) => value?.formatValue.includes(item)) || '';
+                setType(flag);
+                setVisible(true);
+              }
             }}
           >
-            {value?.formatValue ? (
-              <img style={{ width: '100%' }} src={value?.formatValue} />
-            ) : (
-              <img src={imgMap.get(flag) || imgMap.get('other')} />
-            )}
+            <img
+              src={value?.formatValue}
+              onError={(e: any) => {
+                e.target.src = imgMap.get('error');
+              }}
+            />
+          </div>
+        );
+      }
+      if (
+        ['.m3u8', '.flv', '.mp4', '.rmvb', '.mvb'].some((item) => value?.formatValue.includes(item))
+      ) {
+        return (
+          <div
+            className={styles.img}
+            onClick={() => {
+              if (isHttps && value?.formatValue.indexOf('http:') !== -1) {
+                message.error('域名为https时,不支持访问http地址');
+              } else if (['.rmvb', '.mvb'].some((item) => value?.formatValue.includes(item))) {
+                message.error('当前仅支持播放.mp4,.flv,.m3u8格式的视频');
+              } else {
+                const flag =
+                  ['.m3u8', '.flv', '.mp4'].find((item) => value?.formatValue.includes(item)) || '';
+                setType(flag);
+                setVisible(true);
+              }
+            }}
+          >
+            <img src={imgMap.get('video')} />
+          </div>
+        );
+      } else if (
+        ['.txt', '.doc', '.xls', '.pdf', '.ppt', '.docx', '.xlsx', '.pptx'].some((item) =>
+          value?.formatValue.includes(item),
+        )
+      ) {
+        const flag =
+          ['.txt', '.doc', '.xls', '.pdf', '.ppt', '.docx', '.xlsx', '.pptx'].find((item) =>
+            value?.formatValue.includes(item),
+          ) || '';
+        return (
+          <div className={styles.img}>
+            <img src={imgMap.get(flag.slice(1))} />
+          </div>
+        );
+      } else {
+        return (
+          <div className={styles.img}>
+            <img src={imgMap.get('other')} />
           </div>
         );
       }
-      return (
-        <div
-          className={styles.img}
-          onClick={() => {
-            if (['tiff', 'flv', 'm3u8', 'mp4', 'rmvb', 'mvb'].includes(flag)) {
-              setType(flag);
-              setVisible(true);
-            }
-          }}
-        >
-          <img src={imgMap.get(flag) || imgMap.get('other')} />
-        </div>
-      );
     } else if (data?.valueType?.type === 'object' || data?.valueType?.type === 'geoPoint') {
       return (
         <div className={props.type === 'card' ? styles.other : {}}>
-          {JSON.stringify(value?.formatValue)}
+          <Tooltip placement="topLeft" title={JSON.stringify(value?.formatValue)}>
+            {JSON.stringify(value?.formatValue)}
+          </Tooltip>
         </div>
       );
     } else {
       return (
         <div className={props.type === 'card' ? styles.other : {}}>
-          {String(value?.formatValue)}
+          <Tooltip placement="topLeft" title={String(value?.formatValue)}>
+            {String(value?.formatValue)}
+          </Tooltip>
         </div>
       );
     }

+ 1 - 1
src/pages/device/Instance/Detail/Running/Property/PropertyCard.tsx

@@ -105,7 +105,7 @@ const Property = (props: Props) => {
         }}
         data={data}
       />
-      <PropertyLog data={data} visible={visible} close={() => setVisible(false)} />
+      {visible && <PropertyLog data={data} close={() => setVisible(false)} />}
       {indicatorVisible && (
         <Indicators
           data={data}

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

@@ -248,7 +248,7 @@ const Property = (props: Props) => {
           setVisible(false);
         }}
       />
-      <PropertyLog data={currentInfo} visible={infoVisible} close={() => setInfoVisible(false)} />
+      {infoVisible && <PropertyLog data={currentInfo} close={() => setInfoVisible(false)} />}
     </div>
   );
 };

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

@@ -36,7 +36,7 @@ const Running = () => {
       <Tabs
         defaultActiveKey="1"
         tabPosition="left"
-        style={{ height: 600 }}
+        style={{ minHeight: 600 }}
         tabBarExtraContent={{ left: operations() }}
       >
         <Tabs.TabPane tab="属性" key="1">