소스 검색

fix(merge): merge wzy

lind 3 년 전
부모
커밋
b2e66db587
28개의 변경된 파일432개의 추가작업 그리고 353개의 파일을 삭제
  1. 1 1
      src/components/ProTableCard/CardItems/aliyun.tsx
  2. 1 1
      src/components/ProTableCard/CardItems/cascade.tsx
  3. 9 8
      src/components/ProTableCard/CardItems/duerOs.tsx
  4. 4 0
      src/global.less
  5. 1 1
      src/locales/zh-CN/pages.ts
  6. 1 1
      src/pages/Northbound/AliCloud/index.tsx
  7. 4 0
      src/pages/cloud/DuerOS/typings.d.ts
  8. 5 54
      src/pages/device/Instance/Detail/Functions/index.tsx
  9. 75 73
      src/pages/device/Instance/Detail/MetadataMap/index.tsx
  10. 7 13
      src/pages/device/Instance/Detail/Running/index.tsx
  11. 12 8
      src/pages/device/Instance/Detail/index.tsx
  12. 2 1
      src/pages/device/Instance/index.tsx
  13. 54 0
      src/pages/device/components/Empty/index.tsx
  14. 3 3
      src/pages/link/Protocol/index.tsx
  15. 4 6
      src/pages/link/Protocol/save/index.tsx
  16. 2 2
      src/pages/media/Cascade/index.tsx
  17. 0 4
      src/pages/media/Device/Channel/Tree/index.less
  18. 2 0
      src/pages/media/Device/Channel/Tree/index.tsx
  19. 39 2
      src/pages/media/Device/Channel/index.less
  20. 31 17
      src/pages/media/Device/Channel/index.tsx
  21. 31 22
      src/pages/notice/Template/Debug/index.tsx
  22. 17 0
      src/pages/rule-engine/Alarm/Configuration/index.tsx
  23. 8 0
      src/pages/rule-engine/Alarm/Log/TabComponent/index.less
  24. 12 2
      src/pages/rule-engine/Alarm/Log/TabComponent/index.tsx
  25. 72 113
      src/pages/rule-engine/Scene/Save/action/VariableItems/user.tsx
  26. 31 17
      src/pages/system/DataSource/Management/EditTable.tsx
  27. 2 2
      src/pages/system/Permission/index.tsx
  28. 2 2
      src/pages/system/User/index.tsx

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

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

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

@@ -22,7 +22,7 @@ export default (props: CascadeCardProps) => {
       status={props.status.value}
       statusText={props.status.text}
       statusNames={{
-        enabled: StatusColorEnum.processing,
+        enabled: StatusColorEnum.success,
         disabled: StatusColorEnum.error,
       }}
     >

+ 9 - 8
src/components/ProTableCard/CardItems/duerOs.tsx

@@ -4,6 +4,7 @@ import '@/style/common.less';
 import '../index.less';
 import { Tooltip } from 'antd';
 import { DuerOSItem } from '@/pages/cloud/DuerOS/typings';
+import { StatusColorEnum } from '@/components/BadgeStatus';
 
 export interface DuerOSProps extends DuerOSItem {
   detail?: React.ReactNode;
@@ -17,14 +18,14 @@ export default (props: DuerOSProps) => {
   return (
     <TableCard
       actions={props.action}
-      // detail={props.detail}
-      showStatus={false}
-      // status={props.state?.value}
-      // statusText={props.state?.text}
-      // statusNames={{
-      //   enabled: StatusColorEnum.success,
-      //   disabled: StatusColorEnum.error,
-      // }}
+      detail={props.detail}
+      // showStatus={false}
+      status={props?.state?.value}
+      statusText={props?.state?.text}
+      statusNames={{
+        enabled: StatusColorEnum.success,
+        disabled: StatusColorEnum.error,
+      }}
       showMask={false}
     >
       <div className={'pro-table-card-item'}>

+ 4 - 0
src/global.less

@@ -126,4 +126,8 @@ ol {
 
 .ellipsis-70 {
   width: 70%;
+  overflow: hidden;
+  white-space: nowrap;
+  text-align: left;
+  text-overflow: ellipsis;
 }

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

@@ -257,7 +257,7 @@ export default {
   // 设备管理-设备
   'pages.device.instance': '设备',
   'pages.device.instance.registrationTime': '注册时间',
-  'pages.device.instance.status.notActive': '未启用',
+  'pages.device.instance.status.notActive': '用',
   'pages.device.instance.status.offLine': '离线',
   'pages.device.instance.status.onLine': '在线',
   'pages.device.instance.deleteTip': '已启用的设备无法删除',

+ 1 - 1
src/pages/Northbound/AliCloud/index.tsx

@@ -156,7 +156,7 @@ const AliCloud = () => {
       valueType: 'select',
       valueEnum: {
         disabled: {
-          text: '用',
+          text: '用',
           status: 'disabled',
         },
         enabled: {

+ 4 - 0
src/pages/cloud/DuerOS/typings.d.ts

@@ -36,6 +36,10 @@ type DuerOSItem = {
     text: string;
     value: string;
   };
+  state?: {
+    text: string;
+    value: string;
+  };
   actionMappings: ActionMapping[];
   propertyMappings: PropertyMapping[];
 } & BaseItem;

+ 5 - 54
src/pages/device/Instance/Detail/Functions/index.tsx

@@ -1,67 +1,16 @@
-import { Button, Card, Tabs } from 'antd';
+import { 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, PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
-import { getMenuPathByParams } from '@/utils/menu';
-import useHistory from '@/hooks/route/useHistory';
+import Empty from '@/pages/device/components/Empty';
 
 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 }}>
@@ -93,7 +42,9 @@ const Functions = () => {
           </Tabs.TabPane>
         </Tabs>
       ) : (
-        <div style={{ height: minHeight - 150 }}>{empty()}</div>
+        <div style={{ height: minHeight - 150 }}>
+          <Empty />
+        </div>
       )}
     </Card>
   );

+ 75 - 73
src/pages/device/Instance/Detail/MetadataMap/index.tsx

@@ -1,11 +1,11 @@
-import { Card } from 'antd';
+import { Button, Card, Empty } from 'antd';
 import { useEffect, useState } from 'react';
-import { service } from '@/pages/device/Instance';
+import { InstanceModel, 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, Empty } from '@/components';
+import { PermissionButton } from '@/components';
 import { useDomFullHeight } from '@/hooks';
 
 interface Props {
@@ -55,92 +55,94 @@ const MetadataMap = (props: Props) => {
     const dmetadata = JSON.parse(data?.metadata || '{}');
     const height = minHeight - 150;
     if (product) {
+      // 是否有物模型属性
       const flag =
         (type === 'device' &&
           (metadata?.properties || []).length === 0 &&
           (dmetadata?.properties || []).length === 0) ||
         (type === 'product' && (dmetadata?.properties || []).length === 0);
-      if (!product.accessId && flag) {
+
+      const isIndependent = InstanceModel.detail?.independentMetadata;
+      let description = undefined;
+
+      if (type === 'device' && isIndependent && flag) {
+        description = (
+          <span>
+            暂无数据,请配置
+            <Button
+              style={{ margin: 0, padding: '0 4px' }}
+              type={'link'}
+              onClick={() => {
+                InstanceModel.active = 'metadata';
+              }}
+            >
+              物模型属性
+            </Button>
+          </span>
+        );
+      } else if (!product.accessId && flag) {
         if (!permission.update) {
-          return (
-            <div style={{ height }}>
-              <Empty
-                description={<span>请联系管理员配置物模型属性,并选择对应产品的设备接入方式</span>}
-              />
-            </div>
-          );
+          description = <span>请联系管理员配置物模型属性,并选择对应产品的设备接入方式</span>;
         } else {
-          return (
-            <div style={{ height }}>
-              <Empty
-                description={
-                  <span>
-                    请先配置对应产品的
-                    <a
-                      onClick={() => {
-                        checkUrl('metadata');
-                      }}
-                    >
-                      物模型属性
-                    </a>
-                    ,并选择对应产品的
-                    <a
-                      onClick={() => {
-                        checkUrl('access');
-                      }}
-                    >
-                      设备接入方式
-                    </a>
-                  </span>
-                }
-              />
-            </div>
+          description = (
+            <span>
+              请先配置对应产品的
+              <a
+                onClick={() => {
+                  checkUrl('metadata');
+                }}
+              >
+                物模型属性
+              </a>
+              ,并选择对应产品的
+              <a
+                onClick={() => {
+                  checkUrl('access');
+                }}
+              >
+                设备接入方式
+              </a>
+            </span>
           );
         }
       } else if (flag && product.accessId) {
-        return (
-          <div style={{ height }}>
-            <Empty
-              description={
-                !permission.update ? (
-                  <span>请联系管理员配置物模型属性</span>
-                ) : (
-                  <span>
-                    请配置对应产品的
-                    <a
-                      onClick={() => {
-                        checkUrl('metadata');
-                      }}
-                    >
-                      物模型属性
-                    </a>
-                  </span>
-                )
-              }
-            />
-          </div>
+        description = !permission.update ? (
+          <span>请联系管理员配置物模型属性</span>
+        ) : (
+          <span>
+            请配置对应产品的
+            <a
+              onClick={() => {
+                checkUrl('metadata');
+              }}
+            >
+              物模型属性
+            </a>
+          </span>
         );
       } else if (!flag && !product.accessId) {
+        description = (
+          <span>
+            请选择对应产品的
+            <a
+              onClick={() => {
+                checkUrl('access');
+              }}
+            >
+              设备接入方式
+            </a>
+          </span>
+        );
+      }
+
+      if (!description) {
+        return <EditableTable data={data} type={type} />;
+      } else {
         return (
           <div style={{ height }}>
-            <Empty
-              description={
-                <span>
-                  请选择对应产品的
-                  <a
-                    onClick={() => {
-                      checkUrl('access');
-                    }}
-                  >
-                    设备接入方式
-                  </a>
-                </span>
-              }
-            />
+            <Empty description={description} />
           </div>
         );
-      } else {
-        return <EditableTable data={data} type={type} />;
       }
     }
     return (

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

@@ -1,13 +1,16 @@
 import { InstanceModel } from '@/pages/device/Instance';
-import { Card, Empty, Input, Tabs } from 'antd';
+import { Card, Input, Tabs } from 'antd';
 import type { DeviceMetadata } from '@/pages/device/Product/typings';
 import Property from '@/pages/device/Instance/Detail/Running/Property';
 import Event from '@/pages/device/Instance/Detail/Running/Event';
 import { useEffect, useState } from 'react';
+import Empty from '@/pages/device/components/Empty';
+import { useDomFullHeight } from '@/hooks';
 
 const Running = () => {
   const metadata = JSON.parse((InstanceModel.detail?.metadata || '{}') as string) as DeviceMetadata;
   const [list, setList] = useState<any[]>([]);
+  const { minHeight } = useDomFullHeight(`.device-detail-running`);
 
   useEffect(() => {
     setList(metadata?.events || []);
@@ -32,27 +35,18 @@ const Running = () => {
   );
 
   return (
-    <Card>
+    <Card className={'device-detail-running'} style={{ minHeight }}>
       {list?.length === 0 && (metadata?.properties || [])?.length === 0 ? (
         <div
           style={{
-            height: 480,
-            display: 'flex',
-            alignItems: 'center',
-            width: '100%',
-            justifyContent: 'center',
+            height: minHeight - 150,
           }}
         >
           <Empty />
         </div>
       ) : (
         <div className="tabs-full-active">
-          <Tabs
-            defaultActiveKey="1"
-            tabPosition="left"
-            style={{ minHeight: 600 }}
-            tabBarExtraContent={{ left: operations() }}
-          >
+          <Tabs defaultActiveKey="1" tabPosition="left" tabBarExtraContent={{ left: operations() }}>
             <Tabs.TabPane tab="属性" key="1">
               <Property data={metadata?.properties || []} />
             </Tabs.TabPane>

+ 12 - 8
src/pages/device/Instance/Detail/index.tsx

@@ -36,7 +36,7 @@ deviceStatus.set('notActive', <Badge status="processing" text={'未启用'} />);
 
 const InstanceDetail = observer(() => {
   const intl = useIntl();
-  const [tab, setTab] = useState<string>('detail');
+  // const [tab, setTab] = useState<string>('detail');
   const params = useParams<{ id: string }>();
   const service = new Service('device-instance');
   const { permission } = PermissionButton.usePermission('device/Instance');
@@ -225,7 +225,8 @@ const InstanceDetail = observer(() => {
     if (!InstanceModel.current && !params.id) {
       history.goBack();
     } else {
-      setTab('detail');
+      // setTab('detail');
+      InstanceModel.active = 'detail';
       getDetail(params?.id || InstanceModel.current?.id || '');
     }
     return () => {
@@ -234,9 +235,9 @@ const InstanceDetail = observer(() => {
   }, [params.id]);
 
   useEffect(() => {
-    console.log(location.query);
     if ((location as any).query?.key) {
-      setTab((location as any).query?.key || 'detail');
+      // setTab((location as any).query?.key || 'detail');
+      InstanceModel.active = (location as any).query?.key || 'detail';
     }
     const subscription = Store.subscribe(SystemConst.BASE_UPDATE_DATA, (data) => {
       if ((window as any).onTabSaveSuccess) {
@@ -250,7 +251,8 @@ const InstanceDetail = observer(() => {
   useEffect(() => {
     const { state } = location;
     if (state && state?.tab) {
-      setTab(state?.tab);
+      // setTab(state?.tab);
+      InstanceModel.active = state?.tab;
     }
   }, [location]);
 
@@ -258,9 +260,11 @@ const InstanceDetail = observer(() => {
     <PageContainer
       className={'page-title-show'}
       onBack={history.goBack}
-      onTabChange={setTab}
+      onTabChange={(e) => {
+        InstanceModel.active = e;
+      }}
       tabList={list}
-      tabActiveKey={tab}
+      tabActiveKey={InstanceModel.active}
       content={
         <Descriptions size="small" column={4}>
           <Descriptions.Item label={'ID'}>{InstanceModel.detail?.id}</Descriptions.Item>
@@ -363,7 +367,7 @@ const InstanceDetail = observer(() => {
       //   </Button>,
       // ]}
     >
-      {list.find((k) => k.key === tab)?.component}
+      {list.find((k) => k.key === InstanceModel.active)?.component}
     </PageContainer>
   );
 });

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

@@ -55,6 +55,7 @@ export const InstanceModel = model<{
   detail: {},
   config: {},
   metadataItem: {},
+  active: 'detail',
   params: new Set<string>(['test']),
 });
 export const service = new Service('device-instance');
@@ -266,7 +267,7 @@ const Instance = () => {
         notActive: {
           text: intl.formatMessage({
             id: 'pages.device.instance.status.notActive',
-            defaultMessage: '未启用',
+            defaultMessage: '用',
           }),
           status: 'notActive',
         },

+ 54 - 0
src/pages/device/components/Empty/index.tsx

@@ -0,0 +1,54 @@
+import { InstanceModel } from '@/pages/device/Instance';
+import { getMenuPathByParams } from '@/utils/menu';
+import { Button } from 'antd';
+import { Empty, PermissionButton } from '@/components';
+import useHistory from '@/hooks/route/useHistory';
+
+export default () => {
+  const isIndependent = InstanceModel.detail?.independentMetadata;
+  const path = getMenuPathByParams('device/Product/Detail', InstanceModel.detail?.productId);
+  const { permission } = PermissionButton.usePermission('device/Product');
+  const history = useHistory();
+
+  let description = <></>;
+
+  if (!isIndependent) {
+    if (!permission.update) {
+      description = <span>请联系管理员配置物模型属性</span>;
+    } else {
+      description = (
+        <span>
+          暂无数据, 请前往产品配置
+          <Button
+            style={{ margin: 0, padding: '0 4px' }}
+            type={'link'}
+            onClick={() => {
+              history.push(`${path}?key=metadata`);
+            }}
+          >
+            物模型
+          </Button>
+        </span>
+      );
+    }
+  } else {
+    // 物模型解绑
+    description = (
+      <span>
+        暂无数据,请配置
+        <Button
+          style={{ margin: 0, padding: '0 4px' }}
+          type={'link'}
+          onClick={() => {
+            InstanceModel.active = 'metadata';
+            // history.push(`${path}?key=metadata`);
+          }}
+        >
+          物模型
+        </Button>
+      </span>
+    );
+  }
+
+  return <Empty description={description} />;
+};

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

@@ -261,10 +261,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');
@@ -275,7 +275,7 @@ const Protocol = () => {
                 }}
               >
                 {record.state === 1 ? <StopOutlined /> : <PlayCircleOutlined />}
-                {record.state === 1 ? '撤销' : '发布'}
+                {record.state === 1 ? '禁用' : '启用'}
               </PermissionButton>,
               <PermissionButton
                 isPermission={permission.delete}

+ 4 - 6
src/pages/link/Protocol/save/index.tsx

@@ -8,9 +8,9 @@ import type { ISchema } from '@formily/json-schema';
 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 { PermissionButton, RadioCard } from '@/components';
 import { onlyMessage } from '@/utils/util';
+
 interface Props {
   data: ProtocolItem | undefined;
   close: () => void;
@@ -138,7 +138,6 @@ const Save = (props: Props) => {
             'x-disabled': !!props.data?.id,
             'x-decorator-props': {
               gridSpan: 2,
-              tooltip: <div>jar:上传协议jar包,文件格式支持.jar或.zip</div>,
             },
             'x-component-props': {
               model: 'singular',
@@ -179,9 +178,6 @@ const Save = (props: Props) => {
                 'x-visible': false,
                 'x-decorator-props': {
                   gridSpan: 2,
-                  tooltip: (
-                    <div>local:填写本地协议编译目录绝对地址,如:d:/protocol/target/classes</div>
-                  ),
                 },
                 'x-validator': [
                   {
@@ -197,6 +193,8 @@ const Save = (props: Props) => {
                       componentType: '{{$deps[0]==="jar"?"FileUpload":"Input"}}',
                       componentProps:
                         '{{$deps[0]==="jar"?{type:"file", accept: ".jar, .zip"}:{placeholder: "请输入文件地址"}}}',
+                      decoratorProps:
+                        '{{$deps[0]!=="jar"?{gridSpan:"2",tooltip:"local:填写本地协议编译目录绝对地址,如:d:/protocol/target/classes"}:{gridSpan:"2",tooltip:"jar:上传协议jar包,文件格式支持.jar或.zip"}}}',
                     },
                   },
                 },

+ 2 - 2
src/pages/media/Cascade/index.tsx

@@ -169,11 +169,11 @@ const Cascade = () => {
       valueType: 'select',
       valueEnum: {
         disabled: {
-          text: '已停止',
+          text: '禁用',
           status: 'disabled',
         },
         enabled: {
-          text: '已启动',
+          text: '正常',
           status: 'enabled',
         },
       },

+ 0 - 4
src/pages/media/Device/Channel/Tree/index.less

@@ -1,9 +1,5 @@
 .channel-tree {
   height: 100%;
-  margin-right: 16px;
-  padding: 20px;
-  background-color: #fff;
-  border-radius: 2px;
 
   .channel-tree-query {
     margin-bottom: 16px;

+ 2 - 0
src/pages/media/Device/Channel/Tree/index.tsx

@@ -9,6 +9,7 @@ import { debounce } from 'lodash';
 interface TreeProps {
   deviceId: string;
   onSelect: (id: React.Key) => void;
+  onTreeLoad: (type: boolean) => void;
 }
 
 export default (props: TreeProps) => {
@@ -19,6 +20,7 @@ export default (props: TreeProps) => {
     formatResult: (res) => res.result,
     onSuccess: (res) => {
       treeData[0].children = res.result || [];
+      props.onTreeLoad(treeData[0].children.length > 10);
       setTreeData(treeData);
     },
   });

+ 39 - 2
src/pages/media/Device/Channel/index.less

@@ -1,8 +1,45 @@
 .device-channel-warp {
   display: flex;
 
-  .left {
-    width: 300px;
+  .left-warp {
+    position: relative;
+    margin-right: 16px;
+    padding: 20px;
+    background-color: #fff;
+    border-radius: 2px;
+
+    .left-content {
+      width: 0;
+      height: 100%;
+      overflow: hidden;
+
+      &.active {
+        width: 260px;
+      }
+    }
+
+    .left-warp--btn {
+      position: absolute;
+      top: 50%;
+      right: 0;
+      padding: 20px 4px;
+      color: rgba(#000, 0.3);
+      background-color: rgba(#f0f0f0, 6);
+      border-radius: ~'100% 0 0 100% / 50% 0 0 50%';
+      cursor: pointer;
+
+      &:hover {
+        color: rgba(#000, 0.5);
+        background-color: rgba(#f0f0f0, 8);
+      }
+
+      &.active {
+        right: 50%;
+        background-color: transparent;
+        border-radius: 0;
+        transform: translateX(50%) rotate(180deg);
+      }
+    }
   }
 
   .right {

+ 31 - 17
src/pages/media/Device/Channel/index.tsx

@@ -12,6 +12,7 @@ import { Button, message, Popconfirm, Tooltip } from 'antd';
 import {
   DeleteOutlined,
   EditOutlined,
+  LeftOutlined,
   PlusOutlined,
   VideoCameraAddOutlined,
   VideoCameraOutlined,
@@ -23,6 +24,7 @@ import Live from './Live';
 import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import Tree from './Tree';
 import { useDomFullHeight } from '@/hooks';
+import classnames from 'classnames';
 
 export const service = new Service('media');
 
@@ -37,6 +39,7 @@ export default () => {
   const [channelId, setChannelId] = useState('');
   const [type, setType] = useState('');
   const { minHeight } = useDomFullHeight(`.channelDevice`, 24);
+  const [show, setShow] = useState(false);
 
   const location = useLocation();
   const history = useHistory();
@@ -211,24 +214,35 @@ export default () => {
     <PageContainer>
       <div className={'device-channel-warp'}>
         {type === ProviderValue.GB281 && (
-          <div className={'left'}>
-            <Tree
-              deviceId={deviceId}
-              onSelect={(key) => {
-                if (key === deviceId && actionRef.current?.reset) {
-                  actionRef.current?.reset();
-                } else {
-                  setQueryParam({
-                    terms: [
-                      {
-                        column: 'parentId',
-                        value: key,
-                      },
-                    ],
-                  });
-                }
+          <div className={classnames('left-warp')}>
+            <div className={classnames('left-content', { active: show })}>
+              <Tree
+                deviceId={deviceId}
+                onSelect={(key) => {
+                  if (key === deviceId && actionRef.current?.reset) {
+                    actionRef.current?.reset();
+                  } else {
+                    setQueryParam({
+                      terms: [
+                        {
+                          column: 'parentId',
+                          value: key,
+                        },
+                      ],
+                    });
+                  }
+                }}
+                onTreeLoad={setShow}
+              />
+            </div>
+            <div
+              className={classnames('left-warp--btn', { active: !show })}
+              onClick={() => {
+                setShow(!show);
               }}
-            />
+            >
+              <LeftOutlined />
+            </div>
           </div>
         )}
         <div className={'right'}>

+ 31 - 22
src/pages/notice/Template/Debug/index.tsx

@@ -102,7 +102,6 @@ const Debug = observer(() => {
     // 从后端接口来获取变量参数
     service.getVariableDefinitions(state.current?.id || '').then((resp) => {
       const _template = resp.result;
-      console.log(resp, 'userEfffect', state.current);
       if (_template?.variableDefinitions?.length > 0) {
         variableRef.current = _template?.variableDefinitions;
         form.setFieldState('variableDefinitions', (state1) => {
@@ -215,6 +214,7 @@ const Debug = observer(() => {
               'x-component-props': { title: '值', width: '120px' },
               properties: {
                 value: {
+                  required: true,
                   type: 'string',
                   'x-decorator': 'FormItem',
                   'x-component': 'Input',
@@ -236,27 +236,36 @@ const Debug = observer(() => {
   const [spinning, setSpinning] = useState<boolean>(false);
   const start = async () => {
     setSpinning(true);
-    const data: { configId: string; variableDefinitions: any } = await form.submit();
-    // 应该取选择的配置信息
-    if (!state.current) return;
-    const resp = await service.debug(
-      data.configId,
-      state?.current.id,
-      data.variableDefinitions?.reduce(
-        (previousValue: any, currentValue: { id: any; value: any }) => {
-          return {
-            ...previousValue,
-            [currentValue.id]: currentValue.value,
-          };
-        },
-        {},
-      ),
-    );
-    if (resp.status === 200) {
-      onlyMessage('操作成功!');
-      setSpinning(false);
-      state.debug = false;
-    }
+    // const data: { configId: string; variableDefinitions: any } = await form.submit();
+    form
+      .submit()
+      .then(async (data: any) => {
+        // 应该取选择的配置信息
+        if (!state.current) return;
+        const resp = await service.debug(
+          data.configId,
+          state?.current.id,
+          data.variableDefinitions?.reduce(
+            (previousValue: any, currentValue: { id: any; value: any }) => {
+              return {
+                ...previousValue,
+                [currentValue.id]: currentValue.value,
+              };
+            },
+            {},
+          ),
+        );
+        if (resp.status === 200) {
+          onlyMessage('操作成功!');
+          setSpinning(false);
+          state.debug = false;
+        } else {
+          setSpinning(false);
+        }
+      })
+      .catch(() => {
+        setSpinning(false);
+      });
   };
   return (
     <Modal

+ 17 - 0
src/pages/rule-engine/Alarm/Configuration/index.tsx

@@ -105,9 +105,26 @@ const Configuration = () => {
     {
       title: '状态',
       dataIndex: 'state',
+      valueType: 'select',
       renderText: (state) => (
         <Badge text={state?.text} status={state?.value === 'disabled' ? 'error' : 'success'} />
       ),
+      valueEnum: {
+        disabled: {
+          text: intl.formatMessage({
+            id: 'pages.device.product.status.disabled',
+            defaultMessage: '禁用',
+          }),
+          status: 'disabled',
+        },
+        enabled: {
+          text: intl.formatMessage({
+            id: 'pages.device.product.status.enabled',
+            defaultMessage: '正常',
+          }),
+          status: 'enabled',
+        },
+      },
     },
     {
       title: '说明',

+ 8 - 0
src/pages/rule-engine/Alarm/Log/TabComponent/index.less

@@ -69,5 +69,13 @@
 
   .card-state-content {
     transform: skewX(-45deg);
+
+    .stateText {
+      width: 70px;
+      overflow: hidden;
+      white-space: nowrap;
+      text-align: center;
+      text-overflow: ellipsis;
+    }
   }
 }

+ 12 - 2
src/pages/rule-engine/Alarm/Log/TabComponent/index.tsx

@@ -239,8 +239,18 @@ const TabComponent = observer((props: Props) => {
                         style={{ backgroundColor: colorMap.get(item.level) }}
                       >
                         <div className={'card-state-content'}>
-                          {AlarmLogModel.defaultLevel.find((i) => i.level === item.level)?.title ||
-                            item.level}
+                          <Tooltip
+                            placement="topLeft"
+                            title={
+                              AlarmLogModel.defaultLevel.find((i) => i.level === item.level)
+                                ?.title || item.level
+                            }
+                          >
+                            <div className={'stateText'}>
+                              {AlarmLogModel.defaultLevel.find((i) => i.level === item.level)
+                                ?.title || item.level}
+                            </div>
+                          </Tooltip>
                         </div>
                       </div>
                     </div>

+ 72 - 113
src/pages/rule-engine/Scene/Save/action/VariableItems/user.tsx

@@ -1,13 +1,14 @@
 // 收信人
 import { useEffect, useState } from 'react';
 import { ItemGroup } from '@/pages/rule-engine/Scene/Save/components';
-import { Input, Select } from 'antd';
+import { Input, Select, TreeSelect } from 'antd';
 import {
   queryDingTalkUsers,
   queryPlatformUsers,
   queryRelationUsers,
   queryWechatUsers,
 } from '@/pages/rule-engine/Scene/Save/action/service';
+import { defer, filter, forkJoin, from, map } from 'rxjs';
 
 type ChangeType = {
   source?: string;
@@ -26,8 +27,11 @@ interface UserProps {
 export default (props: UserProps) => {
   const [source, setSource] = useState(props.value?.source);
   const [value, setValue] = useState<string | undefined>();
-  const [userList, setUserList] = useState({ platform: [], relation: [] });
   const [relationList, setRelationList] = useState([]);
+  const [treeData, setTreeData] = useState([
+    { name: '平台用户', id: 'p1', selectable: false, children: [] },
+    { name: '关系用户', id: 'p2', selectable: false, children: [] },
+  ]);
 
   useEffect(() => {
     setSource(props.value?.source);
@@ -48,31 +52,26 @@ export default (props: UserProps) => {
   }, [props.value]);
 
   const getPlatformUser = async () => {
-    const _userList: any = {
-      platform: [],
-      relation: [],
-    };
-    const resp1 = await queryPlatformUsers();
-    if (resp1.status === 200) {
-      _userList.platform = resp1.result.map((item: any) => ({
-        label: item.name,
-        value: item.id,
-        username: item.username,
-      }));
-    }
-
-    const resp2 = await queryRelationUsers();
-    if (resp2.status === 200) {
-      _userList.relation = resp2.result.map((item: any) => ({
-        label: item.name,
-        value: item.relation,
-        username: '',
-      }));
-    }
-
-    setUserList(_userList);
+    forkJoin(
+      defer(() => from(queryPlatformUsers())).pipe(
+        filter((item) => item.status === 200),
+        map((resp) => resp.result),
+      ),
+      defer(() => from(queryRelationUsers())).pipe(
+        filter((item) => item.status === 200),
+        map((resp) => resp.result),
+      ),
+    ).subscribe((res) => {
+      const newTree = [...treeData];
+      res.forEach((item, index) => {
+        newTree[index].children = item;
+      });
+      setTreeData(newTree);
+    });
   };
 
+  console.log('treeData', treeData);
+
   const getRelationUsers = async (notifyType: string, configId: string) => {
     if (notifyType === 'dingTalk') {
       const resp = await queryDingTalkUsers(configId);
@@ -170,55 +169,56 @@ export default (props: UserProps) => {
     } else {
       obj.value = _value;
     }
-    console.log(obj);
+
     if (props.onChange) {
       props.onChange(obj);
     }
   };
 
   const filterOption = (input: string, option: any) => {
-    return option.label ? option.label.toLowerCase().includes(input.toLowerCase()) : false;
+    return option.name ? option.name.toLowerCase().includes(input.toLowerCase()) : false;
+  };
+
+  const createTreeNode = (data: any): React.ReactNode => {
+    return data.map((item: any) => {
+      if (item.children) {
+        return (
+          <TreeSelect.TreeNode value={item.id} title={item.name} selectable={false}>
+            {createTreeNode(item.children)}
+          </TreeSelect.TreeNode>
+        );
+      } else {
+        return (
+          <TreeSelect.TreeNode
+            name={item.name}
+            value={item.id}
+            title={
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>{item.name}</span>
+                <span style={{ color: '#cfcfcf' }}>{item.username}</span>
+              </div>
+            }
+          />
+        );
+      }
+    });
   };
 
   const userSelect =
     source === 'relation' ? (
-      <Select
+      <TreeSelect
         showSearch
         value={value}
-        onChange={(key, node) => {
+        dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+        placeholder={'请选择收信人'}
+        onSelect={(key: any, node: any) => {
           setValue(key);
           onchange(source, key, node.isRelation);
         }}
-        placeholder={'请选择收信人'}
-        listHeight={200}
-        filterOption={filterOption}
-        optionLabelProp="label"
+        filterTreeNode={filterOption}
       >
-        {userList.platform.length ? (
-          <Select.OptGroup label={'平台用户'}>
-            {userList.platform.map((item: any) => (
-              <Select.Option value={item.value} isRelation={false} label={item.label}>
-                <div style={{ display: 'flex', justifyContent: 'space-between' }}>
-                  <span>{item.label}</span>
-                  <span style={{ color: '#cfcfcf' }}>{item.username}</span>
-                </div>
-              </Select.Option>
-            ))}
-          </Select.OptGroup>
-        ) : null}
-        {userList.relation.length ? (
-          <Select.OptGroup label={'关系用户'}>
-            {userList.relation.map((item: any) => (
-              <Select.Option value={item.value} isRelation={false} label={item.label}>
-                <div style={{ display: 'flex', justifyContent: 'space-between' }}>
-                  <span>{item.label}</span>
-                  <span style={{ color: '#cfcfcf' }}>{item.username}</span>
-                </div>
-              </Select.Option>
-            ))}
-          </Select.OptGroup>
-        ) : null}
-      </Select>
+        {createTreeNode(treeData)}
+      </TreeSelect>
     ) : (
       <Select
         showSearch
@@ -239,43 +239,19 @@ export default (props: UserProps) => {
 
   const emailSelect =
     source === 'relation' ? (
-      <Select
+      <TreeSelect
         showSearch
         value={value}
-        onChange={(key, node) => {
+        dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+        placeholder={'请选择收信人'}
+        onSelect={(key: any, node: any) => {
           setValue(key);
           onchange(source, key, node.isRelation);
         }}
-        placeholder={'请选择收信人'}
-        listHeight={200}
-        filterOption={filterOption}
-        optionLabelProp="label"
+        filterTreeNode={filterOption}
       >
-        {userList.platform.length ? (
-          <Select.OptGroup label={'平台用户'}>
-            {userList.platform.map((item: any) => (
-              <Select.Option value={item.value} isRelation={false} label={item.label}>
-                <div style={{ display: 'flex', justifyContent: 'space-between' }}>
-                  <span>{item.label}</span>
-                  <span style={{ color: '#cfcfcf' }}>{item.username}</span>
-                </div>
-              </Select.Option>
-            ))}
-          </Select.OptGroup>
-        ) : null}
-        {userList.relation.length ? (
-          <Select.OptGroup label={'关系用户'}>
-            {userList.relation.map((item: any) => (
-              <Select.Option value={item.value} isRelation={true} label={item.label}>
-                <div style={{ display: 'flex', justifyContent: 'space-between' }}>
-                  <span>{item.label}</span>
-                  <span style={{ color: '#cfcfcf' }}>{item.username}</span>
-                </div>
-              </Select.Option>
-            ))}
-          </Select.OptGroup>
-        ) : null}
-      </Select>
+        {createTreeNode(treeData)}
+      </TreeSelect>
     ) : (
       <Input
         value={value}
@@ -288,36 +264,19 @@ export default (props: UserProps) => {
 
   const voiceSelect =
     source === 'relation' ? (
-      <Select
+      <TreeSelect
         showSearch
         value={value}
-        onChange={(key, node) => {
+        dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+        placeholder={'请选择收信人'}
+        onSelect={(key: any, node: any) => {
           setValue(key);
           onchange(source, key, node.isRelation);
         }}
-        placeholder={'请选择收信人'}
-        listHeight={200}
-        filterOption={filterOption}
+        filterTreeNode={filterOption}
       >
-        {userList.platform.length ? (
-          <Select.OptGroup label={'平台用户'}>
-            {userList.platform.map((item: any) => (
-              <Select.Option value={item.value} isRelation={false}>
-                {item.label}
-              </Select.Option>
-            ))}
-          </Select.OptGroup>
-        ) : null}
-        {userList.relation.length ? (
-          <Select.OptGroup label={'关系用户'}>
-            {userList.relation.map((item: any) => (
-              <Select.Option value={item.value} isRelation={true}>
-                {item.label}
-              </Select.Option>
-            ))}
-          </Select.OptGroup>
-        ) : null}
-      </Select>
+        {createTreeNode(treeData)}
+      </TreeSelect>
     ) : (
       <Input
         value={value}

+ 31 - 17
src/pages/system/DataSource/Management/EditTable.tsx

@@ -1,8 +1,8 @@
+import { randomString } from '@/utils/util';
 import { ArrayTable, Editable, Form, FormItem, Input, NumberPicker, Radio } from '@formily/antd';
 import { createForm } from '@formily/core';
 import { createSchemaField } from '@formily/react';
 import { Button } from 'antd';
-import { useEffect } from 'react';
 import RemoveData from './RemoveData';
 
 interface Props {
@@ -15,6 +15,7 @@ interface Props {
 }
 
 const EditTable = (props: Props) => {
+  const _data = (props?.data || []).map((item) => ({ ...item, old_id: randomString() })) || [];
   const SchemaField = createSchemaField({
     components: {
       FormItem,
@@ -29,7 +30,7 @@ const EditTable = (props: Props) => {
 
   const form = createForm({
     initialValues: {
-      array: props.data,
+      array: _data,
     },
   });
 
@@ -52,6 +53,12 @@ const EditTable = (props: Props) => {
               'x-component': 'ArrayTable.Column',
               'x-component-props': { title: '列名' },
               properties: {
+                old_id: {
+                  type: 'string',
+                  'x-decorator': 'FormItem',
+                  'x-component': 'Input',
+                  'x-hidden': true,
+                },
                 name: {
                   type: 'string',
                   'x-decorator': 'FormItem',
@@ -71,6 +78,13 @@ const EditTable = (props: Props) => {
                     },
                   ],
                   required: true,
+                  'x-reactions': (field: any) => {
+                    const old_id = field.query('.old_id').take().value;
+                    const flag = _data.find((item: any) => {
+                      return old_id && item?.old_id && item?.old_id === old_id;
+                    });
+                    field.disabled = !!flag;
+                  },
                 },
               },
             },
@@ -114,16 +128,16 @@ const EditTable = (props: Props) => {
                     placeholder: '请输入长度',
                   },
                   'x-validator': [
-                    {
-                      required: true,
-                      message: '请输入长度',
-                    },
+                    // {
+                    //   required: true,
+                    //   message: '请输入长度',
+                    // },
                     {
                       maximum: 99999,
                       minimum: 1,
                     },
                   ],
-                  required: true,
+                  // required: true,
                 },
               },
             },
@@ -140,16 +154,16 @@ const EditTable = (props: Props) => {
                     placeholder: '请输入精度',
                   },
                   'x-validator': [
-                    {
-                      required: true,
-                      message: '请输入精度',
-                    },
+                    // {
+                    //   required: true,
+                    //   message: '请输入精度',
+                    // },
                     {
                       maximum: 99999,
                       minimum: 0,
                     },
                   ],
-                  required: true,
+                  // required: true,
                 },
               },
             },
@@ -239,10 +253,6 @@ const EditTable = (props: Props) => {
     },
   };
 
-  useEffect(() => {
-    form.setValues({ array: props?.data || [] });
-  }, [props.data]);
-
   return (
     <div>
       <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
@@ -251,7 +261,11 @@ const EditTable = (props: Props) => {
           style={{ marginBottom: 20 }}
           onClick={async () => {
             const data: any = await form.submit();
-            props.onChange(data);
+            const list = (data?.array || []).map((i: any) => {
+              const { old_id, ...extra } = i;
+              return { ...extra };
+            });
+            props.onChange({ array: list });
           }}
         >
           保存

+ 2 - 2
src/pages/system/Permission/index.tsx

@@ -1,11 +1,11 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import React, { useRef, useState } from 'react';
 import {
-  CloseCircleOutlined,
   DeleteOutlined,
   EditOutlined,
   PlayCircleOutlined,
   PlusOutlined,
+  StopOutlined,
 } from '@ant-design/icons';
 import { Badge, Button, Dropdown, Menu, Popconfirm, Space, Tooltip, Upload } from 'antd';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
@@ -217,7 +217,7 @@ const Permission: React.FC = observer(() => {
             }),
           }}
         >
-          {record.status ? <CloseCircleOutlined /> : <PlayCircleOutlined />}
+          {record.status ? <StopOutlined /> : <PlayCircleOutlined />}
         </PermissionButton>,
         <PermissionButton
           type={'link'}

+ 2 - 2
src/pages/system/User/index.tsx

@@ -5,12 +5,12 @@ import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import { Badge, Popconfirm } from 'antd';
 import {
-  CloseCircleOutlined,
   DeleteOutlined,
   EditOutlined,
   PlayCircleOutlined,
   PlusOutlined,
   SafetyOutlined,
+  StopOutlined,
 } from '@ant-design/icons';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { useRef, useState } from 'react';
@@ -177,7 +177,7 @@ const User = observer(() => {
             }),
           }}
         >
-          {record.status ? <CloseCircleOutlined /> : <PlayCircleOutlined />}
+          {record.status ? <StopOutlined /> : <PlayCircleOutlined />}
         </PermissionButton>,
         <PermissionButton
           type="link"