Browse Source

feat: merge

xieyonghong 3 years atrás
parent
commit
e7f8acb8dd
60 changed files with 637 additions and 401 deletions
  1. BIN
      public/images/alarm/io.png
  2. BIN
      public/images/device-access.png
  3. BIN
      public/images/jar.png
  4. BIN
      public/images/local.png
  5. BIN
      public/images/stream.png
  6. 11 3
      src/app.tsx
  7. 39 5
      src/components/DashBoard/header.tsx
  8. 33 20
      src/components/DashBoard/timePicker.tsx
  9. 11 21
      src/components/ProTableCard/CardItems/AccessConfig/index.less
  10. 51 25
      src/components/ProTableCard/CardItems/AccessConfig/index.tsx
  11. 56 0
      src/components/ProTableCard/CardItems/Stream/index.less
  12. 60 0
      src/components/ProTableCard/CardItems/Stream/index.tsx
  13. 1 1
      src/components/ProTableCard/CardItems/networkCard.tsx
  14. 5 5
      src/components/ProTableCard/CardItems/product.tsx
  15. 2 2
      src/components/ProTableCard/CardItems/protocol.tsx
  16. 1 1
      src/components/ProTableCard/CardItems/scene.tsx
  17. 1 1
      src/components/RadioCard/index.less
  18. 3 1
      src/locales/zh-CN/pages.ts
  19. 1 5
      src/pages/device/Category/index.tsx
  20. 1 0
      src/pages/device/DashBoard/index.tsx
  21. 10 10
      src/pages/device/Instance/Detail/Diagnose/Status/index.tsx
  22. 82 32
      src/pages/device/Instance/Detail/Functions/index.tsx
  23. 2 0
      src/pages/device/Instance/Detail/Running/Event/index.tsx
  24. 1 0
      src/pages/device/Instance/Detail/index.tsx
  25. 1 1
      src/pages/device/Instance/index.tsx
  26. 22 15
      src/pages/device/Product/Detail/Access/AccessConfig/index.tsx
  27. 6 6
      src/pages/device/Product/Detail/index.tsx
  28. 1 1
      src/pages/device/Product/Save/index.tsx
  29. 12 12
      src/pages/device/Product/index.tsx
  30. 5 2
      src/pages/link/AccessConfig/Detail/Access/index.tsx
  31. 21 2
      src/pages/link/AccessConfig/index.tsx
  32. 1 1
      src/pages/link/AccessConfig/typings.d.ts
  33. 1 7
      src/pages/link/Certificate/index.tsx
  34. 2 2
      src/pages/link/Channel/Opcua/index.tsx
  35. 1 0
      src/pages/link/DashBoard/index.tsx
  36. 14 3
      src/pages/link/Protocol/index.tsx
  37. 52 31
      src/pages/link/Protocol/save/index.tsx
  38. 5 4
      src/pages/link/Type/index.tsx
  39. 33 18
      src/pages/media/Cascade/Save/index.tsx
  40. 1 0
      src/pages/media/DashBoard/index.tsx
  41. 1 1
      src/pages/media/Device/Save/index.tsx
  42. 3 1
      src/pages/media/SplitScreen/index.tsx
  43. 0 57
      src/pages/media/Stream/index.less
  44. 39 83
      src/pages/media/Stream/index.tsx
  45. 0 1
      src/pages/media/Stream/service.ts
  46. 4 4
      src/pages/media/Stream/typings.d.ts
  47. 1 1
      src/pages/notice/Template/Detail/doc/AliyunSms.tsx
  48. 1 1
      src/pages/notice/Template/Detail/doc/AliyunVoice.tsx
  49. 1 1
      src/pages/notice/Template/Detail/doc/DingTalk.tsx
  50. 1 1
      src/pages/notice/Template/Detail/doc/DingTalkRebot.tsx
  51. 1 1
      src/pages/notice/Template/Detail/doc/WeixinApp.tsx
  52. 1 1
      src/pages/notice/Template/Detail/doc/WeixinCorp.tsx
  53. 1 5
      src/pages/notice/Template/index.tsx
  54. 1 1
      src/pages/rule-engine/Alarm/Config/index.less
  55. 2 1
      src/pages/rule-engine/Alarm/Configuration/index.tsx
  56. 9 1
      src/pages/rule-engine/Alarm/Log/Detail/Info.tsx
  57. 7 0
      src/pages/rule-engine/Alarm/Log/Detail/index.less
  58. 1 0
      src/pages/rule-engine/DashBoard/index.tsx
  59. 4 2
      src/pages/system/Basis/index.tsx
  60. 10 1
      src/utils/util.ts

BIN
public/images/alarm/io.png


BIN
public/images/device-access.png


BIN
public/images/jar.png


BIN
public/images/local.png


BIN
public/images/stream.png


+ 11 - 3
src/app.tsx

@@ -189,7 +189,7 @@ export const request: RequestConfig = {
               .catch(() => {
                 notification.error({
                   key: 'error',
-                  message: '系统错误',
+                  message: '系统开小差,请稍后重试',
                 });
               });
           }
@@ -198,7 +198,7 @@ export const request: RequestConfig = {
     }
     if (!response) {
       notification.error({
-        description: '您的网络发生异常,无法连接服务器',
+        description: '网络异常,请检查网络连接',
         message: '网络异常',
       });
     }
@@ -226,7 +226,7 @@ const MenuItemIcon = (
 
 // ProLayout 支持的api https://procomponents.ant.design/components/layout
 export const layout: RunTimeLayoutConfig = ({ initialState }) => {
-  console.log({ ...initialState });
+  // console.log({ ...initialState });
   return {
     navTheme: 'light',
     headerTheme: 'light',
@@ -235,6 +235,14 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
     waterMarkProps: {
       // content: initialState?.currentUser?.name,
     },
+    itemRender: (route, _, routes) => {
+      const chilck = routes.indexOf(route) > 1;
+      return chilck && route.path !== '/iot/rule-engine/Alarm' ? (
+        <Link to={route.path}>{route.breadcrumbName}</Link>
+      ) : (
+        <span>{route.breadcrumbName}</span>
+      );
+    },
     footerRender: () => <Footer />,
     onPageChange: () => {
       const { location } = history;

+ 39 - 5
src/components/DashBoard/header.tsx

@@ -1,8 +1,8 @@
-import React, { forwardRef, useImperativeHandle, useRef } from 'react';
+import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
 import Style from './index.less';
-import { Col, Form, Row } from 'antd';
+import { Col, Form, Radio, Row } from 'antd';
 import type { TimeType } from './timePicker';
-import RangePicker from './timePicker';
+import RangePicker, { TimeKey } from './timePicker';
 
 export interface HeaderProps {
   title: string;
@@ -22,11 +22,14 @@ export interface HeaderProps {
   closeInitialParams?: boolean;
   defaultTime?: TimeType;
   showTime?: boolean;
+  showTimeTool?: boolean;
 }
 
 export default forwardRef((props: HeaderProps, ref) => {
   const [form] = Form.useForm();
+  const [radioValue, setRadioValue] = useState<TimeType | undefined>(undefined);
   const isCloseInitial = useRef<boolean>(false);
+  const pickerRef = useRef<any>(null);
 
   const change = async (data: any) => {
     if (props.onParamsChange) {
@@ -38,6 +41,10 @@ export default forwardRef((props: HeaderProps, ref) => {
     getValues: form.getFieldsValue,
   }));
 
+  useEffect(() => {
+    setRadioValue(props.defaultTime || TimeKey.today);
+  }, []);
+
   return (
     <div className={Style.header}>
       <div className={Style.title}>{props.title}</div>
@@ -62,10 +69,37 @@ export default forwardRef((props: HeaderProps, ref) => {
                 <Form.Item name={props.extraParams.key}>{props.extraParams.Children}</Form.Item>
               </Col>
             )}
+            {}
             <Col span={props.extraParams ? 18 : 24}>
-              <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
+              <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12 }}>
+                {props.showTimeTool ? (
+                  <Radio.Group
+                    defaultValue="day"
+                    buttonStyle="solid"
+                    value={radioValue}
+                    onChange={(e) => {
+                      setRadioValue(e.target.value);
+                      if (pickerRef.current) {
+                        pickerRef.current.timeChange(e.target.value);
+                      }
+                    }}
+                  >
+                    <Radio.Button value={TimeKey.today}>当天</Radio.Button>
+                    <Radio.Button value={TimeKey.week}>近一周</Radio.Button>
+                    <Radio.Button value={TimeKey.month}>近一月</Radio.Button>
+                    <Radio.Button value={TimeKey.year}>近一年</Radio.Button>
+                  </Radio.Group>
+                ) : null}
                 <Form.Item noStyle name={'time'}>
-                  <RangePicker defaultTime={props.defaultTime} showTime={props.showTime} />
+                  <RangePicker
+                    ref={pickerRef}
+                    defaultTime={props.defaultTime}
+                    showTime={props.showTime}
+                    showTimeTool={props.showTimeTool}
+                    pickerTimeChange={() => {
+                      setRadioValue(undefined);
+                    }}
+                  />
                 </Form.Item>
               </div>
             </Col>

+ 33 - 20
src/components/DashBoard/timePicker.tsx

@@ -1,7 +1,7 @@
 import type { DatePickerProps } from 'antd';
 import { DatePicker, Radio } from 'antd';
 import moment from 'moment';
-import { useEffect, useState } from 'react';
+import { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
 
 export enum TimeKey {
   'today' = 'today',
@@ -18,7 +18,9 @@ interface ExtraTimePickerProps extends Omit<DatePickerProps, 'onChange' | 'value
   onChange?: (data: ValueType) => void;
   value?: ValueType;
   defaultTime?: TimeType;
+  pickerTimeChange?: () => void;
   showTime?: boolean;
+  showTimeTool?: boolean;
 }
 
 export const getTimeByType = (type: TimeType) => {
@@ -34,7 +36,7 @@ export const getTimeByType = (type: TimeType) => {
   }
 };
 
-export default (props: ExtraTimePickerProps) => {
+export default forwardRef((props: ExtraTimePickerProps, ref) => {
   const [radioValue, setRadioValue] = useState<TimeType | undefined>(undefined);
 
   const { value, onChange, ...extraProps } = props;
@@ -56,6 +58,10 @@ export default (props: ExtraTimePickerProps) => {
     change(startTime, endTime, type);
   };
 
+  useImperativeHandle(ref, () => ({
+    timeChange,
+  }));
+
   useEffect(() => {
     timeChange(props.defaultTime || TimeKey.today);
   }, []);
@@ -78,26 +84,33 @@ export default (props: ExtraTimePickerProps) => {
             if (rangeValue && rangeValue.length === 2) {
               change(rangeValue[0]!.valueOf(), rangeValue[1]!.valueOf(), radioValue!);
             }
+            if (props.pickerTimeChange) {
+              props.pickerTimeChange();
+            }
           }}
-          renderExtraFooter={() => (
-            <div style={{ padding: '12px 0' }}>
-              <Radio.Group
-                defaultValue="day"
-                buttonStyle="solid"
-                value={radioValue}
-                onChange={(e) => {
-                  timeChange(e.target.value);
-                }}
-              >
-                <Radio.Button value={TimeKey.today}>当天</Radio.Button>
-                <Radio.Button value={TimeKey.week}>近一周</Radio.Button>
-                <Radio.Button value={TimeKey.month}>近一月</Radio.Button>
-                <Radio.Button value={TimeKey.year}>近一年</Radio.Button>
-              </Radio.Group>
-            </div>
-          )}
+          renderExtraFooter={
+            props.showTimeTool !== true
+              ? () => (
+                  <div style={{ padding: '12px 0' }}>
+                    <Radio.Group
+                      defaultValue="day"
+                      buttonStyle="solid"
+                      value={radioValue}
+                      onChange={(e) => {
+                        timeChange(e.target.value);
+                      }}
+                    >
+                      <Radio.Button value={TimeKey.today}>当天</Radio.Button>
+                      <Radio.Button value={TimeKey.week}>近一周</Radio.Button>
+                      <Radio.Button value={TimeKey.month}>近一月</Radio.Button>
+                      <Radio.Button value={TimeKey.year}>近一年</Radio.Button>
+                    </Radio.Group>
+                  </div>
+                )
+              : undefined
+          }
         />
       }
     </>
   );
-};
+});

+ 11 - 21
src/components/ProTableCard/CardItems/AccessConfig/index.less

@@ -20,6 +20,7 @@
   .context-access {
     display: flex;
     width: 100%;
+    height: 120px;
 
     .card {
       display: flex;
@@ -29,36 +30,16 @@
       margin-left: 20px;
 
       .header {
-        .title {
+        .access-title {
           width: calc(100% - 70px);
-          overflow: hidden;
           font-weight: 700;
           font-size: 18px;
-          white-space: nowrap;
-          text-overflow: ellipsis;
-        }
-
-        .title::before {
-          display: none;
-        }
-
-        .desc {
-          width: 100%;
-          margin-top: 10px;
-          overflow: hidden;
-          color: #666;
-          font-weight: 400;
-          font-size: 12px;
-          white-space: nowrap;
-          text-overflow: ellipsis;
         }
       }
 
       .container {
         display: flex;
         width: 100%;
-        min-height: 60px;
-        margin-top: 10px;
 
         .server,
         .procotol {
@@ -104,6 +85,15 @@
           }
         }
       }
+
+      .desc {
+        width: 100%;
+        margin-right: 10px;
+        margin-bottom: 10px;
+        color: #666;
+        font-weight: 400;
+        font-size: 12px;
+      }
     }
   }
 }

+ 51 - 25
src/components/ProTableCard/CardItems/AccessConfig/index.tsx

@@ -6,6 +6,7 @@ import { Badge, Tooltip } from 'antd';
 import type { AccessItem } from '@/pages/link/AccessConfig/typings';
 import './index.less';
 import classNames from 'classnames';
+import { Store } from 'jetlinks-store';
 
 export interface AccessConfigCardProps extends AccessItem {
   detail?: React.ReactNode;
@@ -25,7 +26,7 @@ export default (props: AccessConfigCardProps) => {
       status={props.state.value}
       statusText={props.state.text}
       statusNames={{
-        enabled: StatusColorEnum.processing,
+        enabled: StatusColorEnum.success,
         disabled: StatusColorEnum.error,
       }}
       showTool={props.showTool}
@@ -38,35 +39,60 @@ export default (props: AccessConfigCardProps) => {
         </div>
         <div className="card">
           <div className="header">
-            <div className="title ellipsis">
-              <Tooltip title={props.name}>{props.name || '--'}</Tooltip>
-            </div>
-            <div className="desc">
-              <Tooltip title={props.description}>{props.description || '--'}</Tooltip>
+            <div className="access-title ellipsis">
+              <Tooltip placement="topLeft" title={props.name}>
+                {props.name}
+              </Tooltip>
             </div>
           </div>
-          <div className="container">
-            <div className="server">
-              <div className="subTitle">{props?.channelInfo?.name || '--'}</div>
-              <div className="serverItem">
-                {props.channelInfo?.addresses.slice(0, 2).map((i: any, index: number) => (
-                  <div className="subItem" key={i.address + `_address${index}`}>
-                    <Tooltip title={i.address}>
-                      <Badge color={i.health === -1 ? 'red' : 'green'} />
-                      {i.address}
+          {(props.protocolDetail?.name || props?.channelInfo?.name) && (
+            <div className="container">
+              {props?.channelInfo?.name && (
+                <div className="server">
+                  <div className="subTitle">{props?.channelInfo?.name}</div>
+                  <div className="serverItem">
+                    {props.channelInfo?.addresses.slice(0, 1).map((i: any, index: number) => (
+                      <div className="subItem" key={i.address + `_address${index}`}>
+                        <Tooltip placement="topLeft" title={i.address}>
+                          <Badge color={i.health === -1 ? 'red' : 'green'} />
+                          {i.address}
+                        </Tooltip>
+                      </div>
+                    ))}
+                  </div>
+                </div>
+              )}
+              {props.protocolDetail?.name && (
+                <div className="procotol">
+                  <div className="subTitle">协议</div>
+                  <div className="desc">
+                    <Tooltip placement="topLeft" title={props.protocolDetail?.name}>
+                      {props.protocolDetail?.name}
                     </Tooltip>
                   </div>
-                ))}
-              </div>
-            </div>
-            <div className="procotol">
-              <div className="subTitle">{props?.protocolDetail?.name || '--'}</div>
-              <div className="desc">
-                <Tooltip title={props.protocolDetail?.description}>
-                  {props.protocolDetail?.description || '--'}
-                </Tooltip>
-              </div>
+                </div>
+              )}
             </div>
+          )}
+          <div className="desc ellipsis">
+            {!!props?.description ? (
+              <Tooltip placement="topLeft" title={props?.description}>
+                {props?.description}
+              </Tooltip>
+            ) : (
+              <Tooltip
+                placement="topLeft"
+                title={
+                  (Store.get('access-providers') || []).find((i: any) => i?.id === props?.provider)
+                    ?.description
+                }
+              >
+                {
+                  (Store.get('access-providers') || []).find((i: any) => i?.id === props?.provider)
+                    ?.description
+                }
+              </Tooltip>
+            )}
           </div>
         </div>
       </div>

+ 56 - 0
src/components/ProTableCard/CardItems/Stream/index.less

@@ -0,0 +1,56 @@
+@import '~antd/es/style/themes/default.less';
+
+.stream-card-item {
+  width: 100%;
+  background: url('/images/access-config-enabled.png') no-repeat;
+  background-size: 100% 100%;
+
+  .context-stream {
+    display: flex;
+    width: 100%;
+
+    .card {
+      display: flex;
+      flex-direction: column;
+      flex-grow: 1;
+      width: 0;
+      margin-left: 20px;
+
+      .header {
+        .stream-title {
+          width: 100%;
+          font-weight: 700;
+          font-size: 18px;
+        }
+      }
+      .container {
+        display: flex;
+        justify-content: space-between;
+        width: 100%;
+        margin-bottom: 20px;
+        padding-right: 20px;
+
+        div {
+          label {
+            color: #000;
+            font-size: 12px;
+            opacity: 0.45;
+          }
+
+          div {
+            color: #000;
+            font-weight: 700;
+            font-size: 12px;
+            opacity: 0.75;
+          }
+        }
+      }
+    }
+  }
+}
+
+:global {
+  .ant-pagination-item {
+    display: none;
+  }
+}

+ 60 - 0
src/components/ProTableCard/CardItems/Stream/index.tsx

@@ -0,0 +1,60 @@
+import React from 'react';
+import { TableCard } from '@/components';
+import '@/style/common.less';
+import './index.less';
+import { Tooltip } from 'antd';
+
+export interface StreamCardProps extends StreamItem {
+  detail?: React.ReactNode;
+  actions?: React.ReactNode[];
+  avatarSize?: number;
+}
+
+const defaultImage = require('/public/images/stream.png');
+
+export default (props: StreamCardProps) => {
+  return (
+    <TableCard
+      detail={props.detail}
+      actions={props.actions}
+      showStatus={false}
+      showMask={false}
+      className={'stream-card-item'}
+    >
+      <div className="context-stream">
+        <div>
+          <img width={88} height={88} src={defaultImage} alt={''} />
+        </div>
+        <div className="card">
+          <div className="header">
+            <div className="stream-title ellipsis">
+              <Tooltip title={props.name}>{props.name}</Tooltip>
+            </div>
+          </div>
+          <div className="container">
+            <div>
+              <label>服务商</label>
+              <div className={'ellipsis'}>
+                <Tooltip title={props?.provider}>{props?.provider}</Tooltip>
+              </div>
+            </div>
+            <div>
+              <label>RTP IP</label>
+              <div className={'ellipsis'}>
+                <Tooltip title={props?.configuration?.rtpIp}>{props?.configuration?.rtpIp}</Tooltip>
+              </div>
+            </div>
+            <div>
+              <label>API HOST</label>
+              <div className={'ellipsis'}>
+                <Tooltip title={props?.configuration?.apiHost}>
+                  {props?.configuration?.apiHost}
+                </Tooltip>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </TableCard>
+  );
+};

+ 1 - 1
src/components/ProTableCard/CardItems/networkCard.tsx

@@ -50,7 +50,7 @@ export default (props: NoticeCardProps) => {
       statusText={props.state.text}
       statusNames={{
         disabled: StatusColorEnum.error,
-        enabled: StatusColorEnum.processing,
+        enabled: StatusColorEnum.success,
       }}
       showMask={false}
     >

+ 5 - 5
src/components/ProTableCard/CardItems/product.tsx

@@ -113,12 +113,12 @@ export default (props: ProductCardProps) => {
       actions={props.actions}
       status={props.state}
       statusText={intl.formatMessage({
-        id: `pages.system.tenant.assetInformation.${props.state ? 'published' : 'unpublished'}`,
-        defaultMessage: '已发布',
+        id: `pages.device.product.status.${props.state ? 'enabled' : 'disabled'}`,
+        defaultMessage: '正常',
       })}
       statusNames={{
         0: StatusColorEnum.error,
-        1: StatusColorEnum.processing,
+        1: StatusColorEnum.success,
       }}
     >
       <div className={'pro-table-card-item'}>
@@ -132,11 +132,11 @@ export default (props: ProductCardProps) => {
           <div className={'card-item-content'}>
             <div>
               <label>设备类型</label>
-              <div className={'ellipsis'}>{props.deviceType ? props.deviceType.text : '--'}</div>
+              <div className={'ellipsis'}>{props?.deviceType?.text}</div>
             </div>
             <div>
               <label>接入方式</label>
-              <div className={'ellipsis'}>{props.transportProtocol || '--'}</div>
+              <div className={'ellipsis'}>{props.transportProtocol || '未接入'}</div>
             </div>
           </div>
         </div>

+ 2 - 2
src/components/ProTableCard/CardItems/protocol.tsx

@@ -20,9 +20,9 @@ export default (props: ProcotolCardProps) => {
       showMask={false}
       actions={props.actions}
       status={props.state === 1 ? 'enabled' : 'disabled'}
-      statusText={props.state === 1 ? '已发布' : '未发布'}
+      statusText={props.state === 1 ? '正常' : '禁用'}
       statusNames={{
-        enabled: StatusColorEnum.processing,
+        enabled: StatusColorEnum.success,
         disabled: StatusColorEnum.error,
       }}
     >

+ 1 - 1
src/components/ProTableCard/CardItems/scene.tsx

@@ -26,7 +26,7 @@ export default (props: DeviceCardProps) => {
       status={props.state.value}
       statusText={props.state.text}
       statusNames={{
-        started: StatusColorEnum.processing,
+        started: StatusColorEnum.success,
         disable: StatusColorEnum.error,
         notActive: StatusColorEnum.warning,
       }}

+ 1 - 1
src/components/RadioCard/index.less

@@ -26,7 +26,7 @@
     }
 
     &:not(:last-child) {
-      margin-right: 10px;
+      margin-right: 24px;
     }
 
     &:hover,

+ 3 - 1
src/locales/zh-CN/pages.ts

@@ -192,6 +192,8 @@ export default {
   'pages.device.product.deviceClass': '设备分类',
   'pages.device.product.status.published': '已发布',
   'pages.device.product.status.unpublished': '未发布',
+  'pages.device.product.status.enabled': '正常',
+  'pages.device.product.status.disabled': '禁用',
   'pages.device.product.status.all': '全部',
   'pages.device.productDetail': '产品详情',
   'pages.device.productDetail.id': '产品ID',
@@ -238,7 +240,7 @@ export default {
   'pages.device.productDetail.setting': '应用配置',
   'pages.device.productDetail.disable': '停用',
   'pages.device.productDetail.enabled': '启用',
-  'pages.device.productDetail.deleteTip': '已发布的产品不能进行删除操作',
+  'pages.device.productDetail.deleteTip': '已启用的产品不能进行删除操作',
 
   // 设备管理-设备分类
   'pages.device.type.device': '直连设备',

+ 1 - 5
src/pages/device/Category/index.tsx

@@ -65,9 +65,7 @@ const Category = observer(() => {
         defaultMessage: '分类名称',
       }),
       dataIndex: 'name',
-      width: 300,
       ellipsis: true,
-      fixed: 'left',
     },
     {
       title: '分类排序',
@@ -94,8 +92,7 @@ const Category = observer(() => {
         defaultMessage: '操作',
       }),
       valueType: 'option',
-      width: 200,
-      fixed: 'right',
+      width: 120,
       render: (text, record) => [
         <PermissionButton
           key="editable"
@@ -182,7 +179,6 @@ const Category = observer(() => {
       <ProTable
         params={param}
         search={false}
-        scroll={{ x: 1366 }}
         request={async (params) => {
           const response = await service.queryTree({
             paging: false,

+ 1 - 0
src/pages/device/DashBoard/index.tsx

@@ -416,6 +416,7 @@ const DeviceBoard = () => {
           height={500}
           defaultTime={'week'}
           showTime={true}
+          showTimeTool={true}
           onParamsChange={getEcharts}
         />
         {amapKey && (

+ 10 - 10
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx

@@ -47,13 +47,13 @@ const Status = observer((props: Props) => {
       key: 'product',
       name: '产品状态',
       data: 'product',
-      desc: '诊断产品状态是否已发布,未发布的状态将导致连接失败',
+      desc: '诊断产品状态是否正常,禁用状态将导致连接失败',
     },
     {
       key: 'device',
       name: '设备状态',
       data: 'device',
-      desc: '诊断设备状态是否已启用,未启用的状态将导致连接失败',
+      desc: '诊断设备状态是否正常,禁用状态将导致连接失败。',
     },
     {
       key: 'device-access',
@@ -207,7 +207,7 @@ const Status = observer((props: Props) => {
                                 text={
                                   networkPermission.action ? (
                                     <span>
-                                      网络组件未启用, 请
+                                      网络组件已禁用, 请
                                       <Popconfirm
                                         title="确认启用"
                                         onConfirm={async () => {
@@ -229,7 +229,7 @@ const Status = observer((props: Props) => {
                                       网络组件
                                     </span>
                                   ) : (
-                                    '网络组件未启用,请联系管理员'
+                                    '网络组件已禁用,请联系管理员'
                                   )
                                 }
                               />
@@ -327,9 +327,9 @@ const Status = observer((props: Props) => {
                   text={
                     productPermission.action ? (
                       <span>
-                        产品未发布,请
+                        产品已禁用,请
                         <Popconfirm
-                          title="确认发布"
+                          title="确认启用"
                           onConfirm={async () => {
                             const resp = await service.deployProduct(
                               InstanceModel.detail?.productId || '',
@@ -345,12 +345,12 @@ const Status = observer((props: Props) => {
                             }
                           }}
                         >
-                          <a>发布</a>
+                          <a>启用</a>
                         </Popconfirm>
                         产品
                       </span>
                     ) : (
-                      '无产品发布权限时:产品未发布,请联系管理员处理'
+                      '无产品发布权限时:产品已禁用,请联系管理员处理'
                     )
                   }
                 />
@@ -383,7 +383,7 @@ const Status = observer((props: Props) => {
                 text={
                   devicePermission.action ? (
                     <span>
-                      设备未启用,请
+                      设备已禁用,请
                       <Popconfirm
                         title="确认启用"
                         onConfirm={async () => {
@@ -405,7 +405,7 @@ const Status = observer((props: Props) => {
                       设备
                     </span>
                   ) : (
-                    '设备未启用,请联系管理员处理'
+                    '设备已禁用,请联系管理员处理'
                   )
                 }
               />

+ 82 - 32
src/pages/device/Instance/Detail/Functions/index.tsx

@@ -1,49 +1,99 @@
-import { Card, Tabs } from 'antd';
+import { Button, Card, Tabs } from 'antd';
 import { InstanceModel } from '@/pages/device/Instance';
 import type { FunctionMetadata } from '@/pages/device/Product/typings';
 import FnForm from './form';
 import AModel from './AdvancedMode';
-import { Empty } from '@/components';
+import { Empty, PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
+import { getMenuPathByParams } from '@/utils/menu';
+import useHistory from '@/hooks/route/useHistory';
 
 const Functions = () => {
   const functionList = JSON.parse(InstanceModel.detail.metadata || '{}')
     .functions as FunctionMetadata[];
+  const history = useHistory();
 
   const { minHeight } = useDomFullHeight(`.device-detail-function`);
+  const { permission } = PermissionButton.usePermission('device/Product');
+
+  const empty = () => {
+    const isIndependent = InstanceModel.detail?.independentMetadata;
+    const path = isIndependent
+      ? getMenuPathByParams('device/Product/Detail', InstanceModel.detail?.productId)
+      : getMenuPathByParams('device/Instance/Detail', InstanceModel.detail?.id);
+
+    let description = <></>;
+    if (isIndependent) {
+      // 物模型解绑
+      if (!permission.update) {
+        description = <span>请联系管理员配置物模型属性</span>;
+      } else {
+        description = (
+          <span>
+            暂无数据, 请前往产品配置
+            <Button
+              style={{ margin: '0 6px' }}
+              type={'link'}
+              onClick={() => {
+                history.push(`${path}?key=metadata`);
+              }}
+            >
+              物模型-功能定义
+            </Button>
+          </span>
+        );
+      }
+    } else {
+      description = (
+        <span>
+          暂无数据,请配置
+          <Button
+            style={{ margin: '0 6px' }}
+            type={'link'}
+            onClick={() => {
+              history.push(`${path}?key=metadata`);
+            }}
+          >
+            物模型-功能定义
+          </Button>
+        </span>
+      );
+    }
+
+    return <Empty description={description} />;
+  };
 
   return (
     <Card className={'device-detail-function'} style={{ minHeight: minHeight }}>
-      <Tabs>
-        <Tabs.TabPane tab={'精简模式'} key={1}>
-          <Tabs tabPosition="left">
-            {functionList &&
-              functionList.map((fn) => {
-                return (
-                  <Tabs.TabPane tab={fn.name} key={fn.id}>
-                    <FnForm data={fn} />
-                  </Tabs.TabPane>
-                );
-              })}
-          </Tabs>
-        </Tabs.TabPane>
-        <Tabs.TabPane tab={'高级模式'} key={2}>
-          <Tabs tabPosition="left">
-            {functionList &&
-              functionList.map((fn) => {
-                return (
-                  <Tabs.TabPane tab={fn.name} key={fn.id}>
-                    <AModel data={fn} />
-                  </Tabs.TabPane>
-                );
-              })}
-          </Tabs>
-        </Tabs.TabPane>
-      </Tabs>
-      {!functionList && (
-        <div style={{ height: minHeight - 150 }}>
-          <Empty />
-        </div>
+      {functionList ? (
+        <Tabs>
+          <Tabs.TabPane tab={'精简模式'} key={1}>
+            <Tabs tabPosition="left">
+              {functionList &&
+                functionList.map((fn) => {
+                  return (
+                    <Tabs.TabPane tab={fn.name} key={fn.id}>
+                      <FnForm data={fn} />
+                    </Tabs.TabPane>
+                  );
+                })}
+            </Tabs>
+          </Tabs.TabPane>
+          <Tabs.TabPane tab={'高级模式'} key={2}>
+            <Tabs tabPosition="left">
+              {functionList &&
+                functionList.map((fn) => {
+                  return (
+                    <Tabs.TabPane tab={fn.name} key={fn.id}>
+                      <AModel data={fn} />
+                    </Tabs.TabPane>
+                  );
+                })}
+            </Tabs>
+          </Tabs.TabPane>
+        </Tabs>
+      ) : (
+        <div style={{ height: minHeight - 150 }}>{empty()}</div>
       )}
     </Card>
   );

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

@@ -27,6 +27,7 @@ const EventLog = (props: Props) => {
       dataIndex: 'timestamp',
       title: '时间',
       sorter: true,
+      ellipsis: true,
       renderText: (text: string) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
     },
     {
@@ -95,6 +96,7 @@ const EventLog = (props: Props) => {
               key: i.id,
               title: i.name,
               dataIndex: i.id,
+              ellipsis: true,
               renderText: (text) => (typeof text === 'object' ? JSON.stringify(text) : text),
             } as ProColumns),
         )

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

@@ -233,6 +233,7 @@ const InstanceDetail = observer(() => {
   }, [params.id]);
 
   useEffect(() => {
+    console.log(location.query);
     if ((location as any).query?.key) {
       setTab((location as any).query?.key || 'detail');
     }

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

@@ -394,7 +394,7 @@ const Instance = () => {
         defaultMessage: '操作',
       }),
       valueType: 'option',
-      width: 200,
+      width: 120,
       fixed: 'right',
       render: (text, record) => tools(record),
     },

+ 22 - 15
src/pages/device/Product/Detail/Access/AccessConfig/index.tsx

@@ -59,11 +59,11 @@ const AccessConfig = (props: Props) => {
       valueType: 'select',
       valueEnum: {
         disabled: {
-          text: '已停止',
+          text: '禁用',
           status: 'disabled',
         },
         enabled: {
-          text: '已启动',
+          text: '正常',
           status: 'enabled',
         },
       },
@@ -98,19 +98,26 @@ const AccessConfig = (props: Props) => {
             messageProtocol: currrent.protocol,
           });
           if (resp.status === 200) {
-            service1
-              .changeDeploy(productModel.current?.id || '', 'deploy')
-              .subscribe((response) => {
-                if (response) {
-                  service1.detail(productModel.current?.id || '').then((res) => {
-                    if (res.status === 200) {
-                      productModel.current = { ...res.result };
-                      message.success('操作成功!');
-                    }
-                    close();
-                  });
-                }
-              });
+            service1.detail(productModel.current?.id || '').then((res) => {
+              if (res.status === 200) {
+                productModel.current = { ...res.result };
+                message.success('操作成功!');
+              }
+              close();
+            });
+            // service1
+            //   .changeDeploy(productModel.current?.id || '', 'deploy')
+            //   .subscribe((response) => {
+            //     if (response) {
+            //       service1.detail(productModel.current?.id || '').then((res) => {
+            //         if (res.status === 200) {
+            //           productModel.current = { ...res.result };
+            //           message.success('操作成功!');
+            //         }
+            //         close();
+            //       });
+            //     }
+            //   });
           }
         } else {
           message.success('请选择接入方式');

+ 6 - 6
src/pages/device/Product/Detail/index.tsx

@@ -264,7 +264,7 @@ const ProductDetail = observer(() => {
       subTitle={
         permission.update ? (
           <Popconfirm
-            title={productModel.current?.state === 1 ? '确认取消发布' : '确认发布'}
+            title={productModel.current?.state === 1 ? '确认禁用' : '确认启用'}
             onConfirm={() => {
               changeDeploy(statusMap[productModel.current?.state || 0].action);
             }}
@@ -272,8 +272,8 @@ const ProductDetail = observer(() => {
             <Switch
               key={2}
               checked={productModel.current?.state === 1}
-              checkedChildren="已发布"
-              unCheckedChildren="未发布"
+              checkedChildren="正常"
+              unCheckedChildren="禁用"
             />
           </Popconfirm>
         ) : (
@@ -287,8 +287,8 @@ const ProductDetail = observer(() => {
               key={2}
               disabled
               checked={productModel.current?.state === 1}
-              checkedChildren="已发布"
-              unCheckedChildren="未发布"
+              checkedChildren="正常"
+              unCheckedChildren="禁用"
             />
           </Tooltip>
         )
@@ -304,7 +304,7 @@ const ProductDetail = observer(() => {
               changeDeploy('deploy');
             },
           }}
-          tooltip={productModel.current?.state === 0 ? { title: '请先发布产品' } : undefined}
+          tooltip={productModel.current?.state === 0 ? { title: '请先启用产品' } : undefined}
           isPermission={permission.update}
           disabled={productModel.current?.state === 0}
         >

+ 1 - 1
src/pages/device/Product/Save/index.tsx

@@ -121,7 +121,7 @@ const Save = (props: Props) => {
         form.resetFields();
         close();
       }}
-      width={610}
+      width={640}
       title={intl.formatMessage({
         id: `pages.data.option.${props.model || 'add'}`,
         defaultMessage: '新增',

+ 12 - 12
src/pages/device/Product/index.tsx

@@ -30,8 +30,8 @@ import useLocation from '@/hooks/route/useLocation';
 
 export const service = new Service('device-product');
 export const statusMap = {
-  1: <Badge status="processing" text="已发布" />,
-  0: <Badge status="error" text="未发布" />,
+  1: <Badge status="success" text="正常" />,
+  0: <Badge status="error" text="禁用" />,
 };
 export const productModel = model<{
   current: ProductItem | undefined;
@@ -62,10 +62,10 @@ const Product = observer(() => {
   const status = {
     1: (
       <Badge
-        status="processing"
+        status="success"
         text={intl.formatMessage({
-          id: 'pages.system.tenant.assetInformation.published',
-          defaultMessage: '已发布',
+          id: 'pages.device.product.status.enabled',
+          defaultMessage: '正常',
         })}
       />
     ),
@@ -73,8 +73,8 @@ const Product = observer(() => {
       <Badge
         status="error"
         text={intl.formatMessage({
-          id: 'pages.system.tenant.assetInformation.unpublished',
-          defaultMessage: '未发布',
+          id: 'pages.device.product.status.disabled',
+          defaultMessage: '禁用',
         })}
       />
     ),
@@ -298,15 +298,15 @@ const Product = observer(() => {
         // },
         0: {
           text: intl.formatMessage({
-            id: 'pages.device.product.status.unpublished',
-            defaultMessage: '未发布',
+            id: 'pages.device.product.status.disabled',
+            defaultMessage: '禁用',
           }),
           status: 0,
         },
         1: {
           text: intl.formatMessage({
-            id: 'pages.device.product.status.published',
-            defaultMessage: '已发布',
+            id: 'pages.device.product.status.enabled',
+            defaultMessage: '正常',
           }),
           status: 1,
         },
@@ -582,7 +582,7 @@ const Product = observer(() => {
                     ? {
                         title: intl.formatMessage({
                           id: 'pages.device.productDetail.deleteTip',
-                          defaultMessage: '已发布的产品不能进行删除操作',
+                          defaultMessage: '已启用的产品不能进行删除操作',
                         }),
                       }
                     : undefined

+ 5 - 2
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -22,6 +22,7 @@ import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu
 import { ExclamationCircleFilled } from '@ant-design/icons';
 import TitleComponent from '@/components/TitleComponent';
 import { PermissionButton } from '@/components';
+import { useDomFullHeight } from '@/hooks';
 
 interface Props {
   change: () => void;
@@ -31,7 +32,7 @@ interface Props {
 
 const Access = (props: Props) => {
   const [form] = Form.useForm();
-
+  const { minHeight } = useDomFullHeight(`.access`);
   const history = useHistory();
 
   const [current, setCurrent] = useState<number>(0);
@@ -356,6 +357,7 @@ const Access = (props: Props) => {
               </Row>
             ) : (
               <Empty
+                style={{ marginTop: '10%', marginBottom: '10%' }}
                 description={
                   <span>
                     暂无数据
@@ -451,6 +453,7 @@ const Access = (props: Props) => {
               </Row>
             ) : (
               <Empty
+                style={{ marginTop: '10%', marginBottom: '10%' }}
                 description={
                   <span>
                     暂无数据
@@ -628,7 +631,7 @@ const Access = (props: Props) => {
   };
 
   return (
-    <Card>
+    <Card className="access" style={{ minHeight }}>
       {!props.data?.id && (
         <Button
           type="link"

+ 21 - 2
src/pages/link/AccessConfig/index.tsx

@@ -10,6 +10,7 @@ import { DeleteOutlined, EditOutlined, PlayCircleOutlined, StopOutlined } from '
 import AccessConfigCard from '@/components/ProTableCard/CardItems/AccessConfig';
 import { PermissionButton, Empty } from '@/components';
 import { useDomFullHeight } from '@/hooks';
+import { Store } from 'jetlinks-store';
 
 export const service = new Service('gateway/device');
 
@@ -26,16 +27,29 @@ const AccessConfig = () => {
       dataIndex: 'name',
     },
     {
+      title: '网关类型',
+      dataIndex: 'provider',
+      renderText: (text) => text?.text,
+      valueType: 'select',
+      request: () =>
+        service.getProviders().then((resp: any) => {
+          return (resp?.result || []).map((item: any) => ({
+            label: item.name,
+            value: item.id,
+          }));
+        }),
+    },
+    {
       title: '状态',
       dataIndex: 'state',
       valueType: 'select',
       valueEnum: {
         disabled: {
-          text: '已停止',
+          text: '禁用',
           status: 'disabled',
         },
         enabled: {
-          text: '已启动',
+          text: '正常',
           status: 'enabled',
         },
       },
@@ -65,6 +79,11 @@ const AccessConfig = () => {
   };
 
   useEffect(() => {
+    service.getProviders().then((resp: any) => {
+      if (resp.status === 200) {
+        Store.set('access-providers', resp.result);
+      }
+    });
     handleSearch(param);
   }, []);
 

+ 1 - 1
src/pages/link/AccessConfig/typings.d.ts

@@ -3,7 +3,7 @@ import type { BaseItem } from '@/utils/typings';
 type AccessItem = {
   id: string | undefined;
   name: string;
-  description: string;
+  description?: string;
   provider: string;
   protocol: string;
   transport: string;

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

@@ -29,7 +29,6 @@ const Certificate = () => {
       title: '证书标准',
       render: (_, record: any) => <span>{record.type?.text || '-'}</span>,
       valueType: 'select',
-      fixed: 'left',
       valueEnum: {
         common: {
           text: '国际标准',
@@ -40,13 +39,11 @@ const Certificate = () => {
     {
       dataIndex: 'name',
       title: '证书名称',
-      width: '30%',
       ellipsis: true,
     },
     {
       dataIndex: 'description',
       title: '说明',
-      width: '30%',
       ellipsis: true,
       render: (text: any) => (
         <div style={{ width: '100%' }} className="ellipsis">
@@ -62,9 +59,7 @@ const Certificate = () => {
         defaultMessage: '操作',
       }),
       valueType: 'option',
-      align: 'center',
-      width: 200,
-      fixed: 'right',
+      width: 100,
       render: (text, record) => [
         <PermissionButton
           key={'update'}
@@ -125,7 +120,6 @@ const Certificate = () => {
         params={param}
         columns={columns}
         search={false}
-        scroll={{ x: 1366 }}
         rowKey="id"
         tableClassName={'link-certificate'}
         tableStyle={{ minHeight }}

+ 2 - 2
src/pages/link/Channel/Opcua/index.tsx

@@ -71,8 +71,8 @@ const Opcua = () => {
         },
         enabled: {
           text: intl.formatMessage({
-            id: 'pages.data.option.enabled',
-            defaultMessage: '启用',
+            id: 'pages.device.product.status.enabled',
+            defaultMessage: '正常',
           }),
           status: 'enabled',
         },

+ 1 - 0
src/pages/link/DashBoard/index.tsx

@@ -429,6 +429,7 @@ export default () => {
             initialValues={{ type: 'bytesSent' }}
             height={400}
             closeInitialParams={true}
+            showTimeTool={true}
             extraParams={{
               key: 'type',
               Children: (

+ 14 - 3
src/pages/link/Protocol/index.tsx

@@ -73,8 +73,19 @@ const Protocol = () => {
     {
       dataIndex: 'state',
       title: '状态',
+      valueType: 'select',
+      valueEnum: {
+        0: {
+          text: '禁用',
+          status: 0,
+        },
+        1: {
+          text: '正常',
+          status: 1,
+        },
+      },
       renderText: (text) => (
-        <Badge color={text !== 1 ? 'red' : 'green'} text={text !== 1 ? '未发布' : '已发布'} />
+        <Badge color={text !== 1 ? 'red' : 'green'} text={text !== 1 ? '禁用' : '正常'} />
       ),
     },
     {
@@ -114,10 +125,10 @@ const Protocol = () => {
           type={'link'}
           style={{ padding: 0 }}
           tooltip={{
-            title: record.state === 1 ? '撤销' : '发布',
+            title: record.state === 1 ? '禁用' : '启用',
           }}
           popConfirm={{
-            title: `确认${record.state === 1 ? '撤销' : '发布'}`,
+            title: `确认${record.state === 1 ? '禁用' : '启用'}`,
             onConfirm: () => {
               if (record.state === 1) {
                 modifyState(record.id, 'un-deploy');

+ 52 - 31
src/pages/link/Protocol/save/index.tsx

@@ -1,4 +1,4 @@
-import { Button, message, Modal } from 'antd';
+import { Button, Modal } from 'antd';
 import { createForm, registerValidateRules } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import React, { useEffect, useState } from 'react';
@@ -9,7 +9,8 @@ import { service } from '@/pages/link/Protocol';
 import FileUpload from '../FileUpload';
 import type { ProtocolItem } from '@/pages/link/Protocol/typings';
 import { PermissionButton } from '@/components';
-
+import { RadioCard } from '@/components';
+import { onlyMessage } from '@/utils/util';
 interface Props {
   data: ProtocolItem | undefined;
   close: () => void;
@@ -44,6 +45,7 @@ const Save = (props: Props) => {
       Select,
       FileUpload,
       FormGrid,
+      RadioCard,
     },
     scope: {
       icon(name: any) {
@@ -59,8 +61,9 @@ const Save = (props: Props) => {
         type: 'void',
         'x-component': 'FormGrid',
         'x-component-props': {
-          maxColumns: 1,
-          minColumns: 1,
+          maxColumns: 2,
+          minColumns: 2,
+          columnGap: 24,
         },
         properties: {
           id: {
@@ -69,7 +72,7 @@ const Save = (props: Props) => {
             'x-decorator': 'FormItem',
             'x-disabled': !!props.data?.id,
             'x-decorator-props': {
-              gridSpan: 1,
+              gridSpan: 2,
             },
             'x-validator': [
               {
@@ -112,7 +115,7 @@ const Save = (props: Props) => {
             'x-component': 'Input',
             'x-decorator': 'FormItem',
             'x-decorator-props': {
-              gridSpan: 1,
+              gridSpan: 2,
             },
             'x-component-props': {
               placeholder: '请输入名称',
@@ -130,14 +133,30 @@ const Save = (props: Props) => {
           },
           type: {
             title: '类型',
-            'x-component': 'Select',
+            'x-component': 'RadioCard',
             'x-decorator': 'FormItem',
             'x-disabled': !!props.data?.id,
             'x-decorator-props': {
+              gridSpan: 2,
               tooltip: <div>jar:上传协议jar包,文件格式支持.jar或.zip</div>,
             },
             'x-component-props': {
-              placeholder: '请选择类型',
+              model: 'singular',
+              itemStyle: {
+                width: '50%',
+              },
+              options: [
+                {
+                  label: 'Jar',
+                  value: 'jar',
+                  imgUrl: require('/public/images/jar.png'),
+                },
+                {
+                  label: 'Local',
+                  value: 'local',
+                  imgUrl: require('/public/images/local.png'),
+                },
+              ],
             },
             'x-validator': [
               {
@@ -145,11 +164,11 @@ const Save = (props: Props) => {
                 message: '请选择类型',
               },
             ],
-            enum: [
-              { label: 'jar', value: 'jar' },
-              { label: 'local', value: 'local' },
-              // { label: 'script', value: 'script' },
-            ],
+            // enum: [
+            //   { label: 'jar', value: 'jar' },
+            //   { label: 'local', value: 'local' },
+            //   // { label: 'script', value: 'script' },
+            // ],
           },
           configuration: {
             type: 'object',
@@ -159,10 +178,9 @@ const Save = (props: Props) => {
                 'x-decorator': 'FormItem',
                 'x-visible': false,
                 'x-decorator-props': {
+                  gridSpan: 2,
                   tooltip: (
-                    <div>
-                      local:填写本地协议编译目录绝对地址,如:d:/workspace/protocol/target/classes
-                    </div>
+                    <div>local:填写本地协议编译目录绝对地址,如:d:/protocol/target/classes</div>
                   ),
                 },
                 'x-validator': [
@@ -189,6 +207,9 @@ const Save = (props: Props) => {
             title: '说明',
             'x-component': 'Input.TextArea',
             'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 2,
+            },
             'x-component-props': {
               rows: 3,
               showCount: true,
@@ -209,7 +230,7 @@ const Save = (props: Props) => {
       response = await service.update(value);
     }
     if (response && response.status === 200) {
-      message.success('操作成功');
+      onlyMessage('操作成功');
       if (deploy) {
         await service.modifyState(value.id, 'deploy');
       }
@@ -242,20 +263,20 @@ const Save = (props: Props) => {
         >
           保存
         </Button>,
-        <Button
-          key={3}
-          type="primary"
-          onClick={() => {
-            save(true);
-          }}
-          disabled={
-            props.data?.id
-              ? !permission.update && !permission.action
-              : !permission.add && !permission.action
-          }
-        >
-          保存并发布
-        </Button>,
+        // <Button
+        //   key={3}
+        //   type="primary"
+        //   onClick={() => {
+        //     save(true);
+        //   }}
+        //   disabled={
+        //     props.data?.id
+        //       ? !permission.update && !permission.action
+        //       : !permission.add && !permission.action
+        //   }
+        // >
+        //   保存并发布
+        // </Button>,
       ]}
     >
       <Form form={form} layout="vertical">

+ 5 - 4
src/pages/link/Type/index.tsx

@@ -126,17 +126,17 @@ const Network = () => {
       valueType: 'select',
       valueEnum: {
         disabled: {
-          text: '已停止',
+          text: '禁用',
           status: 'disabled',
         },
         enabled: {
-          text: '已启动',
+          text: '正常',
           status: 'enabled',
         },
       },
       render: (text, record) => {
         if (record.state.value === 'enabled') {
-          return <Badge color="lime" text="正常" />;
+          return <Badge color="#52c41a" text="正常" />;
         }
         return <Badge color="red" text="禁用" />;
       },
@@ -144,6 +144,7 @@ const Network = () => {
     {
       dataIndex: 'description',
       title: '说明',
+      ellipsis: true,
     },
     {
       title: intl.formatMessage({
@@ -151,7 +152,7 @@ const Network = () => {
         defaultMessage: '操作',
       }),
       valueType: 'option',
-      width: 200,
+      width: 120,
       fixed: 'right',
       render: (text, record) => [
         <PermissionButton

+ 33 - 18
src/pages/media/Cascade/Save/index.tsx

@@ -80,6 +80,10 @@ const Save = () => {
                 host: sipConfigs.host,
                 port: sipConfigs.port,
               },
+              remotePublic: {
+                host: sipConfigs.publicHost,
+                port: sipConfigs.publicPort,
+              },
             },
           };
           form.setFieldsValue(data);
@@ -117,6 +121,8 @@ const Save = () => {
               remotePort: values.sipConfigs.public.port,
               host: values.sipConfigs.local.host,
               port: values.sipConfigs.local.port,
+              publicHost: values.sipConfigs.remotePublic.host,
+              publicPort: values.sipConfigs.remotePublic.port,
             };
             delete values.sipConfigs;
             delete sipConfigs.public;
@@ -227,24 +233,6 @@ const Save = () => {
             </Col>
             <Col span={12}>
               <Form.Item
-                label="传输协议"
-                name={['sipConfigs', 'transport']}
-                rules={[{ required: true, message: '请选择传输协议' }]}
-              >
-                <Radio.Group
-                  optionType="button"
-                  buttonStyle="solid"
-                  onChange={(e) => {
-                    setTransport(e.target.value);
-                  }}
-                >
-                  <Radio.Button value="UDP">UDP</Radio.Button>
-                  <Radio.Button value="TCP">TCP</Radio.Button>
-                </Radio.Group>
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item
                 label={
                   <span>
                     SIP本地地址
@@ -264,6 +252,33 @@ const Save = () => {
             </Col>
             <Col span={12}>
               <Form.Item
+                label="SIP远程地址"
+                name={['sipConfigs', 'remotePublic']}
+                rules={[{ required: true, message: '请输入SIP远程地址' }, { validator: checkSIP }]}
+              >
+                <SipComponent />
+              </Form.Item>
+            </Col>
+            <Col span={24}>
+              <Form.Item
+                label="传输协议"
+                name={['sipConfigs', 'transport']}
+                rules={[{ required: true, message: '请选择传输协议' }]}
+              >
+                <Radio.Group
+                  optionType="button"
+                  buttonStyle="solid"
+                  onChange={(e) => {
+                    setTransport(e.target.value);
+                  }}
+                >
+                  <Radio.Button value="UDP">UDP</Radio.Button>
+                  <Radio.Button value="TCP">TCP</Radio.Button>
+                </Radio.Group>
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item
                 label="用户"
                 name={['sipConfigs', 'user']}
                 rules={[{ required: true, message: '请输入用户' }]}

+ 1 - 0
src/pages/media/DashBoard/index.tsx

@@ -223,6 +223,7 @@ export default () => {
           </DashBoardTopCard.Item>
         </DashBoardTopCard>
         <DashBoard
+          showTimeTool={true}
           className={'media-dash-board-body'}
           title={'播放数量(人次)'}
           options={options}

+ 1 - 1
src/pages/media/Device/Save/index.tsx

@@ -122,7 +122,7 @@ export default (props: SaveProps) => {
           form.resetFields();
           close();
         }}
-        width={610}
+        width={640}
         title={intl.formatMessage({
           id: `pages.data.option.${props.model || 'add'}`,
           defaultMessage: '新增',

+ 3 - 1
src/pages/media/SplitScreen/index.tsx

@@ -6,11 +6,13 @@ import { ScreenPlayer } from '@/components';
 import { ptzStart, ptzStop, ptzTool } from './service';
 import { useRef, useState } from 'react';
 import './index.less';
+import { useDomFullHeight } from '@/hooks';
 
 const SplitScreen = () => {
   const [deviceId, setDeviceId] = useState('');
   const [channelId, setChannelId] = useState('');
   const player = useRef<any>(null);
+  const { minHeight } = useDomFullHeight(`.splitScreen`);
 
   const getMediaUrl = (dId: string, cId: string): string => {
     return ptzStart(dId, cId, 'mp4');
@@ -24,7 +26,7 @@ const SplitScreen = () => {
 
   return (
     <PageContainer>
-      <Card>
+      <Card style={{ minHeight }} className="splitScreen">
         <div className="split-screen">
           <LeftTree onSelect={mediaStart} />
           <div className="right-content">

+ 0 - 57
src/pages/media/Stream/index.less

@@ -1,57 +0,0 @@
-.cardRender {
-  width: 100%;
-  background: url('/images/access.png') no-repeat;
-  background-size: 100% 100%;
-
-  .card {
-    .header {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      width: 100%;
-
-      .title {
-        width: 50%;
-        overflow: hidden;
-        color: rgba(0, 0, 0, 0.85);
-        font-weight: 700;
-        font-size: 16px;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-      }
-
-      .actions {
-        .action span {
-          margin-left: 5px;
-          color: #4f4f4f;
-        }
-      }
-    }
-
-    .content {
-      display: flex;
-      justify-content: space-between;
-      width: 100%;
-      margin-top: 20px;
-
-      .item {
-        display: flex;
-        flex-direction: column;
-
-        .itemTitle {
-          color: rgba(0, 0, 0, 0.45);
-          font-size: 12px;
-        }
-
-        p {
-          color: rgba(0, 0, 0, 0.75);
-          font-weight: 700;
-          font-size: 12px;
-        }
-      }
-    }
-  }
-}
-.stream {
-  display: flex;
-}

+ 39 - 83
src/pages/media/Stream/index.tsx

@@ -1,17 +1,16 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import type { StreamItem } from '@/pages/media/Stream/typings';
 import SearchComponent from '@/components/SearchComponent';
 import type { ProColumns } from '@jetlinks/pro-table';
-import { Button, Card, Col, Empty, message, Pagination, Row, Space } from 'antd';
+import { Button, Card, Col, Empty, message, Pagination, Row } from 'antd';
 import { useEffect, useState } from 'react';
 import Service from '@/pages/media/Stream/service';
 import { getButtonPermission, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import { useHistory } from 'umi';
-import styles from './index.less';
 import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
 import { model } from '@formily/reactive';
 import { PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
+import StreamCard from '@/components/ProTableCard/CardItems/Stream';
 
 export const service = new Service('media/server');
 
@@ -92,89 +91,46 @@ const Stream = () => {
             >
               新增
             </PermissionButton>
-
             <Row gutter={[16, 16]} style={{ marginTop: 10 }}>
               {(dataSource?.data || []).map((item: any) => (
                 <Col key={item.id} span={12}>
-                  <Card hoverable className={styles.cardRender}>
-                    <div className={styles.card}>
-                      <div className={styles.header}>
-                        <div className={styles.title}>{item?.name}</div>
-                        <div className={styles.actions}>
-                          <Space>
-                            <PermissionButton
-                              isPermission={permission.update}
-                              className={styles.action}
-                              onClick={() => {
-                                history.push(
-                                  `${getMenuPathByParams(
-                                    MENUS_CODE['media/Stream/Detail'],
-                                    item.id,
-                                  )}`,
-                                );
-                                StreamModel.current = { ...item };
-                              }}
-                              key="button"
-                              type="link"
-                            >
-                              <EditOutlined
-                                style={{
-                                  color: permission.update ? '#000000' : 'rgba(0, 0, 0, .65)',
-                                  cursor: permission.update ? 'pointer' : 'not-allowed',
-                                }}
-                              />
-                              <span>编辑</span>
-                            </PermissionButton>
-                            <PermissionButton
-                              isPermission={permission.delete}
-                              popConfirm={{
-                                title: '确认删除',
-                                onConfirm: () => {
-                                  service.remove(item.id).then((resp: any) => {
-                                    if (resp.status === 200) {
-                                      message.success('操作成功!');
-                                      handleSearch({ pageSize: 10, terms: [] });
-                                    }
-                                  });
-                                },
-                              }}
-                              key="delete"
-                              type="link"
-                            >
-                              <span
-                                className={styles.action}
-                                style={{
-                                  color: permission.update ? '#000000' : 'rgba(0, 0, 0, .65)',
-                                  cursor: permission.update ? 'pointer' : 'not-allowed',
-                                }}
-                              >
-                                <DeleteOutlined
-                                  style={{
-                                    color: permission.update ? '#E50012' : 'rgba(0, 0, 0, .65)',
-                                  }}
-                                />
-                                <span>删除</span>
-                              </span>
-                            </PermissionButton>
-                          </Space>
-                        </div>
-                      </div>
-                      <div className={styles.content}>
-                        <div className={styles.item}>
-                          <div className={styles.itemTitle}>服务商</div>
-                          <p>{item?.provider}</p>
-                        </div>
-                        <div className={styles.item}>
-                          <div className={styles.itemTitle}>RTP IP</div>
-                          <p>{item?.configuration?.rtpIp}</p>
-                        </div>
-                        <div className={styles.item}>
-                          <div className={styles.itemTitle}>API HOST</div>
-                          <p>{item?.configuration?.apiHost}</p>
-                        </div>
-                      </div>
-                    </div>
-                  </Card>
+                  <StreamCard
+                    {...item}
+                    actions={[
+                      <PermissionButton
+                        isPermission={permission.update}
+                        onClick={() => {
+                          history.push(
+                            `${getMenuPathByParams(MENUS_CODE['media/Stream/Detail'], item.id)}`,
+                          );
+                          StreamModel.current = { ...item };
+                        }}
+                        key="button"
+                        type="link"
+                      >
+                        <EditOutlined />
+                        编辑
+                      </PermissionButton>,
+                      <PermissionButton
+                        isPermission={permission.delete}
+                        popConfirm={{
+                          title: '确认删除',
+                          onConfirm: () => {
+                            service.remove(item.id).then((resp: any) => {
+                              if (resp.status === 200) {
+                                message.success('操作成功!');
+                                handleSearch({ pageSize: 10, terms: [] });
+                              }
+                            });
+                          },
+                        }}
+                        key="delete"
+                        type="link"
+                      >
+                        <DeleteOutlined />
+                      </PermissionButton>,
+                    ]}
+                  />
                 </Col>
               ))}
             </Row>

+ 0 - 1
src/pages/media/Stream/service.ts

@@ -1,7 +1,6 @@
 import BaseService from '@/utils/BaseService';
 import { request } from 'umi';
 import SystemConst from '@/utils/const';
-import type { StreamItem } from './typings';
 
 class Service extends BaseService<StreamItem> {
   queryProviders = () =>

+ 4 - 4
src/pages/media/Stream/typings.d.ts

@@ -1,7 +1,7 @@
-import type { BaseItem } from '@/utils/typings';
-
 type StreamItem = {
-  description: string;
+  id?: string;
+  name: string;
+  description?: string;
   provider: string;
   configuration: {
     secret?: string;
@@ -12,4 +12,4 @@ type StreamItem = {
     dynamicRtpPort: boolean;
     dynamicRtpPortRange: number[];
   };
-} & BaseItem;
+};

+ 1 - 1
src/pages/notice/Template/Detail/doc/AliyunSms.tsx

@@ -16,7 +16,7 @@ const AliyunSms = () => {
 
       <div>
         <h2> 1、绑定配置</h2>
-        <div> 绑定通知配置</div>
+        <div> 使用固定的通知配置发送此通知模板</div>
         <h2> 2、模板</h2>
         <div> 阿里云短信平台自定义的模板名称</div>
         <h2> 3、收信人</h2>

+ 1 - 1
src/pages/notice/Template/Detail/doc/AliyunVoice.tsx

@@ -15,7 +15,7 @@ const AliyunVoice = () => {
       <h1>2.模板配置说明</h1>
       <div>
         <h2>1、绑定配置</h2>
-        <div> 绑定通知配置</div>
+        <div> 使用固定的通知配置发送此通知模板</div>
         <h2> 2、模板ID</h2>
         <div> 阿里云语音对每一条语音通知分配的唯一ID标识</div>
         <h2> 3、被叫号码</h2>

+ 1 - 1
src/pages/notice/Template/Detail/doc/DingTalk.tsx

@@ -19,7 +19,7 @@ const DingTalk = () => {
       </div>
       <h1> 2.模板配置说明</h1>
       <h2> 1、绑定配置</h2>
-      <div> 绑定通知配置</div>
+      <div> 使用固定的通知配置发送此通知模板</div>
       <h2> 2. Agentid</h2>
       <div> 应用唯一标识</div>
       <div className="image">

+ 1 - 1
src/pages/notice/Template/Detail/doc/DingTalkRebot.tsx

@@ -14,7 +14,7 @@ const DingTalkRebot = () => {
       <h1>2.模板配置说明</h1>
       <div>
         <h2> 1、绑定配置</h2>
-        <div> 绑定通知配置</div>
+        <div> 使用固定的通知配置发送此通知模板</div>
         <h2> 2、消息类型</h2>
         <div> 目前支持text、markdown、link3种。</div>
         <h2> 3. 模板内容</h2>

+ 1 - 1
src/pages/notice/Template/Detail/doc/WeixinApp.tsx

@@ -16,7 +16,7 @@ const WeixinApp = () => {
       <h1>2.模板配置说明</h1>
       <div>
         <h2>1. 绑定配置</h2>
-        <div>绑定通知配置</div>
+        <div>使用固定的通知配置发送此通知模板</div>
       </div>
       <div>
         <h2>2. 用户标签</h2>

+ 1 - 1
src/pages/notice/Template/Detail/doc/WeixinCorp.tsx

@@ -19,7 +19,7 @@ const WeixinCorp = () => {
       <h1>2.模版配置说明</h1>
       <div>
         <h2> 1、绑定配置</h2>
-        <div> 绑定通知配置</div>
+        <div> 使用固定的通知配置发送此通知模板</div>
         <h2> 2. Agentid</h2>
         <div> 应用唯一标识</div>
         <div> 获取路径:“企业微信”管理后台--“应用管理”--“应用”--“查看应用”</div>

+ 1 - 5
src/pages/notice/Template/index.tsx

@@ -99,8 +99,6 @@ const Template = observer(() => {
       dataIndex: 'name',
       title: '模版名称',
       ellipsis: true,
-      fixed: 'left',
-      width: '25%',
     },
     {
       dataIndex: 'provider',
@@ -120,9 +118,8 @@ const Template = observer(() => {
         defaultMessage: '操作',
       }),
       valueType: 'option',
-      align: 'center',
+      align: 'left',
       width: 200,
-      fixed: 'right',
       render: (text, record) => [
         <PermissionButton
           key="edit"
@@ -229,7 +226,6 @@ const Template = observer(() => {
         rowKey="id"
         search={false}
         params={param}
-        scroll={{ x: 1366 }}
         columns={columns}
         headerTitle={
           <Space>

+ 1 - 1
src/pages/rule-engine/Alarm/Config/index.less

@@ -1,5 +1,5 @@
 .doc {
-  height: 750px;
+  height: 100%;
   padding: 24px;
   overflow-y: auto;
   color: rgba(#000, 0.8);

+ 2 - 1
src/pages/rule-engine/Alarm/Configuration/index.tsx

@@ -99,8 +99,9 @@ const Configuration = () => {
     {
       title: '操作',
       valueType: 'option',
-      align: 'center',
+      align: 'left',
       fixed: 'right',
+      width: 150,
       render: (_, record) => [
         record.sceneTriggerType === 'manual' && (
           <PermissionButton

+ 9 - 1
src/pages/rule-engine/Alarm/Log/Detail/Info.tsx

@@ -2,6 +2,7 @@ import { Descriptions, Modal } from 'antd';
 import { useEffect, useState } from 'react';
 import moment from 'moment';
 import { Store } from 'jetlinks-store';
+import styles from './index.less';
 
 interface Props {
   data: Partial<AlarmLogHistoryItem>;
@@ -16,7 +17,14 @@ const Info = (props: Props) => {
   }, [props.data]);
 
   return (
-    <Modal title={'详情'} visible onCancel={props.close} onOk={props.close} width={1000}>
+    <Modal
+      title={'详情'}
+      visible
+      onCancel={props.close}
+      onOk={props.close}
+      width={1000}
+      className={styles.conent}
+    >
       <Descriptions bordered column={2}>
         {data.targetType === 'device' && (
           <>

+ 7 - 0
src/pages/rule-engine/Alarm/Log/Detail/index.less

@@ -0,0 +1,7 @@
+.conent {
+  :global {
+    .ant-descriptions-bordered .ant-descriptions-item-label {
+      min-width: 180px;
+    }
+  }
+}

+ 1 - 0
src/pages/rule-engine/DashBoard/index.tsx

@@ -336,6 +336,7 @@ const Dashboard = observer(() => {
         <DashBoard
           title="告警统计"
           height={600}
+          showTimeTool={true}
           options={options}
           onParamsChange={getEcharts}
           ref={alarmCountRef}

+ 4 - 2
src/pages/system/Basis/index.tsx

@@ -10,6 +10,7 @@ import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
 import styles from './index.less';
 import { PageContainer } from '@ant-design/pro-layout';
 import Service from './service';
+import { useDomFullHeight } from '@/hooks';
 
 const Basis = () => {
   const service = new Service();
@@ -18,6 +19,7 @@ const Basis = () => {
   const { permission: userPermission } = usePermissions('system/Basis');
   const [imageUrl, setImageUrl] = useState<string>('');
   const [loading, setLoading] = useState(false);
+  const { minHeight } = useDomFullHeight(`.basis`);
 
   const uploadProps: UploadProps = {
     showUploadList: false,
@@ -108,7 +110,7 @@ const Basis = () => {
 
   return (
     <PageContainer>
-      <Card>
+      <Card className="basis" style={{ minHeight }}>
         <div
           style={{
             display: 'flex',
@@ -121,7 +123,7 @@ const Basis = () => {
               <Form.Item
                 label="系统名称"
                 name="title"
-                rules={[{ required: true, message: '请输入系统名称' }]}
+                // rules={[{ required: true, message: '请输入系统名称' }]}
               >
                 <Input />
               </Form.Item>

+ 10 - 1
src/utils/util.ts

@@ -2,7 +2,7 @@ import moment from 'moment';
 import type { Field, FieldDataSource } from '@formily/core';
 import { action } from '@formily/reactive';
 import Token from '@/utils/token';
-
+import { message } from 'antd';
 /**
  * 下载文件
  * @param url 下载链接
@@ -118,3 +118,12 @@ export const getDomFullHeight = (className: string, extraHeight: number = 0): nu
   }
   return 0;
 };
+export const onlyMessage = (
+  msg: string,
+  type: 'success' | 'error' | 'warning' = 'success',
+  key: number = 1,
+) =>
+  message[type]({
+    content: msg,
+    key: key,
+  });