Pārlūkot izejas kodu

fix: 通知配置-同步用户

sun-chaochao 3 gadi atpakaļ
vecāks
revīzija
fee80089d8

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

@@ -74,6 +74,7 @@ const Status = observer((props: Props) => {
   const productPermission = PermissionButton.usePermission('device/Product').permission;
   const networkPermission = PermissionButton.usePermission('link/Type').permission;
   const devicePermission = PermissionButton.usePermission('device/Instance').permission;
+
   const [diagnoseVisible, setDiagnoseVisible] = useState<boolean>(false);
   const [artificialVisible, setArtificialVisible] = useState<boolean>(false);
   const [diagnoseData, setDiagnoseData] = useState<any>({});
@@ -676,15 +677,17 @@ const Status = observer((props: Props) => {
   };
 
   useEffect(() => {
-    if (!props.flag) {
-      handleSearch();
-    } else {
-      const dt = Store.get('diagnose-status');
-      DiagnoseStatusModel.status = dt?.status;
-      DiagnoseStatusModel.list = dt?.list || [];
-      props.onChange('success');
+    if (devicePermission.view) {
+      if (!props.flag) {
+        handleSearch();
+      } else {
+        const dt = Store.get('diagnose-status');
+        DiagnoseStatusModel.status = dt?.status;
+        DiagnoseStatusModel.list = dt?.list || [];
+        props.onChange('success');
+      }
     }
-  }, []);
+  }, [devicePermission]);
 
   return (
     <Row gutter={24}>

+ 13 - 4
src/pages/device/components/Metadata/Base/Edit/index.tsx

@@ -26,7 +26,7 @@ import {
   FileTypeList,
   PropertySource,
 } from '@/pages/device/data';
-import { useMemo } from 'react';
+import { useMemo, useState } from 'react';
 import { productModel } from '@/pages/device/Product';
 import { service } from '@/pages/device/components/Metadata';
 import { Store } from 'jetlinks-store';
@@ -53,6 +53,7 @@ interface Props {
 
 const Edit = observer((props: Props) => {
   const intl = useIntl();
+  const [loading, setLoading] = useState<boolean>(false);
   const form = useMemo(
     () =>
       createForm({
@@ -975,6 +976,7 @@ const Edit = observer((props: Props) => {
   const { type } = MetadataModel;
 
   const saveMetadata = async (deploy?: boolean) => {
+    setLoading(true);
     const params = (await form.submit()) as MetadataItem;
 
     if (!typeMap.get(props.type)) return;
@@ -1016,7 +1018,6 @@ const Edit = observer((props: Props) => {
     // const result = await saveMap.get(props.type);
     const result = await asyncUpdateMedata(props.type, _data);
     if (result.status === 200) {
-      message.success('操作成功!');
       if ((window as any).onTabSaveSuccess) {
         if (result) {
           (window as any).onTabSaveSuccess(result);
@@ -1026,6 +1027,8 @@ const Edit = observer((props: Props) => {
         Store.set(SystemConst.REFRESH_METADATA_TABLE, true);
         if (deploy) {
           Store.set('product-deploy', deploy);
+        } else {
+          message.success('操作成功!');
         }
         MetadataModel.edit = false;
         MetadataModel.item = {};
@@ -1033,6 +1036,7 @@ const Edit = observer((props: Props) => {
     } else {
       message.error('操作失败!');
     }
+    setLoading(false);
   };
 
   const menu = (
@@ -1064,11 +1068,16 @@ const Edit = observer((props: Props) => {
         placement={'right'}
         extra={
           props.type === 'product' ? (
-            <Dropdown.Button type="primary" onClick={() => saveMetadata()} overlay={menu}>
+            <Dropdown.Button
+              loading={loading}
+              type="primary"
+              onClick={() => saveMetadata()}
+              overlay={menu}
+            >
               保存数据
             </Dropdown.Button>
           ) : (
-            <Button type="primary" onClick={() => saveMetadata()}>
+            <Button loading={loading} type="primary" onClick={() => saveMetadata()}>
               保存数据
             </Button>
           )

+ 111 - 0
src/pages/notice/Config/BindUser/index.tsx

@@ -0,0 +1,111 @@
+import { createForm } from '@formily/core';
+import { createSchemaField } from '@formily/react';
+import { Select, FormItem, Form } from '@formily/antd';
+import { message, Modal } from 'antd';
+import { service, state } from '..';
+import { useEffect, useState } from 'react';
+
+interface Props {
+  close: () => void;
+  data: any;
+  id: string;
+  reload: () => void;
+}
+
+const BindUser = (props: Props) => {
+  const form = createForm({
+    validateFirst: true,
+    initialValues: { user: props.data?.userId || '' },
+  });
+  const [list, setList] = useState<any[]>([]);
+
+  const getUsers = async (id: string) => {
+    const resp = await service.syncUser.noBindUser({
+      pagign: false,
+      terms: [
+        {
+          column: `id$user-third${state.current?.type}_${state.current?.provider}$not`,
+          value: id,
+        },
+      ],
+    });
+    const data = resp.result?.map((item: Record<string, unknown>) => ({
+      label: item.name,
+      value: item.id,
+    }));
+    setList(data);
+  };
+
+  useEffect(() => {
+    if (props.data?.id) {
+      getUsers(props.data.id);
+    }
+  }, [props.data]);
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      Select,
+    },
+  });
+
+  const schema = {
+    type: 'object',
+    properties: {
+      user: {
+        type: 'string',
+        title: '用户',
+        'x-decorator': 'FormItem',
+        'x-component': 'Select',
+        'x-component-props': {
+          placeholder: '请选择用户',
+          filterOption: (input: string, option: any) =>
+            option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
+        },
+        enum: [...list],
+        'x-validator': [
+          {
+            required: true,
+            message: '请选择用户',
+          },
+        ],
+      },
+    },
+  };
+
+  return (
+    <Modal
+      title="绑定用户 "
+      onCancel={() => {
+        props.close();
+      }}
+      visible
+      width={500}
+      onOk={async () => {
+        const values: any = (await form.submit()) as any;
+        const resp = await service.syncUser.bindUser(
+          props.id,
+          state.current?.provider || '',
+          state.current?.id || '',
+          [
+            {
+              userId: values.user,
+              providerName: props.data?.name,
+              thirdPartyUserId: props.data?.id,
+            },
+          ],
+        );
+        if (resp.status === 200) {
+          message.success('操作成功!');
+          props.reload();
+        }
+      }}
+    >
+      <Form form={form} layout="vertical">
+        <SchemaField schema={schema} />
+      </Form>
+    </Modal>
+  );
+};
+
+export default BindUser;

+ 87 - 67
src/pages/notice/Config/SyncUser/index.tsx

@@ -1,52 +1,90 @@
-import { Button, Col, Input, Modal, Row, Tree } from 'antd';
+import { Badge, Button, Col, Input, message, Modal, Popconfirm, Row, Tooltip, Tree } from 'antd';
 import { observer } from '@formily/react';
 import { service, state } from '..';
-import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
+import type { ActionType, ProColumns } from '@jetlinks/pro-table';
+import ProTable from '@jetlinks/pro-table';
 import { useEffect, useRef, useState } from 'react';
 import { history, useLocation } from 'umi';
-import { PermissionButton } from '@/components';
 import { DisconnectOutlined, EditOutlined } from '@ant-design/icons';
+import BindUser from '../BindUser';
 
 const SyncUser = observer(() => {
   const [dept, setDept] = useState<string>();
   const location = useLocation<{ id: string }>();
   const id = (location as any).query?.id;
+  const [visible, setVisible] = useState<boolean>(false);
+  const [current, setCurrent] = useState<any>({});
 
   const idMap = {
     dingTalk: '钉钉',
     weixin: '微信',
   };
+
+  const actionRef = useRef<ActionType>();
+
   const columns: ProColumns<any>[] = [
     {
       dataIndex: 'id',
-      title: `${idMap[id]}ID`,
+      title: `${idMap[id]}用户名`,
+      render: (text: any, record: any) => (
+        <span>
+          {text}({record?.name})
+        </span>
+      ),
     },
     {
-      dataIndex: 'name',
-      title: `${idMap[id]}用户名`,
+      dataIndex: 'userId',
+      title: `用户`,
+      render: (text: any, record: any) => (
+        <span>{record?.userId ? `${record?.username}(${record?.userName})` : '--'}</span>
+      ),
     },
     {
-      dataIndex: 'action',
+      dataIndex: 'status',
       title: '绑定状态',
-      render: () => [
-        <PermissionButton
-          tooltip={{
-            title: '绑定用户',
-          }}
-        >
-          <EditOutlined />
-        </PermissionButton>,
-        <PermissionButton
-          tooltip={{
-            title: '解绑用户',
-          }}
-        >
-          <DisconnectOutlined />
-        </PermissionButton>,
+      render: (text: any, record: any) => (
+        <Badge
+          status={record?.userId ? 'success' : 'error'}
+          text={record?.userId ? '已绑定' : '未绑定'}
+        />
+      ),
+    },
+    {
+      dataIndex: 'action',
+      title: '操作',
+      render: (text: any, record: any) => [
+        <Tooltip title={'绑定用户'} key="bind">
+          <Button
+            type="link"
+            onClick={() => {
+              setCurrent(record);
+              setVisible(true);
+            }}
+          >
+            <EditOutlined />
+          </Button>
+        </Tooltip>,
+        <Tooltip title={'解绑用户'} key="unbind">
+          <Button type="link">
+            <Popconfirm
+              title={'确认解绑'}
+              onConfirm={async () => {
+                if (record?.bindingId) {
+                  const resp = await service.syncUser.unBindUser(record.bindingId);
+                  if (resp.status === 200) {
+                    message.success('操作成功!');
+                    actionRef.current?.reload();
+                  }
+                }
+              }}
+            >
+              <DisconnectOutlined />
+            </Popconfirm>
+          </Button>
+        </Tooltip>,
       ],
     },
   ];
-  const actionRef = useRef<ActionType>();
 
   const [treeData, setTreeData] = useState([]);
 
@@ -60,7 +98,7 @@ const SyncUser = observer(() => {
           if (resp.status === 200) {
             setTreeData(resp.result);
             setDept(resp.result[0].id);
-            console.log(resp.result[0].id, 'id');
+            // console.log(resp.result[0].id, 'id');
           }
         });
       } else if (id === 'weixin') {
@@ -68,7 +106,7 @@ const SyncUser = observer(() => {
           if (resp.status === 200) {
             setTreeData(resp.result);
             setDept(resp.result[0].id);
-            console.log(resp.result[0].id, 'id~~');
+            // console.log(resp.result[0].id, 'id~~');
           }
         });
       }
@@ -82,40 +120,6 @@ const SyncUser = observer(() => {
     getDepartment();
   }, [id]);
 
-  // const updateTreeData = (list: any[], key: React.Key, children: any[]): any[] => {
-  //   return list.map((node) => {
-  //     if (node.id === key) {
-  //       return {
-  //         ...node,
-  //         children: node.children ? [...node.children, ...children] : children,
-  //       };
-  //     }
-  //
-  //     if (node.children) {
-  //       return {
-  //         ...node,
-  //         children: updateTreeData(node.children, key, children),
-  //       };
-  //     }
-  //     return node;
-  //   });
-  // };
-
-  // const getParentKey = (key: any, tree: string | any[]): any => {
-  //   let parentKey;
-  //   for (let i = 0; i < tree.length; i++) {
-  //     const node = tree[i];
-  //     if (node.children) {
-  //       if (node.children.some((item: { key: any; }) => item.key === key)) {
-  //         parentKey = node.key;
-  //       } else if (getParentKey(key, node.children)) {
-  //         parentKey = getParentKey(key, node.children);
-  //       }
-  //     }
-  //   }
-  //   return parentKey;
-  // };
-
   return (
     <Modal
       title="同步用户"
@@ -137,7 +141,6 @@ const SyncUser = observer(() => {
                 setDept(key[0] as string);
               }}
               treeData={treeData}
-              // loadData={onLoadData}
             />
           </div>
         </Col>
@@ -149,26 +152,28 @@ const SyncUser = observer(() => {
               search={false}
               columns={columns}
               params={{ dept: dept }}
-              request={async (params) =>
-                service.syncUser
-                  .getDeptUser(
+              request={(params) =>
+                service
+                  .queryZipSyncUser(
                     {
                       dingTalk: 'dingtalk',
                       weixin: 'wechat',
                     }[id],
+                    id,
+                    state.current?.provider || '',
                     state.current?.id || '',
                     params.dept || '',
                   )
-                  .then((resp) => {
+                  .then((resp: any) => {
                     return {
-                      code: resp.message,
+                      code: resp?.message,
                       result: {
-                        data: resp.result || [],
+                        data: resp?.result || [],
                         pageIndex: 0,
                         pageSize: 0,
                         total: 0,
                       },
-                      status: resp.status,
+                      status: resp?.status,
                     };
                   })
               }
@@ -177,6 +182,21 @@ const SyncUser = observer(() => {
           )}
         </Col>
       </Row>
+      {visible && (
+        <BindUser
+          id={id}
+          close={() => {
+            setCurrent({});
+            setVisible(false);
+          }}
+          data={current}
+          reload={() => {
+            setCurrent({});
+            setVisible(false);
+            actionRef.current?.reload();
+          }}
+        />
+      )}
     </Modal>
   );
 });

+ 48 - 0
src/pages/notice/Config/service.ts

@@ -1,6 +1,7 @@
 import BaseService from '@/utils/BaseService';
 import { request } from 'umi';
 import SystemConst from '@/utils/const';
+import { from, map, zip } from 'rxjs';
 
 class Service extends BaseService<ConfigItem> {
   public getTypes = () =>
@@ -64,6 +65,10 @@ class Service extends BaseService<ConfigItem> {
         method: 'PATCH',
         data,
       }),
+    bindUserThirdParty: (type: string, provider: string, configId: string) =>
+      request(`${SystemConst.API_BASE}/user/third-party/${type}_${provider}/${configId}`, {
+        method: 'GET',
+      }),
     getUserBindInfo: () =>
       request(`${SystemConst.API_BASE}/user/third-party/me`, { method: 'GET' }),
     unBindUser: (bindId: string) =>
@@ -71,6 +76,49 @@ class Service extends BaseService<ConfigItem> {
         method: 'DELETE',
       }),
   };
+
+  public queryZipSyncUser = (
+    type: 'wechat' | 'dingTalk',
+    _type: string,
+    provider: string,
+    configId: string,
+    departmentId: string,
+  ) =>
+    new Promise((resolve) => {
+      zip(
+        from(this.syncUser.getDeptUser(type, configId, departmentId)),
+        from(this.syncUser.bindUserThirdParty(_type, provider, configId)),
+        from(this.syncUser.noBindUser({ paging: false })),
+      )
+        .pipe(map((resp) => resp.map((item) => item.result)))
+        .subscribe((resp) => {
+          const [resp1, resp2, resp3] = resp;
+          const list = resp1.map((item: { id: string; name: string }) => {
+            const data =
+              resp2.find(
+                (i: { userId: string; providerName: string; thirdPartyUserId: string }) =>
+                  i.thirdPartyUserId === item.id,
+              ) || {};
+            let _user: Partial<UserItem> = {};
+            if (data) {
+              _user = resp3.find((i: UserItem) => i.id === data.userId);
+            }
+            return {
+              ..._user,
+              ...data,
+              ...item,
+              bindingId: data?.id,
+              userId: _user?.id,
+              userName: _user?.name,
+            };
+          });
+          resolve({
+            message: 'success',
+            result: list,
+            status: 200,
+          });
+        });
+    });
 }
 
 export default Service;

+ 1 - 0
src/pages/rule-engine/Alarm/Log/TabComponent/index.tsx

@@ -203,6 +203,7 @@ const TabComponent = observer((props: Props) => {
                               <div className="btn">
                                 <ToolFilled className="icon" />
                                 <div>告警处理</div>
+                                {/* action */}
                               </div>
                             </Button>
                           </div>