xieyonghong 3 anni fa
parent
commit
97ce11e60c

+ 8 - 0
src/components/Empty/index.less

@@ -0,0 +1,8 @@
+.empty-body {
+  display: flex;
+  flex-direction: column;
+  align-content: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+}

+ 11 - 0
src/components/Empty/index.tsx

@@ -0,0 +1,11 @@
+import './style';
+import { Empty } from 'antd';
+import type { EmptyProps } from 'antd';
+
+export default (props: EmptyProps) => {
+  return (
+    <div className={'empty-body'}>
+      <Empty {...props} />
+    </div>
+  );
+};

+ 1 - 0
src/components/Empty/style.ts

@@ -0,0 +1 @@
+import './index.less';

+ 1 - 0
src/components/ProTableCard/index.less

@@ -6,6 +6,7 @@
 
 .pro-table-card {
   position: relative;
+  background-color: #fff;
 
   .pro-table-card-setting-item {
     color: rgba(0, 0, 0, 0.75);

+ 44 - 23
src/components/ProTableCard/index.tsx

@@ -1,13 +1,14 @@
 import type { ProTableProps } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import type { ParamsType } from '@ant-design/pro-provider';
-import React, { useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
 import { isFunction } from 'lodash';
 import { Empty, Pagination, Space } from 'antd';
 import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
 import classNames from 'classnames';
 import LoadingComponent from '@ant-design/pro-layout/es/PageLoading';
 import './index.less';
+import { getDomFullHeight } from '@/utils/util';
 
 enum ModelEnum {
   TABLE = 'TABLE',
@@ -39,32 +40,43 @@ const ProTableCard = <
   const [column, setColumn] = useState(props.gridColumn || 4);
   const [loading, setLoading] = useState(false);
   const [dataLength, setDataLength] = useState<number>(0);
+  const [minHeight, setMinHeight] = useState(100);
 
   /**
    * 处理 Card
    * @param dataSource
    */
-  const handleCard = (dataSource: readonly T[] | undefined): JSX.Element => {
-    setDataLength(dataSource ? dataSource.length : 0);
-    return (
-      <>
-        {dataSource && dataSource.length ? (
-          <div
-            className={'pro-table-card-items'}
-            style={{ gridTemplateColumns: `repeat(${column}, 1fr)` }}
-          >
-            {dataSource.map((item) =>
-              cardRender && isFunction(cardRender) ? cardRender(item) : null,
-            )}
-          </div>
-        ) : (
-          <div style={{ display: 'flex', justifyContent: 'center' }}>
-            <Empty />
-          </div>
-        )}
-      </>
-    );
-  };
+  const handleCard = useCallback(
+    (dataSource: readonly T[] | undefined): JSX.Element => {
+      setDataLength(dataSource ? dataSource.length : 0);
+      return (
+        <>
+          {dataSource && dataSource.length ? (
+            <div
+              className={'pro-table-card-items'}
+              style={{ gridTemplateColumns: `repeat(${column}, 1fr)` }}
+            >
+              {dataSource.map((item) =>
+                cardRender && isFunction(cardRender) ? cardRender(item) : null,
+              )}
+            </div>
+          ) : (
+            <div
+              style={{
+                display: 'flex',
+                justifyContent: 'center',
+                alignItems: 'center',
+                minHeight: minHeight - 150,
+              }}
+            >
+              <Empty />
+            </div>
+          )}
+        </>
+      );
+    },
+    [minHeight],
+  );
 
   const windowChange = () => {
     if (window.innerWidth <= 1440) {
@@ -92,7 +104,15 @@ const ProTableCard = <
   }, [props.params]);
 
   return (
-    <div className={'pro-table-card'}>
+    <div
+      className={'pro-table-card'}
+      style={{ minHeight: minHeight }}
+      ref={(ref) => {
+        if (ref) {
+          setMinHeight(getDomFullHeight('pro-table-card'));
+        }
+      }}
+    >
       <ProTable<T, U, ValueType>
         {...extraProps}
         params={
@@ -103,6 +123,7 @@ const ProTableCard = <
             pageSize,
           } as any
         }
+        className={'pro-table-card-body'}
         options={model === ModelEnum.CARD ? false : props.options}
         request={async (param, sort, filter) => {
           if (request) {

+ 1 - 0
src/components/index.ts

@@ -11,3 +11,4 @@ export { default as PermissionButton } from './PermissionButton';
 export { default as TitleComponent } from './TitleComponent';
 export { default as AMap } from './AMapComponent/amap';
 export { default as PathSimplifier } from './AMapComponent/PathSimplifier';
+export { default as Empty } from './Empty';

+ 3 - 0
src/pages/device/Category/index.tsx

@@ -12,6 +12,7 @@ import { observer } from '@formily/react';
 import type { Response } from '@/utils/typings';
 import SearchComponent from '@/components/SearchComponent';
 import { PermissionButton } from '@/components';
+import { getDomFullHeight } from '@/utils/util';
 
 export const service = new Service('device/category');
 
@@ -195,6 +196,8 @@ const Category = observer(() => {
             status: response.status,
           };
         }}
+        className={'device-category'}
+        tableStyle={{ minHeight: getDomFullHeight('device-category', 94) }}
         rowKey="id"
         columns={columns}
         onChange={(_, f, sorter: any) => {

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

@@ -11,6 +11,7 @@ import BindChildDevice from './BindChildDevice';
 import moment from 'moment';
 import { Link } from 'umi';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { getDomFullHeight } from '@/utils/util';
 
 const ChildDevice = () => {
   const intl = useIntl();
@@ -116,7 +117,10 @@ const ChildDevice = () => {
   ];
 
   return (
-    <Card>
+    <Card
+      className={'device-detail-childDevice'}
+      style={{ minHeight: getDomFullHeight('device-detail-childDevice', 12) }}
+    >
       <SearchComponent<LogItem>
         field={[...columns]}
         target="child-device"

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

@@ -7,6 +7,7 @@ import './index.less';
 import classNames from 'classnames';
 import { Store } from 'jetlinks-store';
 import { DiagnoseStatusModel } from './Status/model';
+import { getDomFullHeight } from '@/utils/util';
 
 interface ListProps {
   key: string;
@@ -131,7 +132,7 @@ const Diagnose = () => {
     };
   }, []);
   return (
-    <Card className="diagnose">
+    <Card className="diagnose" style={{ minHeight: getDomFullHeight('diagnose', 12) }}>
       <div className={current === 'message' ? 'header-message' : 'header'}>
         <Row gutter={24} style={{ padding: 10, width: '100%' }}>
           {list.map((item: ListProps) => (

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

@@ -3,13 +3,22 @@ import { InstanceModel } from '@/pages/device/Instance';
 import type { FunctionMetadata } from '@/pages/device/Product/typings';
 import FnForm from './form';
 import AModel from './AdvancedMode';
+import { getDomFullHeight } from '@/utils/util';
+import { useEffect, useState } from 'react';
+import { Empty } from '@/components';
 
 const Functions = () => {
   const functionList = JSON.parse(InstanceModel.detail.metadata || '{}')
     .functions as FunctionMetadata[];
 
+  const [minHeight, setMinHeight] = useState(100);
+
+  useEffect(() => {
+    setMinHeight(getDomFullHeight('device-detail-function', 12));
+  }, []);
+
   return (
-    <Card>
+    <Card className={'device-detail-function'} style={{ minHeight: minHeight }}>
       <Tabs>
         <Tabs.TabPane tab={'精简模式'} key={1}>
           <Tabs tabPosition="left">
@@ -36,6 +45,11 @@ const Functions = () => {
           </Tabs>
         </Tabs.TabPane>
       </Tabs>
+      {!functionList && (
+        <div style={{ height: minHeight - 150 }}>
+          <Empty />
+        </div>
+      )}
     </Card>
   );
 };

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

@@ -11,6 +11,7 @@ import type { DeviceInstance } from '../../typings';
 import { EditOutlined } from '@ant-design/icons';
 import Tags from '@/pages/device/Instance/Detail/Tags';
 import { PermissionButton } from '@/components';
+import { getDomFullHeight } from '@/utils/util';
 
 const Info = observer(() => {
   const intl = useIntl();
@@ -19,7 +20,10 @@ const Info = observer(() => {
 
   return (
     <>
-      <Card>
+      <Card
+        className={'device-detail-body'}
+        style={{ minHeight: getDomFullHeight('device-detail-body', 12) }}
+      >
         <Descriptions
           size="small"
           column={3}

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

@@ -7,6 +7,7 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import { useRef, useState } from 'react';
 import SearchComponent from '@/components/SearchComponent';
+import { getDomFullHeight } from '@/utils/util';
 
 const Log = () => {
   const intl = useIntl();
@@ -78,7 +79,10 @@ const Log = () => {
   ];
 
   return (
-    <Card>
+    <Card
+      className={'device-detail-log'}
+      style={{ minHeight: getDomFullHeight('device-detail-log', 12) }}
+    >
       <SearchComponent<LogItem>
         field={[...columns]}
         target="logs"

+ 78 - 55
src/pages/device/Instance/Detail/MetadataMap/index.tsx

@@ -1,11 +1,12 @@
-import { Card, Empty } from 'antd';
+import { Card } from 'antd';
 import { useEffect, useState } from 'react';
 import { service } from '@/pages/device/Instance';
 import EditableTable from './EditableTable';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import type { ProductItem } from '@/pages/device/Product/typings';
 import { useParams } from 'umi';
-import { PermissionButton } from '@/components';
+import { PermissionButton, Empty } from '@/components';
+import { getDomFullHeight } from '@/utils/util';
 
 interface Props {
   type: 'device' | 'product';
@@ -17,6 +18,7 @@ const MetadataMap = (props: Props) => {
   const [data, setData] = useState<any>({});
   const params = useParams<{ id: string }>();
   const { permission } = PermissionButton.usePermission('device/Product');
+  const [minHeight, setMinHeight] = useState(300);
 
   const handleSearch = async () => {
     if (props.type === 'product') {
@@ -50,6 +52,7 @@ const MetadataMap = (props: Props) => {
   const renderComponent = () => {
     const metadata = JSON.parse(product?.metadata || '{}');
     const dmetadata = JSON.parse(data?.metadata || '{}');
+    const height = minHeight - 150;
     if (product) {
       const flag =
         (type === 'device' &&
@@ -59,24 +62,70 @@ const MetadataMap = (props: Props) => {
       if (!product.accessId && flag) {
         if (!permission.update) {
           return (
-            <Empty
-              description={<span>请联系管理员配置物模型属性,并选择对应产品的设备接入方式</span>}
-            />
+            <div style={{ height }}>
+              <Empty
+                description={<span>请联系管理员配置物模型属性,并选择对应产品的设备接入方式</span>}
+              />
+            </div>
           );
         } else {
           return (
+            <div style={{ height }}>
+              <Empty
+                description={
+                  <span>
+                    请先配置对应产品的
+                    <a
+                      onClick={() => {
+                        checkUrl('metadata');
+                      }}
+                    >
+                      物模型属性
+                    </a>
+                    ,并选择对应产品的
+                    <a
+                      onClick={() => {
+                        checkUrl('access');
+                      }}
+                    >
+                      设备接入方式
+                    </a>
+                  </span>
+                }
+              />
+            </div>
+          );
+        }
+      } else if (flag && product.accessId) {
+        return (
+          <div style={{ height }}>
+            <Empty
+              description={
+                !permission.update ? (
+                  <span>请联系管理员配置物模型属性</span>
+                ) : (
+                  <span>
+                    请配置对应产品的
+                    <a
+                      onClick={() => {
+                        checkUrl('metadata');
+                      }}
+                    >
+                      物模型属性
+                    </a>
+                  </span>
+                )
+              }
+            />
+          </div>
+        );
+      } else if (!flag && !product.accessId) {
+        return (
+          <div style={{ height }}>
             <Empty
               description={
                 <span>
-                  请先配置对应产品的
-                  <a
-                    onClick={() => {
-                      checkUrl('metadata');
-                    }}
-                  >
-                    物模型属性
-                  </a>
-                  ,并选择对应产品的
+                  请选择对应产品的
                   <a
                     onClick={() => {
                       checkUrl('access');
@@ -87,58 +136,32 @@ const MetadataMap = (props: Props) => {
                 </span>
               }
             />
-          );
-        }
-      } else if (flag && product.accessId) {
-        return (
-          <Empty
-            description={
-              !permission.update ? (
-                <span>请联系管理员配置物模型属性</span>
-              ) : (
-                <span>
-                  请配置对应产品的
-                  <a
-                    onClick={() => {
-                      checkUrl('metadata');
-                    }}
-                  >
-                    物模型属性
-                  </a>
-                </span>
-              )
-            }
-          />
-        );
-      } else if (!flag && !product.accessId) {
-        return (
-          <Empty
-            description={
-              <span>
-                请选择对应产品的
-                <a
-                  onClick={() => {
-                    checkUrl('access');
-                  }}
-                >
-                  设备接入方式
-                </a>
-              </span>
-            }
-          />
+          </div>
         );
       } else {
         return <EditableTable data={data} type={type} />;
       }
     }
-    return <Empty />;
+    return (
+      <div style={{ height }}>
+        <Empty />
+      </div>
+    );
   };
 
   useEffect(() => {
     handleSearch();
   }, [props.type]);
 
-  return <Card bordered={false}>{renderComponent()}</Card>;
+  useEffect(() => {
+    setMinHeight(getDomFullHeight('device-detail-metadataMap', 12));
+  }, []);
+
+  return (
+    <Card bordered={false} className="device-detail-metadataMap" style={{ minHeight }}>
+      {renderComponent()}
+    </Card>
+  );
 };
 
 export default MetadataMap;

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

@@ -17,6 +17,7 @@ import { InstanceModel } from '@/pages/device/Instance';
 import AddPoint from '@/pages/link/Channel/Modbus/Access/addPoint';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { map } from 'rxjs/operators';
+import { getDomFullHeight } from '@/utils/util';
 
 const Modbus = () => {
   const intl = useIntl();
@@ -210,7 +211,7 @@ const Modbus = () => {
   }, [data]);
 
   return (
-    <Card className={styles.list}>
+    <Card className={styles.list} style={{ minHeight: getDomFullHeight(styles.list, 12) }}>
       <div style={{ display: 'flex' }}>
         <div>
           <div style={{ width: '250px', marginTop: 15 }}>

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

@@ -17,6 +17,7 @@ import { InstanceModel } from '@/pages/device/Instance';
 import AddPoint from '@/pages/link/Channel/Opcua/Access/addPoint';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { map } from 'rxjs/operators';
+import { getDomFullHeight } from '@/utils/util';
 
 const Opcua = () => {
   const intl = useIntl();
@@ -226,7 +227,7 @@ const Opcua = () => {
   }, [data]);
 
   return (
-    <Card className={styles.list}>
+    <Card className={styles.list} style={{ minHeight: getDomFullHeight(styles.list, 12) }}>
       <div style={{ display: 'flex' }}>
         <div>
           <div style={{ width: '250px', marginTop: 15 }}>

+ 5 - 1
src/pages/device/Product/Detail/index.tsx

@@ -17,6 +17,7 @@ import MetadataMap from '@/pages/device/Instance/Detail/MetadataMap';
 import SystemConst from '@/utils/const';
 import { PermissionButton } from '@/components';
 import { QuestionCircleOutlined } from '@ant-design/icons';
+import { getDomFullHeight } from '@/utils/util';
 
 export const ModelEnum = {
   base: 'base',
@@ -313,7 +314,10 @@ const ProductDetail = observer(() => {
         </PermissionButton>,
       ]}
     >
-      <Card>
+      <Card
+        className={'product-detail-body'}
+        style={{ minHeight: getDomFullHeight('product-detail-body', 12) }}
+      >
         {list.find((k) => k.key === mode)?.component}
         {/* <Tabs
           defaultActiveKey={ModelEnum.base}

+ 5 - 1
src/pages/device/components/Metadata/index.tsx

@@ -14,6 +14,7 @@ import { PermissionButton } from '@/components';
 import { Store } from 'jetlinks-store';
 import SystemConst from '@/utils/const';
 import { useParams } from 'umi';
+import { getDomFullHeight } from '@/utils/util';
 
 interface Props {
   tabAction?: ReactNode;
@@ -44,7 +45,10 @@ const Metadata = observer((props: Props) => {
   };
 
   return (
-    <div style={{ position: 'relative' }}>
+    <div
+      className={'device-detail-metadata'}
+      style={{ position: 'relative', minHeight: getDomFullHeight('device-detail-metadata', 32) }}
+    >
       <div className={styles.tips}>
         <InfoCircleOutlined style={{ marginRight: '3px' }} />
         {InstanceModel.detail?.independentMetadata

+ 25 - 16
src/pages/link/AccessConfig/index.tsx

@@ -2,13 +2,14 @@ import SearchComponent from '@/components/SearchComponent';
 import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import { PageContainer } from '@ant-design/pro-layout';
 import type { ProColumns } from '@jetlinks/pro-table';
-import { Card, Col, Empty, message, Pagination, Row } from 'antd';
+import { Card, Col, message, Pagination, Row } from 'antd';
 import { useEffect, useState } from 'react';
 import { useHistory } from 'umi';
 import Service from './service';
 import { DeleteOutlined, EditOutlined, PlayCircleOutlined, StopOutlined } from '@ant-design/icons';
 import AccessConfigCard from '@/components/ProTableCard/CardItems/AccessConfig';
-import { PermissionButton } from '@/components';
+import { PermissionButton, Empty } from '@/components';
+import { getDomFullHeight } from '@/utils/util';
 
 export const service = new Service('gateway/device');
 
@@ -16,6 +17,7 @@ const AccessConfig = () => {
   const history = useHistory();
   const [param, setParam] = useState<any>({ pageSize: 10, terms: [] });
   const { permission } = PermissionButton.usePermission('link/AccessConfig');
+  const [minHeight, setMinHeight] = useState(100);
 
   const columns: ProColumns<any>[] = [
     {
@@ -67,20 +69,25 @@ const AccessConfig = () => {
 
   return (
     <PageContainer>
-      <Card>
-        <SearchComponent
-          field={columns}
-          // enableSave={false}
-          target={'access-config'}
-          onSearch={(data: any) => {
-            const dt = {
-              pageSize: 10,
-              terms: [...data?.terms],
-            };
-            handleSearch(dt);
+      <SearchComponent
+        field={columns}
+        // enableSave={false}
+        target={'access-config'}
+        onSearch={(data: any) => {
+          const dt = {
+            pageSize: 10,
+            terms: [...data?.terms],
+          };
+          handleSearch(dt);
+        }}
+      />
+      <Card className={'link-accessConfig'} style={{ minHeight }}>
+        <div
+          style={{ width: '100%', display: 'flex', justifyContent: 'flex-start' }}
+          ref={() => {
+            setMinHeight(getDomFullHeight('link-accessConfig'));
           }}
-        />
-        <div style={{ width: '100%', display: 'flex', justifyContent: 'flex-start' }}>
+        >
           <PermissionButton
             isPermission={permission.add}
             onClick={() => {
@@ -177,7 +184,9 @@ const AccessConfig = () => {
             ))}
           </Row>
         ) : (
-          <Empty />
+          <div style={{ height: minHeight - 150 }}>
+            <Empty />
+          </div>
         )}
         {dataSource.data.length > 0 && (
           <div style={{ display: 'flex', marginTop: 20, justifyContent: 'flex-end' }}>

+ 9 - 1
src/pages/link/Certificate/index.tsx

@@ -1,5 +1,5 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { useRef, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import { message, Tooltip } from 'antd';
@@ -11,6 +11,7 @@ import usePermissions from '@/hooks/permission';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import { history } from 'umi';
 import Service from '../service';
+import { getDomFullHeight } from '@/utils/util';
 
 export const service = new Service('network/certificate');
 
@@ -19,6 +20,7 @@ const Certificate = () => {
   const actionRef = useRef<ActionType>();
   const [param, setParam] = useState({});
   const { permission } = usePermissions('link/Certificate');
+  const [minHeight, setMinHeight] = useState(100);
 
   const columns: ProColumns<CertificateItem>[] = [
     {
@@ -103,6 +105,10 @@ const Certificate = () => {
     },
   ];
 
+  useEffect(() => {
+    setMinHeight(getDomFullHeight('link-certificate', 94));
+  }, []);
+
   return (
     <PageContainer>
       <SearchComponent<CertificateItem>
@@ -120,6 +126,8 @@ const Certificate = () => {
         columns={columns}
         search={false}
         rowKey="id"
+        className={'link-certificate'}
+        tableStyle={{ minHeight }}
         headerTitle={
           <PermissionButton
             onClick={() => {

+ 18 - 0
src/utils/util.ts

@@ -100,3 +100,21 @@ export const randomString = (length?: number) => {
   }
   return pwd;
 };
+
+/**
+ * 获取当前DOM元素需要撑满的高度
+ * @param className
+ * @param extraHeight 额外减去的高度
+ */
+export const getDomFullHeight = (className: string, extraHeight: number = 0): number => {
+  const dom = document.querySelector(`.${className}`);
+  if (dom) {
+    const bodyClient = document.body.getBoundingClientRect();
+    const domClient = dom.getBoundingClientRect();
+    if (domClient.y < 50) {
+      return 100;
+    }
+    return bodyClient.height - domClient.y - 24 - extraHeight;
+  }
+  return 0;
+};