umi#router TypeScript Examples

The following examples show how to use umi#router. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: 404.tsx    From jetlinks-ui-antd with MIT License 6 votes vote down vote up
NoFoundPage: React.FC<{}> = () => (
  <Result
    status="404"
    title="404"
    subTitle="Sorry, the page you visited does not exist."
    extra={
      <Button type="primary" onClick={() => router.push('/')}>
        Back Home
      </Button>
    }
  />
)
Example #2
Source File: index.tsx    From jetlinks-ui-antd with MIT License 6 votes vote down vote up
ToolBar: React.FC<Props> = (props) => {
    return (
        <div>
            <Button icon="plus" type="primary" onClick={() => router.push('/device/product/add')}>
                新建
            </Button>
        </div>
    );
}
Example #3
Source File: index.tsx    From jetlinks-ui-antd with MIT License 6 votes vote down vote up
ToolBar: React.FC<Props> = (props) => {

    return (
        <div>
            <Button icon="plus" type="primary" onClick={() => router.push('/device/instance/add')}>
                新建
            </Button>
            <Button>
                同步状态
            </Button>
        </div>
    );
}
Example #4
Source File: SecurityLayout.tsx    From jetlinks-ui-antd with MIT License 5 votes vote down vote up
SecurityLayout = (props: SecurityLayoutProps) => {
  const { dispatch, settings } = props;
  const [isReady, setIsReady] = useState(false);
  const { children, loading } = props;
  // You can replace it to your authentication rule (such as check token exists)
  // 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
  const isLogin = !!localStorage.getItem('x-access-token');
  const token = getAccessToken();
  const queryString = stringify({
    redirect: window.location.href,
  });
  useEffect(() => {
    setIsReady(true);
    if (dispatch) {
      if (token !== 'null') {
        dispatch({
          type: 'user/fetchCurrent',
        });
      }else{
          router.push('/user/login');
      }
    }
  }, []);

  /**
   * constructor
   */
  useEffect(() => {
    if (dispatch) {
      dispatch({
        type: 'settings/fetchConfig',
        callback: () => {
          if (settings.titleIcon) {
            document.getElementById('title-icon')!.href = settings.titleIcon;
          }
          setIsReady(true);
        },
      });
    }
  }, [settings.title]);

  const render = () => {
    if (isLogin) {
      initWebSocket();
    }

    if ((!isLogin && loading) || !isReady) {
      return <PageLoading />;
    }
    if (!isLogin) {
      // TODO 此处应使用注释的代码。但跳转存在问题,
      // return <Redirect to={`/user/login?${queryString}`} />;
      return <Redirect to="/user/login"></Redirect>;
    }
    return children;
  };
  return render();
}
Example #5
Source File: user.ts    From jetlinks-ui-antd with MIT License 5 votes vote down vote up
UserModel: UserModelType = {
  namespace: 'user',

  state: {
    currentUser: {},
  },

  effects: {
    *fetch(_, { call, put }) {
      const response = yield call(queryUsers);
      yield put({
        type: 'save',
        payload: response,
      });
    },
    *fetchCurrent(_, { call, put }) {
      const response = yield call(queryCurrent);
      if (response) {
        // 取出login 后缓存的数据
        const loginStorage = localStorage.getItem('hsweb-autz');
        if (loginStorage) {
          const tempLogin = JSON.parse(loginStorage);
          tempLogin.permissions = response.result.permissions;
          const autz = JSON.stringify(tempLogin);
          autz && localStorage.setItem('hsweb-autz', autz);
        } else {
          const autz = JSON.stringify(response.result);
          autz && localStorage.setItem('hsweb-autz', autz);
        }
        yield put({
          type: 'saveCurrentUser',
          payload: response.result.user,
        });
      } else {
        router.push('/user/login');
      }
      reloadAuthorized();
    },
  },

  reducers: {
    saveCurrentUser(state, action) {
      return {
        ...state,
        // currentUser: action.payload || {},
        currentUser: {
          ...state?.currentUser,
          ...action.payload
        }
      };
    },
    changeNotifyCount(
      state = {
        currentUser: {},
      },
      action,
    ) {
      return {
        ...state,
        currentUser: {
          ...state.currentUser,
          notifyCount: action.payload.totalCount,
          unreadCount: action.payload.unreadCount,
        },
      };
    },
  },
}
Example #6
Source File: NoticeIconView.tsx    From jetlinks-ui-antd with MIT License 5 votes vote down vote up
render() {
    const { currentUser, fetchingNotices, onNoticeVisibleChange } = this.props;
    const noticeData = this.getNoticeData();
    const unreadMsg = this.getUnreadData(noticeData);
    return (
      <NoticeIcon
        className={styles.action}
        count={currentUser && currentUser.unreadCount}
        onItemClick={item => {
          this.changeReadState(item as NoticeItem);
        }}
        loading={fetchingNotices}
        clearText="当前标记为已读"
        viewMoreText="查看更多"
        onClear={this.handleNoticeClear}
        onPopupVisibleChange={onNoticeVisibleChange}
        onViewMore={() => router.push('/account/notification')}
        clearClose
      >
        {/* <NoticeIcon.Tab
          tabKey="notification"
          count={unreadMsg.notification}
          list={noticeData.notification}
          title="通知"
          emptyText="你已查看所有通知"
          showViewMore
        /> */}

        <NoticeIcon.Tab
          tabKey="read"
          count={unreadMsg.unread}
          list={noticeData.unread}
          title="未读消息"
          emptyText="您已读完所有消息"
          showViewMore
        />
        <NoticeIcon.Tab
          tabKey="handle"
          title="待办消息"
          emptyText="暂无消息"
          count={unreadMsg.handle}
          list={noticeData.handle}
          showViewMore
        />

      </NoticeIcon>
    );
  }
Example #7
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Device = (props: Props) => {
  const [visible, setVisible] = useState<boolean>(false);
  const data = useContext(TenantContext);
  const service = new Service('tenant');

  const [online, setOnline] = useState(0);
  const [offline, setOffline] = useState(0);
  const getData = () => {
    service.assets.deviceCount(encodeQueryParam({
      terms: {
        id$assets: JSON.stringify({
          tenantId: data?.id,
          assetType: 'device',
          memberId: props.user,
        }),
        state: 'offline'
      }
    })).subscribe(resp => {
      setOffline(resp)
    });
    service.assets.deviceCount(encodeQueryParam({
      terms: {
        id$assets: JSON.stringify({
          tenantId: data?.id,
          assetType: 'device',
          memberId: props.user,
        }),
        state: 'online'
      }
    })).subscribe(resp => {
      setOnline(resp)
    })
  };
  useEffect(() => {
    getData();
  }, [props.user]);

  return (
    <List.Item style={{paddingRight: '10px'}}>
      <Card
        hoverable
        className={styles.card}
        actions={[
          <Tooltip title="查看">
            <Icon type="eye" onClick={() => router.push({
              pathname: `/device/instance`,
              search:'iop='+JSON.stringify({
                terms: {
                  id$assets: {
                    tenantId: data?.id,
                    assetType: 'device',
                    memberId: props.user,
                  }
                }
              }) 
            })}/>
          </Tooltip>,
          <Tooltip title="编辑">
            <Icon type="edit" onClick={() => setVisible(true)}/>
          </Tooltip>]}
      >
        <Card.Meta
          avatar={<IconFont type="icon-device" style={{fontSize: 45}}/>}
          title={<a>设备</a>}
        />
        <div className={styles.cardInfo}>
          <div>
            <p>在线</p>
            <p>{online}</p>
          </div>
          <div>
            <p>离线</p>
            <p>{offline}</p>
          </div>
        </div>
      </Card>
      {visible && (
        <Edit
          user={props.user}
          data={data}
          close={() => {
            setVisible(false);
            getData();
          }}/>
      )}
    </List.Item>
  )
}
Example #8
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
GroupList: React.FC<Props> = props => {
  const service = new Service('device');
  const [loading, setLoading] = useState<boolean>(false);
  const [saveVisible, setSaveVisible] = useState<boolean>(false);
  const [current, setCurrnet] = useState<any>({});
  const [list, setList] = useState<any>({});
  const [add, setAdd] = useState<boolean>(false);
  const [searchParam, setSearchParam] = useState<any>({
    pageSize: 10,
    sorts: {
      order: 'descend',
      field: 'id',
    },
  });
  const search = (params?: any) => {
    setSearchParam(params);
    setLoading(true);
    const defaultTerms = { parentId$isnull: true };
    service
      .groupList(
        encodeQueryParam({
          ...params,
          terms: { ...defaultTerms, ...params?.terms },
        }),
      )
      .subscribe(
        d => setList(d),
        () => message.error('刷新重试'),
        () => setLoading(false),
      );
  };
  useEffect(() => {
    search(searchParam);
  }, []);

  const saveGroup = (item: GroupItem) => {
    if (current.id) {
      //编辑
      service.saveOrUpdataGroup(item).subscribe(
        () => message.success('保存成功'),
        () => {},
        () => {
          setSaveVisible(false);
          search(searchParam);
        },
      );
    } else {
      //新增
      service.saveGroup(item).subscribe(
        () => message.success('保存成功'),
        () => {},
        () => {
          setSaveVisible(false);
          search(searchParam);
        },
      );
    }
  };
  return (
    <PageHeaderWrapper title="设备分组">
      <Card bordered={false} style={{ marginBottom: 10 }}>
        <Row>
          <Button
            type="primary"
            style={{ marginLeft: 8 }}
            onClick={() => {
              setSaveVisible(true);
              setAdd(true);
              setCurrnet({});
            }}
          >
            <Icon type="plus" />
            新建分组
          </Button>
          <span style={{ marginLeft: 20 }}>
            <label>分组名称:</label>
            <Input.Search
              style={{ width: '20%' }}
              placeholder="分组名称"
              onSearch={value => {
                search({
                  terms: {
                    name$LIKE: value,
                  },
                  pageSize: searchParam.pageSize,
                });
              }}
            />
          </span>
        </Row>
      </Card>
      <Card>
        <ProTable
          loading={loading}
          dataSource={list?.data}
          columns={[
            { title: '标识', dataIndex: 'id' },
            { title: '名称', dataIndex: 'name' },
            { title: '描述', dataIndex: 'description' },
            {
              title: '操作',
              render: (_, record: any) => (
                <>
                  <a
                    onClick={() => {
                      router.push({
                        pathname: '/device/tree/detail',
                        query: { id: record.id },
                      });
                    }}
                  >
                    查看
                  </a>
                  <Divider type="vertical" />
                  <a
                    onClick={() => {
                      setCurrnet(record);
                      setAdd(false);
                      setSaveVisible(true);
                    }}
                  >
                    编辑
                  </a>
                  <Divider type="vertical" />
                  <Popconfirm
                    title="确认删除此分组?"
                    onConfirm={() => {
                      service.removeGroup(record.id).subscribe(
                        () => message.success('删除成功!'),
                        () => message.error('删除失败'),
                        () => search(),
                      );
                    }}
                  >
                    <a>删除</a>
                  </Popconfirm>
                </>
              ),
            },
          ]}
          rowKey="id"
          onSearch={(params: any) => {
            search(params);
          }}
          paginationConfig={list}
        />
      </Card>
      {saveVisible && (
        <Save
          flag={add}
          data={current}
          close={() => {
            setSaveVisible(false);
          }}
          save={(item: GroupItem) => saveGroup(item)}
        />
      )}
    </PageHeaderWrapper>
  );
}
Example #9
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
edgeDevice: React.FC<Props> = () => {
  const [result, setResult] = useState({
    data: [],
    pageIndex: 0,
    total: 0,
    pageSize: 0,
  });
  const [loading, setLoading] = useState(false);
  const [searchParam, setSearchParam] = useState({
    pageSize: 8,
    terms: {
      'productId$edge-product': 1,
    },
    sorts: {
      order: 'desc',
      field: 'id',
    },
  });
  const [saveVisible, setSaveVisible] = useState(false);
  const [info, setInfo] = useState({});
  const statusColor = new Map();
  statusColor.set('online', 'green');
  statusColor.set('offline', 'red');
  statusColor.set('notActive', 'blue');

  const handleSearch = (param?: any) => {
    setLoading(true);
    setSearchParam(param);
    apis.edgeDevice.list(encodeQueryParam(param)).then(res => {
      if (res.status === 200) {
        setResult(res.result);
        setLoading(false);
      }
    });
  };

  // const changeDeploy = (record: any) => {
  //     apis.deviceInstance
  //         .changeDeploy(record.id)
  //         .then(response => {
  //             if (response.status === 200) {
  //                 message.success('操作成功');
  //                 deviceIdList.splice(0, deviceIdList.length);
  //                 handleSearch(searchParam);
  //             }
  //         })
  //         .catch(() => {
  //         });
  // };

  // const unDeploy = (record: any) => {
  //     apis.deviceInstance
  //         .unDeploy(record.id)
  //         .then(response => {
  //             if (response.status === 200) {
  //                 message.success('操作成功');
  //                 deviceIdList.splice(0, deviceIdList.length);
  //                 handleSearch(searchParam);
  //             }
  //         })
  //         .catch(() => {
  //         });
  // };

  const onChange = (page: number, pageSize: number) => {
    handleSearch({
      pageIndex: page - 1,
      pageSize,
      terms: searchParam.terms,
    });
  };
  useEffect(() => {
    const query: any = getPageQuery();
    if (query.hasOwnProperty('productId')) {
      handleSearch({
        terms: {
          productId: query.productId,
        },
        pageSize: 10,
      });
    } else {
      handleSearch(searchParam);
    }
  }, []);

  return (
    <PageHeaderWrapper title="设备管理">
      <Card bordered={false}>
        <div className={styles.tableList}>
          <div>
            <SearchForm
              formItems={[
                {
                  label: 'ID',
                  key: 'id',
                  type: 'string',
                },
                {
                  label: '名称',
                  key: 'name$LIKE',
                  type: 'string',
                },
              ]}
              search={(params: any) => {
                setSearchParam(params);
                handleSearch({
                  terms: { ...params, 'productId$edge-product': 1 },
                  sorts: {
                    order: 'desc',
                    field: 'id',
                  },
                  pageSize: 8,
                });
              }}
            />
          </div>
          <div className={styles.tableListOperator}>
            <Button
              icon="plus"
              type="primary"
              onClick={() => {
                setSaveVisible(true);
                setInfo({});
              }}
            >
              新增
            </Button>
          </div>
        </div>
      </Card>
      <br />
      <div>
        {result.data && (
          <List<any>
            rowKey="id"
            loading={loading}
            grid={{ gutter: 24, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }}
            dataSource={(result || {}).data}
            pagination={{
              current: result?.pageIndex + 1,
              total: result?.total,
              pageSize: result?.pageSize,
              showQuickJumper: true,
              showSizeChanger: true,
              hideOnSinglePage: true,
              pageSizeOptions: ['8', '16', '40', '80'],
              style: { marginTop: -20 },
              showTotal: (total: number) =>
                `共 ${total} 条记录 第  ${result.pageIndex + 1}/${Math.ceil(
                  result.total / result.pageSize,
                )}页`,
              onChange,
            }}
            renderItem={item => {
              if (item && item.id) {
                return (
                  <List.Item key={item.id}>
                    <Card
                      hoverable
                      bodyStyle={{ paddingBottom: 20, height: 200 }}
                      actions={[
                        <Tooltip key="seeProduct" title="查看">
                          <Icon
                            type="eye"
                            onClick={() => {
                              router.push(`/edge-gateway/device/detail/${item.id}`);
                            }}
                          />
                        </Tooltip>,
                        <Tooltip key="update" title="编辑">
                          <Icon
                            type="edit"
                            onClick={() => {
                              setSaveVisible(true);
                              setInfo(item);
                            }}
                          />
                        </Tooltip>,
                      ]}
                    >
                      <Card.Meta
                        avatar={<Avatar size={40} src={img} />}
                        title={
                          <AutoHide title={item.name} style={{ width: '95%', fontWeight: 600 }} />
                        }
                        description={
                          <AutoHide
                            title={moment(item.createTime).format('YYYY-MM-DD HH:mm:ss')}
                            style={{ width: '95%' }}
                          />
                        }
                      />
                      <div>
                        <div
                          style={{
                            width: '100%',
                            display: 'flex',
                            marginTop: '10px',
                            justifyContent: 'space-',
                          }}
                        >
                          <div style={{ textAlign: 'center', width: '30%' }}>
                            <p>ID</p>
                            <AutoHide title={item.id} style={{ fontSize: 14, width: '95%' }} />
                          </div>
                          <div style={{ textAlign: 'center', width: '30%' }}>
                            <p>产品名称</p>
                            <p style={{ fontSize: 14 }}>{item.productName}</p>
                          </div>
                          <div style={{ textAlign: 'center', width: '30%' }}>
                            <p>状态</p>
                            <p style={{ fontSize: 14, fontWeight: 600 }}>
                              <Badge
                                color={statusColor.get(item.state?.value)}
                                text={item.state?.text}
                              />
                            </p>
                          </div>
                        </div>
                      </div>
                    </Card>
                  </List.Item>
                );
              }
              return '';
            }}
          />
        )}
      </div>
      {saveVisible && (
        <Save
          close={() => {
            setSaveVisible(false);
          }}
          save={() => {
            handleSearch(searchParam);
            setSaveVisible(false);
          }}
          data={info}
        />
      )}
    </PageHeaderWrapper>
  );
}
Example #10
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
edgeProduct: React.FC<Props> = props => {
    const [result, setResult] = useState({
        data: [],
        pageIndex: 0,
        total: 0,
        pageSize: 0
    });
    const [searchParam, setSearchParam] = useState({ pageSize: 8, terms: {} });
    const [saveVisible, setSaveVisible] = useState(false);
    const [deviceCount, setDeviceCount] = useState({});
    const [info, setInfo] = useState({});

    const cardInfoTitle = {
        fontSize: 14,
        color: 'rgba(0, 0, 0, 0.85)'
    };

    const handleSearch = (params?: any) => {
        setSearchParam(params);
        apis.edgeProduct.list(encodeQueryParam(params)).then(res => {
            if (res.status === 200) {
                setResult(res.result)
            }
        })
    };

    const onChange = (page: number, pageSize: number) => {
        handleSearch({
            pageIndex: page - 1,
            pageSize,
            terms: searchParam.terms
        });
    };

    useEffect(() => {
        handleSearch(searchParam);
    }, []);

    useEffect(() => {
        result.data?.map((item: any) => {
            apis.deviceInstance.count(encodeQueryParam({ terms: { 'productId': item.id } }))
                .then(res => {
                    if (res.status === 200) {
                        deviceCount[item.id] = String(res.result);
                        setDeviceCount({ ...deviceCount });
                    } else {
                        deviceCount[item.id] = '/';
                        setDeviceCount({ ...deviceCount });
                    }
                }).catch();
        });
    }, [result]);

    return (
        <PageHeaderWrapper title="产品管理">
            <Card bordered={false}>
                <div className={styles.tableList}>
                    <div>
                        <SearchForm
                            formItems={[
                                {
                                    label: 'ID',
                                    key: 'id$LIKE',
                                    type: 'string',
                                },
                                {
                                    label: '名称',
                                    key: 'name$LIKE',
                                    type: 'string',
                                }
                            ]}
                            search={(params: any) => {
                                setSearchParam(params);
                                handleSearch({
                                    terms: params,
                                    pageSize: 8
                                });
                            }}
                        />
                    </div>
                    <div className={styles.tableListOperator}>
                        <Button
                            icon="plus"
                            type="primary"
                            onClick={() => {
                                setSaveVisible(true);
                                setInfo({});
                            }}>
                            新增
            </Button>
                    </div>
                </div>
            </Card>
            <br />
            <div className={cardStyles.filterCardList}>
                {result.data && (
                    <List<any>
                        rowKey="id"
                        loading={props.loading}
                        grid={{ gutter: 24, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }}
                        dataSource={(result || {}).data}
                        pagination={{
                            current: result?.pageIndex + 1,
                            total: result?.total,
                            pageSize: result?.pageSize,
                            showQuickJumper: true,
                            showSizeChanger: true,
                            hideOnSinglePage: true,
                            pageSizeOptions: ['8', '16', '40', '80'],
                            style: { marginTop: -20 },
                            showTotal: (total: number) =>
                                `共 ${total} 条记录 第  ${result.pageIndex + 1}/${Math.ceil(
                                    result.total / result.pageSize,
                                )}页`,
                            onChange
                        }}

                        renderItem={item => {
                            if (item && item.id) {
                                return (
                                    <List.Item key={item.id}>
                                        <Card hoverable bodyStyle={{ paddingBottom: 20 }}
                                            actions={[
                                                <Tooltip key="seeProduct" title="查看">
                                                    <Icon
                                                        type="eye"
                                                        onClick={() => {
                                                            router.push(`/device/product/save/${item.id}`);
                                                        }}
                                                    />
                                                </Tooltip>,
                                                <Tooltip key="update" title='编辑'>
                                                    <Icon
                                                        type="edit"
                                                        onClick={() => {
                                                            setSaveVisible(true);
                                                            setInfo(item);
                                                        }}
                                                    />
                                                </Tooltip>,
                                                <Tooltip key="del" title='删除'>
                                                    <Popconfirm
                                                        title="删除此产品?"
                                                        onConfirm={() => {
                                                            apis.edgeProduct.remove(item.id).then(res => {
                                                                if (res.status === 200) {
                                                                    handleSearch(searchParam);
                                                                    message.success('删除成功!');
                                                                }
                                                            })
                                                        }}>
                                                        <Icon type="close" />
                                                    </Popconfirm>
                                                </Tooltip>
                                            ]}
                                        >
                                            <Card.Meta
                                                avatar={<Avatar size={40} src={item.photoUrl || img} />}
                                                title={<AutoHide title={item.name} style={{ width: '95%', fontWeight: 600 }} />}
                                                description={<AutoHide title={item.id} style={{ width: '95%' }} />}
                                            />
                                            <div className={cardStyles.cardItemContent}>
                                                <div className={cardStyles.cardInfo}>
                                                    <div style={{ textAlign: 'center', width: '30%' }}>
                                                        <p style={cardInfoTitle}>厂家</p>
                                                        <p style={{ fontSize: 14, fontWeight: 600 }}>
                                                            <AutoHide title={item.manufacturer} style={{ width: '95%' }} />
                                                        </p>
                                                    </div>
                                                    <div style={{ textAlign: 'center', width: '30%' }}>
                                                        <p style={cardInfoTitle}>型号</p>
                                                        <p style={{ fontSize: 14, fontWeight: 600 }}>
                                                            <AutoHide title={item.model} style={{ width: '95%' }} />
                                                        </p>
                                                    </div>
                                                    <div style={{ textAlign: 'center', width: '30%' }}>
                                                        <p style={cardInfoTitle}>设备数量</p>
                                                        <p style={{ fontSize: 14, fontWeight: 600 }}>
                                                            <Tooltip key="findDevice" title="点击查看设备">
                                                                <a onClick={() => {
                                                                    router.push(`/edge-gateway/device?productId=${item.id}`);
                                                                }}
                                                                >{deviceCount[item.id]}</a>
                                                            </Tooltip>
                                                        </p>
                                                    </div>
                                                </div>
                                            </div>
                                        </Card>
                                    </List.Item>
                                );
                            }
                            return '';
                        }}
                    />
                )}
            </div>
            {
                saveVisible && <Save
                    close={() => {
                        setSaveVisible(false);
                    }}
                    save={() => {
                        handleSearch(searchParam);
                        setSaveVisible(false);
                    }}
                    data={info}
                />
            }
        </PageHeaderWrapper>
    )
}
Example #11
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
MediaDevice: React.FC<Props> = () => {
  const service = new Service('media/device');
  const [loading, setLoading] = useState<boolean>(false);
  const [deviceUpdate, setDeviceUpdate] = useState<boolean>(false);
  const [deviceData, setDeviceData] = useState<any>({});
  const [result, setResult] = useState<any>({});

  const [searchParam, setSearchParam] = useState(initState.searchParam);
  const statusMap = new Map();
  statusMap.set('online', 'success');
  statusMap.set('offline', 'error');
  statusMap.set('notActive', 'processing');

  const streamMode = new Map();
  streamMode.set('UDP', 'UDP');
  streamMode.set('TCP_ACTIVE', 'TCP主动');
  streamMode.set('TCP_PASSIVE', 'TCP被动');

  useEffect(() => {
    handleSearch(searchParam);
  }, []);

  const handleSearch = (params?: any) => {
    setSearchParam(params);
    setLoading(true);
    service.query(encodeQueryParam(params)).subscribe(
      data => setResult(data),
      () => {
      },
      () => setLoading(false),
    );
  };

  const columns: ColumnProps<any>[] = [
    {
      title: '国标设备编号',
      dataIndex: 'id',
      width: 200,
      ellipsis: true,
      fixed: 'left',
    },
    {
      title: '设备名称',
      dataIndex: 'name',
      render: (record: any) => (record ? record : result.id),
      ellipsis: true,
    },
    {
      title: '信令传输',
      dataIndex: 'transport',
      width: 90,
      ellipsis: true,
    },
    {
      title: '流传输模式',
      dataIndex: 'streamMode',
      width: 110,
      render: record => (record ? streamMode.get(record) : '/'),
      ellipsis: true,
    },
    {
      title: '通道数',
      dataIndex: 'channelNumber',
      width: 100,
      ellipsis: true,
    },
    {
      title: '设备状态',
      dataIndex: 'state',
      width: 110,
      render: record =>
        record ? <Badge status={statusMap.get(record.value)} text={record.text}/> : '/',
      filters: [
        {
          text: '未启用',
          value: 'notActive',
        },
        {
          text: '离线',
          value: 'offline',
        },
        {
          text: '在线',
          value: 'online',
        },
      ],
      filterMultiple: false,
    },
    {
      title: '设备IP',
      dataIndex: 'host',
      ellipsis: true,
      width: 150,
    },
    {
      title: '设备端口',
      dataIndex: 'port',
      width: 90,
      ellipsis: true,
    },
    {
      title: '设备厂家',
      dataIndex: 'manufacturer',
      ellipsis: true,
    },
    {
      title: '设备型号',
      dataIndex: 'model',
      ellipsis: true,
    },
    {
      title: '固件版本',
      dataIndex: 'firmware',
      ellipsis: true,
    },
    {
      title: '创建时间',
      dataIndex: 'createTime',
      render: (text: any) => (text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : '/'),
      sorter: true,
      width: 180,
      ellipsis: true,
    },
    {
      title: '操作',
      key: 'center',
      fixed: 'right',
      width: 260,
      render: (record: any) => (
        <Fragment>
          <a
            onClick={() => {
              router.push(`/device/instance/save/${record.id}`);
            }}
          >
            查看
          </a>
          <Divider type="vertical"/>
          <a
            onClick={() => {
              setDeviceData(record);
              setDeviceUpdate(true);
            }}
          >
            编辑
          </a>
          <Divider type="vertical"/>
          <a
            onClick={() => {
              router.push(`/media/device/channel/${record.id}`);
            }}
          >
            查看通道
          </a>
          <Divider type="vertical"/>
          {record.state.value === 'online' ? (
            <a
              onClick={() => {
                setLoading(true);
                service._sync(record.id).subscribe(
                  () => {
                    message.success('通道更新成功');
                  },
                  () => {
                    message.error('通道更新失败');
                  },
                  () => setLoading(false),
                );
              }}
            >
              更新通道
            </a>
          ):(
            <Popconfirm
              placement="topRight"
              title="确定删除此国标设备吗?"
              onConfirm={() => {
                setLoading(true);
                service.remove(record.id).subscribe(
                  () => {
                    message.success('国标设备删除成功');
                  },
                  () => {
                    message.error('国标设备删除失败');
                  },
                  () => {
                    handleSearch(searchParam);
                    setLoading(false);
                  },
                );
              }}
            >
              <a>删除</a>
            </Popconfirm>
          )}
        </Fragment>
      ),
    },
  ];
  return (
    <PageHeaderWrapper title="国标设备">
      <Card style={{marginBottom: 16, height: 92}}>
        <div className={styles.tableList} style={{marginTop: -22}}>
          <div>
            <SearchForm
              search={(params: any) => {
                setSearchParam(params);
                handleSearch({
                  terms: {...params},
                  pageSize: 10,
                  sorts: {field: 'id', order: 'desc'},
                });
              }}
              formItems={[
                {
                  label: '设备名称',
                  key: 'name$LIKE',
                  type: 'string',
                },
              ]}
            />
          </div>
        </div>
      </Card>
      <Card>
        <div className={styles.StandardTable}>
          <ProTable
            loading={loading}
            dataSource={result?.data}
            columns={columns}
            rowKey="id"
            scroll={{x: '150%'}}
            onSearch={(params: any) => {
              params.sorts = params.sorts.field ? params.sorts : {field: 'id', order: 'desc'};
              handleSearch(params);
            }}
            paginationConfig={result}
          />
        </div>
      </Card>
      {deviceUpdate && (
        <DeviceUpdate
          close={() => {
            setDeviceUpdate(false);
            handleSearch(searchParam);
          }}
          data={deviceData}
        />
      )}
    </PageHeaderWrapper>
  );
}
Example #12
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Modbus = () => {

    const [spinning, setSpinning] = useState<boolean>(false);
    const [visible, setVisible] = useState<boolean>(false);
    const [current, setCurrent] = useState<any>({});
    const [searchParams, setSearchParams] = useState<any>({ pageSize: 10, pageIndex: 0 })
    const [dataSource, setDataSource] = useState<any>({
        data: []
    })

    const handleSearch = (params: any) => {
        setSearchParams(params)
        setSpinning(true)
        apis.modbus.getChanelList(params).then(resp => {
            if (resp.status === 200) {
                setDataSource(resp.result)
            }
            setSpinning(false)
        })
    }

    useEffect(() => {
        handleSearch(searchParams)
    }, [])

    const statusMap = new Map();
    statusMap.set('enabled', 'success');
    statusMap.set('disabled', 'error');

    const columns = [
        {
            title: '通道名称',
            dataIndex: 'name',
            key: 'name',
        },
        {
            title: 'IP',
            dataIndex: 'host',
            key: 'host',
        },
        {
            title: '端口',
            dataIndex: 'port',
            key: 'port',
        },
        {
            title: '状态',
            dataIndex: 'state',
            key: 'state',
            render: (record: any) =>
                record ? <Badge status={statusMap.get(record.value)} text={record.text} /> : '',
        },
        {
            title: '操作',
            key: 'action',
            align: 'center',
            render: (text: any, record: any) => (
                <span>
                    <a onClick={() => {
                        setCurrent(record)
                        setVisible(true)
                    }}>编辑</a>
                    <Divider type="vertical" />
                    <a onClick={() => {
                        router.push(`/network/modbus/${record.id}`)
                    }}>设备接入</a>
                    <Divider type="vertical" />
                    <Popconfirm title={`确认${record.state.value === 'enabled' ? '禁用' : '启用'}`}
                        onConfirm={() => {
                            const data = {
                                ...record,
                                state: record.state.value === 'enabled' ? 'disabled' : 'enabled'
                            }
                            apis.modbus.updataChanel(data).then(resp => {
                                if (resp.status === 200) {
                                    message.success('操作成功!')
                                    handleSearch(searchParams)
                                }
                            })
                        }}
                    >
                        <a>{record.state.value === 'enabled' ? '禁用' : '启用'}</a>
                    </Popconfirm>
                    <Divider type="vertical" />
                    <Popconfirm title="确认删除" onConfirm={() => {
                        apis.modbus.removeChanel(record.id).then(resp => {
                            if (resp.status === 200) {
                                message.success('操作成功!')
                                handleSearch(searchParams)
                            }
                        })
                    }}>
                        <a>删除</a>
                    </Popconfirm>
                </span>
            ),
        }
    ];

    const onTableChange = (
        pagination: PaginationConfig
    ) => {
        handleSearch({
            pageIndex: Number(pagination.current) - 1,
            pageSize: pagination.pageSize,
            terms: searchParams?.terms
        });
    };

    return (
        <Spin spinning={spinning}>
            <PageHeaderWrapper title="Modbus">
                <Card bordered={false} style={{ marginBottom: 20 }}>
                    <SearchForm
                        search={(params: any) => {
                            if (params) {
                                const terms: any[] = []
                                Object.keys(params).forEach(key => {
                                    if (params[key]) {
                                        terms.push(
                                            {
                                                "terms": [
                                                    {
                                                        "column": "name",
                                                        "value": `%${params[key]}%`,
                                                        "termType": "like"
                                                    }
                                                ]
                                            }
                                        )
                                    }
                                })
                                setSearchParams({ pageSize: 10, pageIndex: 0, terms })
                                handleSearch({ pageSize: 10, pageIndex: 0, terms })
                            } else {
                                setSearchParams({ pageSize: 10, pageIndex: 0 })
                                handleSearch({ pageSize: 10, pageIndex: 0 })
                            }
                        }}
                        formItems={[{
                            label: '通道名称',
                            key: 'name',
                            type: 'string',
                        }]}
                    />
                </Card>
                <Card bordered={false}>
                    <div style={{ margin: '10px 0' }}><Button type="primary" onClick={() => {
                        setVisible(true)
                        setCurrent({})
                    }}>新增</Button></div>
                    <Table dataSource={dataSource?.data || []} rowKey='id' columns={columns}
                    onChange={onTableChange}
                        pagination={{
                            current: dataSource.pageIndex + 1,
                            total: dataSource.total,
                            pageSize: dataSource.pageSize || 10,
                            showQuickJumper: true,
                            showSizeChanger: true,
                            pageSizeOptions: ['10', '20', '50', '100'],
                            showTotal: (total: number) =>
                                `共 ${total} 条记录 第  ${dataSource.pageIndex + 1}/${Math.ceil(
                                    dataSource.total / dataSource.pageSize,
                                )}页`,
                        }} />
                </Card>
            </PageHeaderWrapper>
            {
                visible && <Save close={() => {
                    setVisible(false)
                    handleSearch(searchParams)
                }} data={current} />
            }
        </Spin>
    )
}
Example #13
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
OrgChart = () => {
    const [list, setList] = useState<any>({});
    const [edit, setEdit] = useState<boolean>(false);
    const [current, setCurrent] = useState<any>({});
    const [autzVisible, setAutzVisible] = useState(false);
    const [userVisible, setUserVisible] = useState(false);
    const [parentId, setParentId] = useState(null);

    const hitCenter = () => {
        const orgChart = document.getElementsByClassName('orgchart-container')[0];
        const {width} = orgChart.getBoundingClientRect();
        orgChart.scrollLeft = width;
    };
    const handleSearch = () => {
        apis.org.list(encodeQueryParam({paging: false, terms: {typeId: 'org'}})).then(resp => {
            const data = {
                id: null,
                name: '机构管理',
                title: '组织架构',
                children: resp.result,
            };
            setList(data);
            hitCenter();
        });
    };
    useEffect(() => {
        handleSearch();
    }, []);

    const saveData = (data: any) => {
        if (data.id) {
            apis.org
                .saveOrUpdate(data)
                .then(res => {
                    if (res.status === 200) {
                        message.success('保存成功');
                    }
                })
                .then(() => {
                    handleSearch();
                });
        } else {
            apis.org
                .add(data)
                .then(res => {
                    if (res.status === 200) {
                        message.success('保存成功');
                    }
                })
                .then(() => {
                    handleSearch();
                });
        }
    };

    const remove = (id: string) => {
        apis.org
            .remove(id)
            .then(resp => {
                if (resp.status === 200) {
                    message.success('操作成功');
                }
            })
            .finally(() => {
                handleSearch();
            });
    };
    const menu = (nodeData: any) => {
        return nodeData.id === null ? (
            <Menu>
                <Menu.Item>
                    <a
                        target="_blank"
                        rel="noopener noreferrer"
                        onClick={() => {
                            setCurrent({});
                            setParentId(nodeData);
                            setEdit(true);
                        }}
                    >
                        添加下级
                    </a>
                </Menu.Item>
            </Menu>
        ) : (
            <Menu>
                <Menu.Item>
                    <a
                        target="_blank"
                        rel="noopener noreferrer"
                        onClick={() => {
                            setParentId(null);
                            setCurrent(nodeData);
                            setEdit(true);
                        }}
                    >
                        编辑
                    </a>
                </Menu.Item>
                <Menu.Item>
                    <a
                        target="_blank"
                        rel="noopener noreferrer"
                        onClick={() => {
                            setCurrent({});
                            setParentId(nodeData);
                            setEdit(true);
                        }}
                    >
                        添加下级
                    </a>
                </Menu.Item>
                <Menu.Item>
                    <a
                        target="_blank"
                        rel="noopener noreferrer"
                        onClick={() => {
                            setCurrent(nodeData);
                            setAutzVisible(true);
                        }}
                    >
                        权限分配
                    </a>
                </Menu.Item>
                <Menu.Item>
                    <a
                        target="_blank"
                        rel="noopener noreferrer"
                        onClick={() => {
                            setCurrent(nodeData);
                            setUserVisible(true);
                        }}
                    >
                        绑定用户
                    </a>
                </Menu.Item>
                <Menu.Item>
                    <a
                        target="_blank"
                        rel="noopener noreferrer"
                        onClick={() => {
                            router.push(`/system/org-chart/assets/${nodeData.id}/org`);
                        }}
                    >
                        资产分配
                    </a>
                </Menu.Item>
                <Menu.Item>
                    <a
                        target="_blank"
                        rel="noopener noreferrer"
                        onClick={() => {
                            remove(nodeData.id);
                        }}
                    >
                        删除
                    </a>
                </Menu.Item>
            </Menu>
        );
    };

    return (
        <PageHeaderWrapper title="机构管理">
            <div className={styles.orgContainer}>
                <OrganizationChart
                    datasource={list}
                    // draggable
                    // onClickNode={(node: any) => {
                    //   message.success(JSON.stringify(node));
                    // }}
                    pan={true}
                    // zoom={true}
                    NodeTemplate={(nodeData: any) => (
                        <NodeTemplate data={nodeData.nodeData} action={menu(nodeData.nodeData)}/>
                    )}
                />
                {edit && (
                    <Save
                        data={current}
                        close={() => {
                            setEdit(false);
                        }}
                        save={(item: any) => {
                            saveData(item);
                            setEdit(false);
                        }}
                        parentId={parentId}
                    />
                )}
                {autzVisible && (
                    <Authorization
                        close={() => {
                            setAutzVisible(false);
                            setCurrent({});
                        }}
                        target={current}
                        targetType="org"
                    />
                )}
                {userVisible && (
                    <BindUser
                        data={current}
                        close={() => {
                            setUserVisible(false);
                        }}
                    />
                )}
            </div>
        </PageHeaderWrapper>
    );
}
Example #14
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Edit = (props: Props) => {
  const service = new Service('tenant');

  const initState: State = {
    list: {},
  };

  const [list, setList] = useState(initState.list);
  const [add, setAdd] = useState<boolean>(false);
  const { data } = props;
  const [cat, setCat] = useState<boolean>(false);
  const [asset, setAsset] = useState();
  const [selected, setSelected] = useState<any[]>([]);
  const [tenant, setTenant] = useState<any[]>([]);

  const initSearch = {
    terms: {
      id$assets: JSON.stringify({
        tenantId: data?.id,
        assetType: 'device',
        memberId: props.user,
        // not: true,
      }),
    },
    pageIndex: 0,
    pageSize: 10,
  };
  const [searchParam, setSearchParam] = useState<any>(initSearch);
  useEffect(() => {
    list.data?.map((item: any) => {
      service.assets.members(data.id, 'device', item.id).subscribe(resp => {
        tenant[item.id] = resp
          .filter((item: any) => item.binding === true)
          .map((i: any) => i.userName)
          .join('、');
        setTenant({ ...tenant });
      });
    });
  }, [list]);

  // let device = (tempSearch: any, datalist: any[]) => {
  //   return new Promise((resolve) => {
  //     service.assets.device(encodeQueryParam(tempSearch)).subscribe(res => {
  //       if (res.data.length > 0) {
  //         res.data.forEach((value: any) => {
  //           service.assets.members(data.id, 'device', value.id).subscribe(resp => {
  //             let str = '';
  //             resp.filter((item: any) => item.binding === true).map((i: any) => i.userName).forEach((i: string) => {
  //               str += i + '、'
  //             });
  //             datalist.push({
  //               id: value.id,
  //               name: value.name,
  //               tenant: str.substring(0, str.length - 1)
  //             });
  //             if (datalist.length == res.data.length) {
  //               resolve({
  //                 pageIndex: res.pageIndex,
  //                 pageSize: res.pageSize,
  //                 total: res.total,
  //                 data: datalist
  //               })
  //             }
  //           });
  //         })
  //       }else {
  //         resolve({
  //           pageIndex: res.pageIndex,
  //           pageSize: res.pageSize,
  //           total: res.total,
  //           data: []
  //         })
  //       }
  //     })
  //   })
  // };

  const handleSearch = (params: any) => {
    const tempParam = { ...searchParam, ...params };
    const defaultItem = searchParam.terms;
    const tempTerms = params?.terms;
    const terms = tempTerms ? { ...defaultItem, ...tempTerms } : initSearch;
    let tempSearch: {};

    if (tempTerms) {
      tempParam.terms = terms;
      tempSearch = tempParam;
    } else {
      tempSearch = initSearch;
    }
    setSearchParam(tempSearch);
    service.assets.device(encodeQueryParam(tempSearch)).subscribe(res => {
      if (res.data.length > 0) {
        let datalist: any = [];
        res.data.forEach((value: any) => {
          datalist.push({
            id: value.id,
            name: value.name,
          });
        });
        setList({
          pageIndex: res.pageIndex,
          pageSize: res.pageSize,
          total: res.total,
          data: datalist,
        });
      } else {
        setList({
          pageIndex: res.pageIndex,
          pageSize: res.pageSize,
          total: res.total,
          data: [],
        });
      }
    });
  };

  useEffect(() => {
    handleSearch(searchParam);
  }, []);
  const rowSelection = {
    onChange: (selectedRowKeys: any[], selectedRows: any[]) => {
      setSelected(selectedRows);
    },
    getCheckboxProps: (record: any) => ({
      name: record.name,
    }),
  };
  const columns = [
    {
      title: 'ID',
      dataIndex: 'id',
      align: 'center',
    },
    {
      title: '名称',
      dataIndex: 'name',
      align: 'center',
    },
    {
      title: '租户名称',
      ellipsis: true,
      align: 'left',
      width: 400,
      render: (record: any) => (
        <div
          style={{ overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}
          onClick={() => {
            setAsset(record);
            setCat(true);
          }}
        >
          <span style={{ color: '#1890ff' }}>{tenant[record.id]}</span>
        </div>
      ),
    },
    {
      title: '操作',
      align: 'center',
      render: (_: string, record: any) => (
        <Fragment>
          <a
            onClick={() => {
              router.push(`/device/instance/save/${record.id}`);
            }}
          >
            查看
          </a>
        </Fragment>
      ),
    },
  ];
  const unbind = () => {
    service.assets
      .unbind(data.id, [
        {
          assetIdList: selected.map(item => item.id),
          assetType: 'device',
          userId: props?.user,
        },
      ])
      .subscribe(() => {
        message.success('解绑成功');
        setSelected([]);
        handleSearch(searchParam);
      });
  };
  return (
    <Drawer title="编辑设备资产" visible width="75VW" onClose={() => props.close()}>
      <SearchForm
        search={(params: any) => {
          handleSearch({ terms: params });
        }}
        formItems={[
          {
            label: 'ID',
            key: 'id$LIKE',
            type: 'string',
          },
          {
            label: '名称',
            key: 'name$LIKE',
            type: 'string',
          },
        ]}
      />
      <Button type="primary" style={{ marginBottom: 10 }} onClick={() => setAdd(true)}>
        添加
      </Button>
      {selected.length > 0 && (
        <Button
          type="danger"
          style={{ marginBottom: 10, marginLeft: 10 }}
          onClick={() => {
            unbind();
          }}
        >
          {`解绑${selected.length}项`}
        </Button>
      )}
      <ProTable
        rowKey="id"
        rowSelection={rowSelection}
        columns={columns}
        dataSource={list?.data || []}
        onSearch={(searchData: any) => handleSearch(searchData)}
        paginationConfig={list || {}}
      />

      <div
        style={{
          position: 'absolute',
          right: 0,
          bottom: 0,
          width: '100%',
          borderTop: '1px solid #e9e9e9',
          padding: '10px 16px',
          background: '#fff',
          textAlign: 'right',
        }}
      >
        <Button
          onClick={() => {
            props.close();
          }}
          style={{ marginRight: 8 }}
        >
          关闭
        </Button>
      </div>
      {add && (
        <Add
          user={props.user}
          data={data}
          close={() => {
            setAdd(false);
            handleSearch(searchParam);
          }}
        />
      )}
      {cat && (
        <User
          asset={asset}
          close={() => {
            setCat(false);
            handleSearch(searchParam);
          }}
        />
      )}
    </Drawer>
  );
}
Example #15
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
DeviceModel: React.FC<Props> = props => {
  const { result } = props.deviceProduct;
  const { dispatch, location } = props;

  const initState: State = {
    data: result,
    searchParam: {
      pageSize: 8,
      terms: location?.query.iop && JSON.parse(location?.query.iop)?.terms,
      sorts: { field: 'id', order: 'desc' },
    },
    saveVisible: false,
  };

  // 消息协议
  const [protocolSupports, setProtocolSupports] = useState([]);
  const [categoryList, setCategoryList] = useState([]);
  const [searchParam, setSearchParam] = useState(initState.searchParam);
  const [saveVisible, setSaveVisible] = useState(initState.saveVisible);
  const [deviceCount, setDeviceCount] = useState({});
  const [spinning, setSpinning] = useState(true);
  const [basicInfo, setBasicInfo] = useState<any>();

  const handleSearch = (params?: any) => {
    setSearchParam(params);
    dispatch({
      type: 'deviceProduct/query',
      payload: encodeQueryParam(params),
    });
  };

  const deploy = (record: any) => {
    dispatch({
      type: 'deviceProduct/deploy',
      payload: record.id,
      callback: response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        }
      },
    });
  };
  const unDeploy = (record: any) => {
    dispatch({
      type: 'deviceProduct/unDeploy',
      payload: record.id,
      callback: response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        }
      },
    });
  };

  const handleDelete = (params: any) => {
    dispatch({
      type: 'deviceProduct/remove',
      payload: params.id,
      callback: response => {
        if (response.status === 200) {
          message.success('删除成功');
          handleSearch(searchParam);
        }
      },
    });
  };

  useEffect(() => {
    apis.deviceProdcut
      .deviceCategory({paging:false})
      .then((response: any) => {
        if (response.status === 200) {
          setCategoryList(
            response.result.map((item: any) => ({
              id: item.id,
              pId: item.parentId,
              value: item.id,
              title: item.name,
            })),
          );
        }
      })
      .catch(() => {});

    apis.deviceProdcut
      .protocolSupport()
      .then(response => {
        if (response.status === 200) {
          setProtocolSupports(response.result.map((i: any) => ({ id: i.id, name: i.name })));
        }
      })
      .catch(() => {});

    handleSearch(searchParam);
  }, []);

  useEffect(() => {
    result.data?.map((item: any) => {
      apis.deviceInstance
        .count(encodeQueryParam({ terms: { productId: item.id } }))
        .then(res => {
          if (res.status === 200) {
            deviceCount[item.id] = String(res.result);
            setDeviceCount({ ...deviceCount });
          } else {
            deviceCount[item.id] = '/';
            setDeviceCount({ ...deviceCount });
          }
        })
        .catch();
    });

    setSpinning(false);
  }, [result]);

  const handleSave = (record: any) => {
    dispatch({
      type: 'deviceProduct/insert',
      payload: record,
      callback: response => {
        if (response.status === 200) {
          setSaveVisible(false);
          message.success('保存成功');
          router.push(`/device/product/save/${record.id}`);
        }
      },
    });
  };

  const onChange = (page: number, pageSize: number) => {
    handleSearch({
      pageIndex: page - 1,
      pageSize,
      terms: searchParam.terms,
      sorts: searchParam.sorts,
    });
  };

  const onShowSizeChange = (current: number, size: number) => {
    handleSearch({
      pageIndex: current - 1,
      pageSize: size,
      terms: searchParam.terms,
      sorts: searchParam.sorts,
    });
  };

  const uploadProps = (item: any) => {
    dispatch({
      type: 'deviceProduct/insert',
      payload: item,
      callback: (response: any) => {
        if (response.status === 200) {
          message.success('导入成功');
          handleSearch(searchParam);
        }
      },
    });
  };
  /*  const uploadProps: UploadProps = {
    accept: '.json',
    action: '/jetlinks/file/static',
    headers: {
      'X-Access-Token': getAccessToken(),
    },
    showUploadList: false,
    onChange(info) {
      if (info.file.status === 'done') {
        const fileUrl = info.file.response.result;
        request(fileUrl, {method: 'GET'}).then(e => {
          if (e || e !== null) {
            dispatch({
              type: 'deviceProduct/insert',
              payload: e,
              callback: (response: any) => {
                if (response.status === 200) {
                  message.success('导入成功');
                  handleSearch(searchParam);
                }
              },
            });
          }
        }).catch(() => {
          message.error('导入配置失败');
        });
      }
    },
  };*/

  const cardInfoTitle = {
    fontSize: 14,
    color: 'rgba(0, 0, 0, 0.85)',
  };

  return (
    <PageHeaderWrapper title="产品管理">
      <Spin spinning={spinning}>
        <Card bordered={false}>
          <div>
            <div>
              <SearchForm
                search={(params: any) => {
                  handleSearch({
                    terms: { ...params },
                    pageSize: 8,
                    sorts: searchParam.sorts,
                  });
                }}
                formItems={[
                  {
                    label: '产品名称',
                    key: 'name$LIKE',
                    type: 'string',
                  },
                  {
                    label: '所属品类',
                    key: 'classifiedId$LIKE',
                    type: 'treeSelect',
                    props: {
                      data: categoryList,
                      dropdownStyle: { maxHeight: 500 },
                      showSearch:true,
                      optionFilterProp:'children'
                    },
                  },
                  {
                    label: '产品类型',
                    key: 'deviceType$IN',
                    type: 'list',
                    props: {
                      data: [
                        { id: 'device', name: '直连设备' },
                        { id: 'childrenDevice', name: '网关子设备' },
                        { id: 'gateway', name: '网关设备' },
                      ],
                      mode: 'tags',
                    },
                  },
                  {
                    label: '消息协议',
                    key: 'messageProtocol$IN',
                    type: 'list',
                    props: {
                      data: protocolSupports,
                      mode: 'tags',
                    },
                  },
                ]}
              />
            </div>
            <div>
              <Button
                icon="plus"
                type="primary"
                onClick={() => {
                  router.push('/device/product/add');
                }}
              >
                新建
              </Button>
              <Divider type="vertical" />

              <Upload
              action="/jetlinks/file/static"
              headers={{
                'X-Access-Token': getAccessToken(),
              }}
                showUploadList={false}
                accept=".json"
                beforeUpload={file => {
                  const reader = new FileReader();
                  reader.readAsText(file);
                  reader.onload = result => {
                    try {
                      uploadProps(JSON.parse(result.target.result));
                    } catch (error) {
                      message.error('文件格式错误');
                    }
                  };
                }}
              >
                <Button>
                  <Icon type="upload" />
                  快速导入
                </Button>
              </Upload>
            </div>
          </div>
        </Card>
        <br />
        <div className={cardStyles.filterCardList}>
          {result.data && (
            <List<any>
              rowKey="id"
              loading={props.loading}
              grid={{ gutter: 24, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }}
              dataSource={(result || {}).data}
              pagination={{
                current: result?.pageIndex + 1,
                total: result?.total,
                pageSize: result?.pageSize,
                showQuickJumper: true,
                showSizeChanger: true,
                hideOnSinglePage: true,
                pageSizeOptions: ['8', '16', '40', '80'],
                style: { marginTop: -20 },
                showTotal: (total: number) =>
                  `共 ${total} 条记录 第  ${result.pageIndex + 1}/${Math.ceil(
                    result.total / result.pageSize,
                  )}页`,
                onChange,
                onShowSizeChange,
              }}
              renderItem={item => {
                if (item && item.id) {
                  return (
                    <List.Item key={item.id}>
                      <Card
                        hoverable
                        bodyStyle={{ paddingBottom: 20 }}
                        actions={[
                          <Tooltip key="seeProduct" title="查看">
                            <Icon
                              type="eye"
                              onClick={() => {
                                router.push(`/device/product/save/${item.id}`);
                              }}
                            />
                          </Tooltip>,
                          <Tooltip key="update" title="编辑">
                            <Icon
                              type="edit"
                              onClick={() => {
                                setBasicInfo(item);
                                setSaveVisible(true);
                              }}
                            />
                          </Tooltip>,
                          <Tooltip key="download" title="下载">
                            <Icon
                              type="download"
                              onClick={() => {
                                downloadObject(item, '产品');
                              }}
                            />
                          </Tooltip>,
                          <Tooltip key="more_actions" title="">
                            <Dropdown
                              overlay={
                                <Menu>
                                  <Menu.Item key="1">
                                    <Popconfirm
                                      placement="topRight"
                                      title={
                                        item.state !== 0
                                          ? '确定停用此组件吗?'
                                          : '确定发布此组件吗?'
                                      }
                                      onConfirm={() => {
                                        if (item.state === 0) {
                                          deploy(item);
                                        } else {
                                          unDeploy(item);
                                        }
                                      }}
                                    >
                                      <Button
                                        icon={item.state !== 0 ? 'close' : 'check'}
                                        type="link"
                                      >
                                        {item.state !== 0 ? '停用' : '发布'}
                                      </Button>
                                    </Popconfirm>
                                  </Menu.Item>
                                  {item.state === 0 ? (
                                    deviceCount[item.id] === '0' ? (
                                      <Menu.Item key="2">
                                        <Popconfirm
                                          placement="topRight"
                                          title="确定删除此组件吗?"
                                          onConfirm={() => {
                                            if (item.state === 0 && deviceCount[item.id] === '0') {
                                              handleDelete(item);
                                            } else {
                                              message.error('产品以发布,无法删除');
                                            }
                                          }}
                                        >
                                          <Button icon="delete" type="link">
                                            删除
                                          </Button>
                                        </Popconfirm>
                                      </Menu.Item>
                                    ) : (
                                      <Menu.Item key="2">
                                        <Tooltip
                                          placement="bottom"
                                          title="该产品已绑定设备,无法删除"
                                        >
                                          <Button icon="stop" type="link">
                                            删除
                                          </Button>
                                        </Tooltip>
                                      </Menu.Item>
                                    )
                                  ) : (
                                    <Menu.Item key="2">
                                      <Tooltip placement="bottom" title="该产品已发布,无法删除">
                                        <Button icon="stop" type="link">
                                          删除
                                        </Button>
                                      </Tooltip>
                                    </Menu.Item>
                                  )}
                                </Menu>
                              }
                            >
                              <Icon type="ellipsis" />
                            </Dropdown>
                          </Tooltip>,
                        ]}
                      >
                        <Card.Meta
                          avatar={<Avatar size={40} src={item.photoUrl || productImg} />}
                          title={
                            <AutoHide title={item.name} style={{ width: '95%', fontWeight: 600 }} />
                          }
                          description={<AutoHide title={item.id} style={{ width: '95%' }} />}
                        />
                        <div className={cardStyles.cardItemContent}>
                          <div className={cardStyles.cardInfo}>
                            <div style={{ width: '33%', textAlign: 'center' }}>
                              <Spin spinning={!deviceCount[item.id]}>
                                <p style={cardInfoTitle}>设备数量</p>
                                <p style={{ fontSize: 14, fontWeight: 600 }}>
                                  <Tooltip key="findDevice" title="点击查看设备">
                                    <a
                                      onClick={() => {
                                        router.push(`/device/instance?productId=${item.id}`);
                                      }}
                                    >
                                      {numeral(deviceCount[item.id]).format('0,0')}
                                    </a>
                                  </Tooltip>
                                </p>
                              </Spin>
                            </div>
                            <div style={{ width: '33%', textAlign: 'center' }}>
                              <p style={cardInfoTitle}>发布状态</p>
                              <p style={{ fontSize: 14, fontWeight: 600 }}>
                                <Badge
                                  color={item.state === 0 ? 'red' : 'green'}
                                  text={item.state === 0 ? '未发布' : '已发布'}
                                />
                              </p>
                            </div>
                            <div style={{ width: '33%', textAlign: 'center' }}>
                              <p style={cardInfoTitle}>产品类型</p>
                              <AutoHide
                                title={item.deviceType?.text}
                                style={{ fontSize: 14, fontWeight: 600, width: '95%' }}
                              />
                              {/* <p style={{ fontSize: 14, fontWeight: 600 }}>{item.deviceType?.text}</p> */}
                            </div>
                          </div>
                        </div>
                      </Card>
                    </List.Item>
                  );
                }
                return '';
              }}
            />
          )}
        </div>
      </Spin>
      {saveVisible && (
        <Save
          data={basicInfo}
          close={() => {
            setBasicInfo({});
            setSaveVisible(false);
          }}
          save={item => handleSave(item)}
        />
      )}
    </PageHeaderWrapper>
  );
}
Example #16
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Edit = (props: Props) => {
  const service = new Service('tenant');

  const initState: State = {
    list: {},
  };

  const [list, setList] = useState(initState.list);
  const [add, setAdd] = useState<boolean>(false);
  const [cat, setCat] = useState<boolean>(false);
  const [asset, setAsset] = useState();
  const { data } = props;
  const [selected, setSelected] = useState<any[]>([]);
  const [tenant, setTenant] = useState<any[]>([]);

  const initSearch = {
    terms: {
      id$assets: JSON.stringify({
        tenantId: data?.id,
        assetType: 'product',
        memberId: props.user,
        // not: true,
      }),
    },
    pageIndex: 0,
    pageSize: 10,
  };
  const [searchParam, setSearchParam] = useState<any>(initSearch);

  // 获取设备资产和成员
  // let product = (tempSearch: any) =>
  //   service.assets
  //     .product(encodeQueryParam(tempSearch))
  //     .pipe(
  //       mergeMap(result =>
  //         from(result.data).pipe(
  //           mergeMap((item: any) =>
  //             service.assets.members(data.id, 'product', item.id).pipe(
  //               map(j => ({
  //                 ...item,
  //                 tenant: j
  //                   .filter((a: { binding: boolean }) => a.binding === true)
  //                   .map((l: { userName: any }) => l.userName)
  //                   .join('、'),
  //               })),
  //             ),
  //           ),
  //           toArray(),
  //           map(i => ({ ...result, data: i })),
  //         ),
  //       ),
  //     )
  //     .subscribe((data: any) => {
  //       setList(data);
  //     });

  useEffect(() => {
    list.data?.map((item: any) => {
      service.assets.members(data.id, 'product', item.id).subscribe(resp => {
        tenant[item.id] = resp
          .filter((item: any) => item.binding === true)
          .map((i: any) => i.userName)
          .join('、');
        setTenant({ ...tenant });
      });
    });
  }, [list]);

  const handleSearch = (params: any) => {
    const tempParam = { ...searchParam, ...params };
    const defaultItem = searchParam.terms;
    const tempTerms = params?.terms;
    const terms = tempTerms ? { ...defaultItem, ...tempTerms } : initSearch;
    let tempSearch = {};

    if (tempTerms) {
      tempParam.terms = terms;
      tempSearch = tempParam;
    } else {
      tempSearch = initSearch;
    }
    setSearchParam(tempSearch);
    service.assets.product(encodeQueryParam(tempSearch)).subscribe(res => {
      if (res.data.length > 0) {
        let datalist: any = [];
        res.data.forEach((value: any) => {
          datalist.push({
            id: value.id,
            name: value.name,
            photoUrl: value.photoUrl || productImg,
          });
        });
        setList({
          pageIndex: res.pageIndex,
          pageSize: res.pageSize,
          total: res.total,
          data: datalist,
        });
      } else {
        setList({
          pageIndex: res.pageIndex,
          pageSize: res.pageSize,
          total: res.total,
          data: [],
        });
      }
    });
  };
  useEffect(() => {
    handleSearch(searchParam);
  }, []);
  const rowSelection = {
    onChange: (selectedRowKeys: any[], selectedRows: any[]) => {
      setSelected(selectedRows);
    },
    getCheckboxProps: (record: any) => ({
      name: record.name,
    }),
  };
  const columns = [
    {
      title: 'ID',
      dataIndex: 'id',
      align: 'center',
    },
    {
      title: '名称',
      render: (record: any) => (
        <div>
          <Avatar shape="square" src={record.photoUrl || productImg} />
          <span> {record.name}</span>
        </div>
      ),
    },
    {
      title: '租户名称',
      ellipsis: true,
      align: 'left',
      width: 400,
      render: (record: any) => (
        <div
          style={{ overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}
          onClick={() => {
            setAsset(record);
            setCat(true);
          }}
        >
          <span style={{ color: '#1890ff' }}>{tenant[record.id]}</span>
        </div>
      ),
    },
    {
      title: '操作',
      align: 'center',
      render: (_: string, record: any) => (
        <Fragment>
          <a
            onClick={() => {
              router.push(`/device/product/save/${record.id}`);
            }}
          >
            查看
          </a>
        </Fragment>
      ),
    },
  ];

  const unbind = () => {
    service.assets
      .unbind(data.id, [
        {
          assetIdList: selected.map(item => item.id),
          assetType: 'product',
          userId: props?.user,
        },
      ])
      .subscribe(() => {
        message.success('解绑成功');
        handleSearch(searchParam);
        setSelected([]);
      });
  };
  return (
    <Drawer title="编辑产品资产" visible width="75VW" onClose={() => props.close()}>
      <SearchForm
        search={(params: any) => {
          handleSearch({ terms: params });
        }}
        formItems={[
          {
            label: 'ID',
            key: 'id$LIKE',
            type: 'string',
          },
          {
            label: '名称',
            key: 'name$LIKE',
            type: 'string',
          },
        ]}
      />
      <Button type="primary" style={{ marginBottom: 10 }} onClick={() => setAdd(true)}>
        添加
      </Button>
      {selected.length > 0 && (
        <Button
          type="danger"
          style={{ marginBottom: 10, marginLeft: 10 }}
          onClick={() => {
            unbind();
          }}
        >
          {`解绑${selected.length}项`}
        </Button>
      )}
      <ProTable
        rowKey="id"
        rowSelection={rowSelection}
        columns={columns}
        dataSource={list?.data || []}
        onSearch={(searchData: any) => handleSearch(searchData)}
        paginationConfig={list || {}}
      />
      <div
        style={{
          position: 'absolute',
          right: 0,
          bottom: 0,
          width: '100%',
          borderTop: '1px solid #e9e9e9',
          padding: '10px 16px',
          background: '#fff',
          textAlign: 'right',
        }}
      >
        <Button
          onClick={() => {
            props.close();
          }}
          style={{ marginRight: 8 }}
        >
          关闭
        </Button>
      </div>
      {add && (
        <Add
          user={props.user}
          data={data}
          close={() => {
            setAdd(false);
            handleSearch(searchParam);
          }}
        />
      )}
      {cat && (
        <User
          asset={asset}
          close={() => {
            setCat(false);
            handleSearch(searchParam);
          }}
        />
      )}
    </Drawer>
  );
}
Example #17
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Product = (props: Props) => {
  const [visible, setVisible] = useState<boolean>(false);
  const data = useContext(TenantContext);

  const service = new Service('tenant');

  const [pub, setPub] = useState(0);
  const [unPub, setUnPub] = useState(0);

  const getData = () => {
    service.assets
      .productCount(
        encodeQueryParam({
          terms: {
            id$assets: JSON.stringify({
              tenantId: data?.id,
              assetType: 'product',
              memberId: props.user,
            }),
            state: 1,
          },
        }),
      )
      .subscribe(resp => {
        setPub(resp);
      });
    service.assets
      .productCount(
        encodeQueryParam({
          terms: {
            id$assets: JSON.stringify({
              tenantId: data?.id,
              assetType: 'product',
              memberId: props.user,
            }),
            state: 0,
          },
        }),
      )
      .subscribe(resp => {
        setUnPub(resp);
      });
  };
  useEffect(() => {
    getData();
  }, [props.user]);
  return (
    <List.Item style={{ paddingRight: '10px' }}>
      <Card
        hoverable
        className={styles.card}
        actions={[
          <Tooltip title="查看">
            <Icon
              type="eye"
              onClick={() =>
                router.push({
                  pathname: `/device/product`,
                  search:
                    'iop=' +
                    JSON.stringify({
                      terms: {
                        id$assets: {
                          tenantId: data?.id,
                          assetType: 'product',
                          memberId: props.user,
                          // not: true,
                        },
                      },
                    }),
                })
              }
            />
          </Tooltip>,
          <Tooltip title="编辑">
            <Icon type="edit" onClick={() => setVisible(true)} />
          </Tooltip>,
        ]}
      >
        <Card.Meta
          avatar={<IconFont type="icon-chanpin" style={{ fontSize: 45 }} />}
          title={<a>产品</a>}
        />
        <div className={styles.cardInfo}>
          <div>
            <p>已发布</p>
            <p>{pub}</p>
          </div>
          <div>
            <p>未发布</p>
            <p>{unPub}</p>
          </div>
        </div>
      </Card>
      {visible && (
        <Edit
          data={data}
          user={props.user}
          close={() => {
            setVisible(false);
            getData();
          }}
        />
      )}
    </List.Item>
  );
}
Example #18
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Protocol = (props: Props) => {
    const [visible, setVisible] = useState<boolean>(false);
    const data = useContext(TenantContext);
    const service = new Service('tenant');

    const [deploy, setDeploy] = useState(0);
    const [unDeploy, setUndeploy] = useState(0);
    const getData = () => {
        service.assets.protocolCount(encodeQueryParam({
            terms: {
                id$assets: JSON.stringify({
                    tenantId: data?.id,
                    assetType: 'protocol',
                    memberId: props.user,
                }),
                state: 1 // 已发布
            }
        })).subscribe(resp => {
            setDeploy(resp)
        });
        service.assets.protocolCount(encodeQueryParam({
            terms: {
                id$assets: JSON.stringify({
                    tenantId: data?.id,
                    assetType: 'protocol',
                    memberId: props.user,
                }),
                state: 0 // 未发布
            }
        })).subscribe(resp => {
            setUndeploy(resp);
            // setActive(resp)
        })
    }
    useEffect(() => {
        getData();
    }, [props.user]);

    return (
        <List.Item style={{ paddingRight: '10px' }}>
            <Card
                hoverable
                className={styles.card}
                actions={[
                    <Tooltip title="查看">
                        <Icon type="eye" onClick={() => router.push({
                            pathname: `/network/protocol`,
                            query: {
                                terms: {
                                    id$assets: JSON.stringify({
                                        tenantId: data?.id,
                                        assetType: 'protocol',
                                        memberId: props.user,
                                    })
                                }
                            }
                        })} />
                    </Tooltip>,
                    <Tooltip title="编辑">
                        <Icon type="edit" onClick={() => setVisible(true)} />
                    </Tooltip>]}
            >
                <Card.Meta
                    avatar={<IconFont type="icon-anzhuangbaoguanli" style={{ fontSize: 45 }} />}
                    title={<a>协议</a>}
                />
                <div className={styles.cardInfo}>
                    <div>
                        <p>已发布</p>
                        <p>{deploy}</p>
                    </div>
                    <div>
                        <p>未发布</p>
                        <p>{unDeploy}</p>
                    </div>
                </div>
            </Card>
            {visible && (
                <Edit
                    user={props.user}
                    data={data}
                    close={() => {
                        setVisible(false);
                        getData();
                    }} />
            )}
        </List.Item>
    )
}
Example #19
Source File: userIndex.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
UserTenant = () => {
  const service = new Service('tenant');
  const [loading, setLoading] = useState<boolean>(false);
  const [tloading, setTloading] = useState<boolean>(false);
  const [visible, setVisible] = useState<boolean>(false);
  const [current, setCurrent] = useState<Partial<TenantItem>>({});
  const [list, setList] = useState<ListData<TenantItem>>();
  const [searchParam, setSearchParam] = useState({
    sorts: {
      field: 'id',
      order: 'desc',
    },
  });

  const handleSearch = (params: any) => {
    setTloading(true);
    setSearchParam(params);
    service.list(encodeQueryParam(params)).subscribe(data => {
      setList(data);
      setTloading(false);
    });
  };

  useEffect(() => {
    handleSearch(searchParam);
  }, []);

  const paginationProps: PaginationConfig = {
    showQuickJumper: true,
    pageSize: 5,
    total: list?.total || 0,
    showTotal: (total: number) =>
      `共 ${total} 条记录 第  ${(searchParam.pageIndex || 0) + 1}/${Math.ceil(
        total / (searchParam.pageSize || 5),
      )}页`,
    onChange: (page, pageSize) => {
      handleSearch({ ...searchParam, pageIndex: page - 1, pageSize: 5 });
    },
  };

  const extraContent = (
    <div className={styles.extraContent}>
      <RadioGroup
        defaultValue="all"
        onChange={e => handleSearch(encodeQueryParam({ terms: { state: e.target.value } }))}
      >
        <RadioButton value="">全部</RadioButton>
        <RadioButton value="enabled">正常</RadioButton>
        <RadioButton value="disabled">禁用</RadioButton>
      </RadioGroup>
      {/* <Search className={styles.extraContentSearch} placeholder="请输入" onSearch={() => ({})} /> */}
    </div>
  );

  const saveItem = (data: any) => {
    setLoading(true);
    service.create(data).subscribe(
      res => {
        if (res) {
          message.success('保存成功');
          setLoading(false);
          setVisible(false);
          setCurrent({});
          handleSearch(searchParam);
        }
      },
      () => {
        message.error('保存失败');
      },
    );
  };

  const changeState = (item: any, state: string) => {
    setLoading(true);
    const data = item;
    data.state = state;
    service.update(data).subscribe(() => {
      setLoading(false);
      message.success('操作成功');
      handleSearch(searchParam);
    });
  };

  return (
    <PageHeaderWrapper>
      <Spin spinning={tloading}>
        <div className={styles.standardList}>
          <Card
            className={styles.listCard}
            bordered={false}
            title="租户列表"
            style={{ marginTop: 24 }}
            bodyStyle={{ padding: '0 32px 40px 32px' }}
            extra={extraContent}
          >
            <Button
              type="dashed"
              style={{ width: '100%', marginBottom: 8 }}
              onClick={() => {
                setVisible(true);
                setCurrent({});
              }}
            >
              <PlusOutlined />
              添加
            </Button>

            <List
              size="large"
              rowKey="id"
              pagination={paginationProps}
              dataSource={list?.data}
              renderItem={(item: TenantItem) => (
                <List.Item
                  actions={[
                    <a
                      onClick={e => {
                        e.preventDefault();
                        router.push(`/system/tenant/detail/${item.id}`);
                      }}
                    >
                      查看
                    </a>,
                    <>
                      {item.state.value === 'enabled' ? (
                        <Popconfirm
                          title="确认禁用此租户?"
                          onConfirm={() => {
                            changeState(item, 'disabled');
                          }}
                        >
                          <a key="edit">禁用</a>
                        </Popconfirm>
                      ) : (
                        <Popconfirm
                          title="确认禁用此租户?"
                          onConfirm={() => {
                            changeState(item, 'enabled');
                          }}
                        >
                          <a key="edit">启用</a>
                        </Popconfirm>
                      )}
                    </>,
                  ]}
                >
                  <List.Item.Meta
                    avatar={<Avatar src={item.photo || defaultImg} shape="square" size="large" />}
                    title={<a>{item.name}</a>}
                    description={item.description}
                  />
                  <ListContent data={item} />
                </List.Item>
              )}
            />
          </Card>
        </div>
      </Spin>

      {visible && (
        <Spin spinning={loading}>
          <Save
            close={() => {
              setVisible(false);
            }}
            save={(item: any) => saveItem(item)}
            data={current}
          />
        </Spin>
      )}
    </PageHeaderWrapper>
  );
}
Example #20
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Login: React.FC<Props> = props => {
  const { dispatch, settings, location: { query } } = props;
  const token = getAccessToken();
  const service = new Service('');
  const [username, setUsername] = useState<string>('');
  const [password, setPassword] = useState<string>('');
  const [expires, setExpires] = useState<number>(3600000);
  const [isReady, setIsReady] = useState(false);

  const [captcha, setCaptcha] = useState<string>('');
  const [captchaImg, setCaptchaImg] = useState<string>('');
  const [code, setCode] = useState<string>("");
  const [enable, setEnable] = useState<boolean>(false);
  const [current, setCurrent] = useState<boolean>(false);

  const handleSubmit = () => {
    dispatch({
      type: 'login/login',
      payload: {
        username,
        password,
        expires,
        tokenType: 'default',
        verifyKey: captcha,
        verifyCode: code,
        bindCode: query?.code
      },
      callback: () => { getCodeImg() }
    });
  };

  const getCodeImg = () => {
    if (enable) {
      service.getCaptcha().subscribe((resp) => {
        setCaptcha(resp.key);
        setCaptchaImg(resp.base64);
      });
    }
  }

  useEffect(() => {
    if (dispatch) {
      dispatch({
        type: 'settings/fetchConfig',
        callback: () => {
          const title = document.getElementById('sys-title');
          const icon = document.getElementById('title-icon');
          if (title && settings.title) {
            title.textContent = settings.title;
          }
          if (icon && settings.titleIcon) {
            icon.href = settings.titleIcon;
          }
          if (token!=='null') {
            service.queryCurrent().subscribe((resp) => {
              if (resp.status === 200) {
                setCurrent(true)
              } else {
                setCurrent(false)
              }
              setIsReady(true);
            })
          } else {
            setIsReady(true);
          }
        }
      });
    }

    //判断是否开启验证码
    service.captchaConfig().subscribe((resp) => {
      if (resp) {
        setEnable(resp.enabled)
        if (resp.enabled) {
          //获取验证码
          service.getCaptcha().subscribe((resp) => {
            setCaptcha(resp.key);
            setCaptchaImg(resp.base64);
          });
          // getCodeImg();
        } else {
          //未开启验证码
        }
      }
    });



  }, [settings.title]);

  const Login = () => {
    const information = JSON.parse(localStorage.getItem('user-detail') || '{}');
    if (current) {
      return (
        <div className={style.login}>
          <div className={style.bg1} />
          <div className={style.gyl}>
            物联网平台
        <div className={style.gy2}>MQTT TCP CoAP HTTP , 多消息协议适配 , 可视化规则引擎
        </div>
          </div>
          <div className={style.box}>
            <div className={style.box1} >
              <div style={{ width: '100%', height: '30px' }}></div>
              <div className={style.avatar}>
                <Avatar size="small" className={style.avatarx} src={information.avatar || 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3273016457,3482023254&fm=26&gp=0.jpg'} alt="avatar" />
              </div>
              <input
                onClick={() => {
                  let result = getQueryString(window.location.hash);
                  if (result &&
                    result.client_id !== undefined &&
                    result.response_type !== undefined &&
                    result.redirect_uri !== undefined &&
                    result.state !== undefined
                  ) {
                    apis.login.oauth(result).then(res => {
                      if (res.status === 200) {
                        window.location.href = res.result;
                      }
                    });
                  } else {
                    router.replace('/');
                  }
                }}
                className={style.btn}
                type="button"
                name="登录"
                value="登录"
              />
              <div style={{ width: '100%', height: '30px' }}></div>
              <input
                onClick={() => {
                  localStorage.setItem('x-access-token', '');
                  setCurrent(false)
                  if (window.location.pathname !== '/user/login') {
                    router.replace({
                      pathname: '/user/login'
                    });
                  } else {
                    router.push('/user/login');
                  }
                }}
                className={style.btn}
                type="button"
                name="切换账号"
                value="切换账号"
              />
              <div style={{ width: '100%', height: '30px' }}></div>
            </div>
          </div>
        </div>
      )
    } else {
      return renderLogin()
    }
  }
  const renderLogin = () => (
    <div className={style.login}>

      <div className={style.bg1} />
      <div className={style.gyl}>
        物联网平台
        <div className={style.gy2}>MQTT TCP CoAP HTTP , 多消息协议适配 , 可视化规则引擎
        </div>
      </div>
      {/* style={{ height: enable ? '387px' : '330px' }} */}
      <div className={style.box}>
        <div className={style.box1} >
          <div className={style.header}>用户登录</div>

          <div className={style.item}>
            <div className={style.userLabel}>用户名</div>
            <input
              style={{ borderStyle: 'none none solid none' }}
              onChange={e => setUsername(e.target.value)}
              value={username}
              type="text"
            />
          </div>
          <div className={style.item} onKeyUp={e => { if (e.keyCode === 13 && !enable) { handleSubmit(); } }}>
            <div className={style.userLabel}>
              密<span style={{ marginLeft: '1em' }} />码
            </div>
            <input
              style={{ borderStyle: 'none none solid none' }}
              onChange={e => setPassword(e.target.value)}
              value={password}
              type="password"
            />
          </div>
          {
            enable ? <div className={style.item}>
              <div className={style.userLabel}>验证码</div>
              <input onKeyUp={e => { if (e.keyCode === 13) { handleSubmit(); } }}
                style={{ borderStyle: 'none none solid none' }}
                onChange={e => setCode(e.target.value)}
                value={code}
                type="text"
              />
              <div className={style.code} onClick={() => { getCodeImg(); }}><img src={captchaImg} className={style.code_img} /></div>
            </div> : <div></div>
          }


          <div className={style.remember}>
            <div className={style.remember_box}>
              <input
                type="checkbox"
                checked={expires === -1}
                onChange={() => {
                  setExpires(expires === -1 ? 3600000 : -1);
                }}
              />
              <div className={style.text}>记住我</div>
            </div>
          </div>

          <input
            onClick={() => {
              handleSubmit();
            }}
            className={style.btn}
            type="button"
            name="登录"
            value="登录"
          />
        </div>
      </div>
    </div>
  )
  // return isReady ? renderLogin() : <Spin spinning={isReady} />;
  return isReady ? Login() : <Spin spinning={isReady} />;
}
Example #21
Source File: request.ts    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
errorHandler = (error: { response: Response }): Response | undefined => {
  const {
    // response: { status },
    response,
  } = error;

  // if (response && response.status) {
  // const errorText = codeMessage[response.status] || response.statusText;
  // const { status, url } = response;

  // notification.error({
  //   message: `请求错误 ${status}: ${url}`,
  //   description: errorText,
  // });
  if (response) {
    if (response.status === 401) {
      notification.error({
        key: 'error',
        message: '未登录或登录已过期,请重新登录。',
      });
      localStorage.removeItem('x-access-token');
      // const { redirect } = getPageQuery();
      setTimeout(() => router.push('/user/login'), 300);
      // if (window.location.pathname !== '/user/login' && !redirect) {
      //   router.replace({
      //     pathname: '/user/login',
      //     search: stringify({
      //       // redirect: window.location.href,
      //     }),
      //   });
      // } else {

      // }
    } else if (response.status === 400) {
      response.text().then(resp => {
        if (resp) {
          notification.error({
            key: 'error',
            message: JSON.parse(resp).message,
          });
        } else {
          response.json().then((res: any) => {
            notification.error({
              key: 'error',
              message: `请求错误:${res.message}`,
            });
          });
        }
      });
      return response;
    } else if (response.status === 500) {
      response
        .json()
        .then((res: any) => {
          notification.error({
            key: 'error',
            message: `${res.message}`,
          });
        })
        .catch(() => {
          notification.error({
            key: 'error',
            message: response.statusText,
          });
        });
    } else if (response.status === 504) {
      notification.error({
        key: 'error',
        message: '服务器错误',
      });

      // router.push('/user/login');
    } else if (response.status === 403) {
      response.json().then((res: any) => {
        notification.error({
          key: 'error',
          message: `${res.message}`,
        });
      });

      // router.push('/exception/403');
      // return;
    } else if (response.status === 404) {
      if (window.location.hash.search('/user/login') != -1) {
        notification.error({
          key: 'error',
          message: '404 错误',
        });
        return response;
      }
      // console.log(status, '状态');
      router.push('/exception/404');
    } else {
      notification.error({
        key: 'error',
        message: '服务器内部错误',
      });
    }
  } else {
    notification.error({
      key: 'error',
      message: '服务器内部错误,请检测您的配置',
    });
  }
  return response;
  // } else if (!response) {
  //   notification.error({
  //     description: '您的网络发生异常,无法连接服务器',
  //     message: '网络异常',
  //   });
  // }
  // return response;
}
Example #22
Source File: DeviceTree.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
DeviceTree: React.FC<Props> = props => {
  const {
    location: { query },
  } = props;

  const initialState: State = {
    data: [],
    deviceData: {},
    saveVisible: false,
    bindVisible: false,
    detailVisible: false,
    current: {},
    parentId: query.id,
    deviceIds: [],
    device: {},
  };
  const reducer = (state: State, action: any) => {
    switch (action.type) {
      case 'operation':
        return { ...state, ...action.payload };
      default:
        throw new Error();
    }
  };

  const [state, dispatch] = useReducer(reducer, initialState);
  const [add, setAdd] = useState<boolean>(false);
  const [loading, setLoading] = useState<boolean>(false);
  const [deviceLoading, setDeviceLoading] = useState<boolean>(true);
  const {
    data,
    deviceData,
    saveVisible,
    bindVisible,
    detailVisible,
    current,
    parentId,
    deviceIds,
    device,
  } = state;
  const service = new Service('device');
  const [searchParam, setSearchParam] = useState({
    pageSize: 10,
    sorts: {
      order: 'descend',
      field: 'alarmTime',
    },
  });
  const [searchValue, setSearchValue] = useState('');
  const search = (param?: any) => {
    setDeviceLoading(true);
    const defaultTerms = {
      terms: {
        id: query.id,
      },
      paging: false,
    };

    service
      .groupTree(
        encodeQueryParam({
          ...defaultTerms,
          terms: { ...defaultTerms.terms, ...param?.terms },
        }),
      )
      .subscribe(
        resp => {
          dispatch({
            type: 'operation',
            payload: {
              data: resp,
            },
          });
          searchDevice(resp[0], searchParam);
          dispatch({
            type: 'operation',
            payload: {
              current: resp[0],
              parentId: resp[0].id,
            },
          });
        },
        () => { },
        () => {
          setLoading(true);
        },
      );
  };
  useEffect(() => {
    search();
  }, []);
  const saveGroup = (item: GroupItem) => {
    if (add) {
      service.saveGroup({ ...item, parentId }).subscribe(
        () => message.success('添加成功'),
        () => { },
        () => {
          dispatch({ type: 'operation', payload: { saveVisible: false, parentId: null } });
          search();
        },
      );
    } else {
      service.saveOrUpdataGroup(item).subscribe(
        () => message.success('添加成功'),
        () => { },
        () => {
          dispatch({ type: 'operation', payload: { saveVisible: false, parentId: null } });
          search();
        },
      );
    }
  };

  const searchDevice = (item: GroupItem | any, params?: any) => {
    service
      .groupDevice(
        encodeQueryParam({
          pageSize: 10,
          ...params,
          terms: {
            'id$dev-group': item.id,
            name$LIKE: item.searchValue,
          },
        }),
      )
      .subscribe(
        resp => {
          dispatch({
            type: 'operation',
            payload: {
              deviceData: resp,
              // deviceIds: resp.data.map((item: any) => item.id),
            },
          });
        },
        () => { },
        () => {
          setDeviceLoading(false);
        },
      );
  };
  const bindDevice = () => {
    if (deviceIds.length > 0) {
      service.bindDevice(parentId!, deviceIds).subscribe(
        () => message.success('绑定成功'),
        () => message.error('绑定失败'),
        () => {
          dispatch({
            type: 'operation',
            payload: {
              bindVisible: false,
              deviceIds: []
            },
          });
          searchDevice({ id: parentId });
        },
      );
    } else {
      message.error('未选择需要绑定的设备')
    }
  };
  const unbindDevice = (deviceId: string[]) => {
    service.unbindDevice(parentId!, deviceId).subscribe(
      () => message.success('解绑成功'),
      () => message.error('解绑失败'),
      () => {
        dispatch({
          type: 'operation',
          payload: {
            bindVisible: false,
          },
        });
        searchDevice({ id: parentId });
      },
    );
  };

  const unbindAll = () => {
    if (selectedRowKeys.length > 0) {
      service.unbind(parentId!, selectedRowKeys).subscribe(
        () => message.success('解绑成功'),
        () => message.error('解绑失败'),
        () => {
          dispatch({
            type: 'operation',
            payload: {
              bindVisible: false,
            },
          });
          searchDevice({ id: parentId });
          setSelectedRowKeys([]);
        },
      );
    } else {
      service.unbindAll(parentId!).subscribe(
        () => message.success('解绑成功'),
        () => message.error('解绑失败'),
        () => {
          dispatch({
            type: 'operation',
            payload: {
              bindVisible: false,
            },
          });
          searchDevice({ id: parentId });
        },
      );
    }
  };
  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
  const rowSelection = {
    selectedRowKeys,
    onChange: (selectedRowKeys: string[] | any[]) => {
      setSelectedRowKeys(selectedRowKeys);
    },
  };

  return (
    <PageHeaderWrapper
      onBack={() => router.push('/device/tree')}
      title={<>{data[0] ? data[0].name : null}</>}
    >
      {loading && (
        <Card>
          <Row gutter={24}>
            <Col span={8}>
              <Table
                title={() => (
                  <>
                    分组
                    <span style={{ marginLeft: 20, marginRight: 10 }}>
                      <Input.Search
                        style={{ width: '60%' }}
                        placeholder="请输入输入名称"
                        // onChange={() => search()}
                        onSearch={value => {
                          if (value) {
                            const tempData = data.filter(
                              (item: any) => item.name.indexOf(value) > -1,
                            );
                            dispatch({ type: 'operation', payload: { data: tempData } });
                          } else {
                            search();
                          }
                        }}
                      />
                    </span>
                    <Button
                      type="primary"
                      onClick={() => {
                        setAdd(true);
                        dispatch({
                          type: 'operation',
                          payload: {
                            saveVisible: true,
                            parentId: query.id,
                            // current:null,
                          },
                        });
                      }}
                    >
                      新增
                    </Button>
                  </>
                )}
                onRow={item => {
                  return {
                    onClick: () => {
                      searchDevice(item, searchParam);
                      setDeviceLoading(true);
                      dispatch({
                        type: 'operation',
                        payload: { current: item, parentId: item.id },
                      });
                    },
                  };
                }}
                bordered={false}
                pagination={false}
                dataSource={data}
                size="small"
                defaultExpandedRowKeys={data[0] && [data[0].id]}
                rowKey={(item: any) => item.id}
                columns={[
                  {
                    title: '序号',
                    dataIndex: 'id',
                    // width:200,
                    ellipsis: true,
                    render: (record) => (
                      <Tooltip title={record}>
                        <span>{record}</span>
                      </Tooltip>
                    )
                  },
                  { title: '名称', dataIndex: 'name', ellipsis: true },
                  {
                    title: '操作',
                    width: 100,
                    align: 'center',
                    render: (_, record) => (
                      <Fragment>
                        <Icon
                          type="edit"
                          onClick={() => {
                            setAdd(false);
                            dispatch({
                              type: 'operation',
                              payload: {
                                current: record,
                                saveVisible: true,
                              },
                            });
                          }}
                        />
                        {
                          record.level < 6 &&
                          <>
                            <Divider type="vertical" />
                            <Tooltip title="新增子分组">
                              <Icon
                                type="plus"
                                onClick={() => {
                                  setAdd(true);
                                  dispatch({
                                    type: 'operation',
                                    payload: {
                                      parentId: record.id,
                                      saveVisible: true,
                                    },
                                  });
                                }}
                              />
                            </Tooltip>
                          </>
                        }
                        <Divider type="vertical" />
                        <Tooltip title="删除">
                          <Popconfirm
                            title="确认删除分组?"
                            onConfirm={() => {
                              service.removeGroup(record.id).subscribe(
                                () => message.success('删除成功!'),
                                () => message.error('删除失败'),
                                () => search(),
                              );
                            }}
                          >
                            <Icon type="close" />
                          </Popconfirm>
                        </Tooltip>
                        {/* <Divider type="vertical" />
                                                <Tooltip title="绑定设备">
                                                    <Icon type="apartment" onClick={() => {
                                                        dispatch({
                                                            type: 'operation',
                                                            payload: {
                                                                bindVisible: true,
                                                                parentId: record.id
                                                            }
                                                        })
                                                    }} />
                                                </Tooltip> */}
                      </Fragment>
                    ),
                  },
                ]}
              />
            </Col>
            <Col span={16}>
              <ProTable
                loading={deviceLoading}
                title={() => (
                  <Fragment>
                    {current.name}
                    <span style={{ marginLeft: 20 }}>
                      <Input.Search
                        style={{ width: '30%' }}
                        placeholder="输入名称后自动查询"
                        onSearch={value => {
                          setSearchValue(value);
                          searchDevice({
                            id: parentId,
                            searchValue: value,
                          })
                        }}
                      />
                    </span>

                    <Popconfirm title="确认解绑?" onConfirm={() => unbindAll()}>
                      <Button style={{ marginLeft: 10 }} type="danger">
                        解绑{selectedRowKeys.length > 0 ? `${selectedRowKeys.length}项` : '全部'}
                      </Button>
                    </Popconfirm>
                    <Button
                      style={{ marginLeft: 10 }}
                      onClick={() => {
                        dispatch({
                          type: 'operation',
                          payload: {
                            bindVisible: true,
                            parentId: parentId,
                          },
                        });
                      }}
                    >
                      绑定设备
                    </Button>
                  </Fragment>
                )}
                dataSource={deviceData?.data}
                paginationConfig={deviceData}
                size="small"
                rowKey="id"
                rowSelection={rowSelection}
                onSearch={(params: any) => {
                  searchDevice({ id: parentId, searchValue: searchValue }, params);
                }}
                columns={[
                  { title: 'ID', dataIndex: 'id' },
                  { title: '名称', dataIndex: 'name' },
                  { title: '产品名称', dataIndex: 'productName' },
                  { title: '状态', dataIndex: 'state', render: (text: any) => text.text },
                  {
                    title: '操作',
                    render: (_: any, record: any) => (
                      <Fragment>
                        <a
                          onClick={() => {
                            dispatch({
                              type: 'operation',
                              payload: {
                                device: record,
                                detailVisible: true,
                              },
                            });
                          }}
                        >
                          详情
                        </a>

                        <Divider type="vertical" />
                        <a
                          onClick={() => {
                            unbindDevice([record.id]);
                          }}
                        >
                          解绑
                        </a>
                      </Fragment>
                    ),
                  },
                ]}
              />
            </Col>
          </Row>
        </Card>
      )}
      {saveVisible && (
        <Save
          data={current}
          flag={add}
          close={() => {
            dispatch({ type: 'operation', payload: { saveVisible: false, current: {} } });
            setAdd(false);
          }}
          save={(item: GroupItem) => saveGroup(item)}
        />
      )}
      {bindVisible && (
        <Modal
          title="绑定设备"
          visible
          width="80vw"
          onCancel={() => {
            dispatch({
              type: 'operation',
              payload: {
                deviceIds: [],
                bindVisible: false
              },
            });
          }}
          onOk={() => {
            bindDevice();
          }}
        >
          <ChoiceDevice
            parentId={parentId}
            save={(item: any[]) => {
              dispatch({
                type: 'operation',
                payload: {
                  deviceIds: item,
                },
              });
            }}
          />
        </Modal>
      )}
      {detailVisible && (
        <Drawer
          visible
          width="80vw"
          title="设备详情"
          onClose={() => {
            dispatch({
              type: 'operation',
              payload: {
                detailVisible: false,
              },
            });
          }}
        >
          <DeviceInfo
            location={{
              pathname: `/device/instance/save/${device.id}`,
              search: '',
              hash: '',
              query: {},
              state: undefined,
            }}
          />
        </Drawer>
      )}
    </PageHeaderWrapper>
  );
}
Example #23
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Save: React.FC<Props> = props => {
  const initState: State = {
    protocolSupports: [],
    protocolTransports: [],
    organizationList: [],
    configName: '',
    configForm: [],
    classified: [],
    classifiedData: {},
    defaultMetadata: '{"events":[],"properties":[],"functions":[],"tags":[]}',
  };
  const systemVersion = localStorage.getItem('system-version');

  const {getFieldDecorator, setFieldsValue} = props.form;
  // 消息协议
  const [protocolSupports, setProtocolSupports] = useState(initState.protocolSupports);
  // 消息协议
  const [organizationList, setOrganizationList] = useState(initState.organizationList);
  // 传输协议
  const [protocolTransports, setProtocolTransports] = useState(initState.protocolTransports);
  const [classified, setClassified] = useState(initState.classified);
  const [classifiedData, setClassifiedData] = useState(initState.classifiedData);

  //默认物模型
  const [defaultMetadata, setDefaultMetadata] = useState(initState.defaultMetadata);

  const [photoUrl, setPhotoUrl] = useState(props.data?.photoUrl);
  const [classifiedVisible, setClassifiedVisible] = useState(false);

  const [storagePolicy, setStoragePolicy] = useState<any[]>([]);
  const [checkStorage, setCheckStorage] = useState<any>({});
  const onMessageProtocolChange = (value: string) => {
    // 获取链接协议
    apis.deviceProdcut
      .protocolTransports(value)
      .then(e => {
        if (e.status === 200) {
          setProtocolTransports(e.result);
        }
      })
      .catch(() => {
      });
  };

  const getDefaultModel = (id: string, transport: string) => {
    apis.deviceProdcut
      .getDefaultModel(id, transport)
      .then(res => {
        if (res.status === 200) {
          if (res.result === '{}') {
            setDefaultMetadata('{"events":[],"properties":[],"functions":[],"tags":[]}');
          } else {
            setDefaultMetadata(res.result);
          }
        } else {
          setDefaultMetadata('{"events":[],"properties":[],"functions":[],"tags":[]}');
        }
      })
      .catch(() => {
        setDefaultMetadata('{"events":[],"properties":[],"functions":[],"tags":[]}');
      });
  };
  useEffect(() => {
    apis.deviceProdcut
      .protocolSupport()
      .then(e => {
        if (e.status === 200) {
          setProtocolSupports(e.result);
        }
      })
      .catch(() => {
      });

    apis.deviceProdcut
      .deviceCategoryTree(encodeQueryParam({paging: false, sorts: {field: 'id', order: 'desc'}}))
      .then((response: any) => {
        if (response.status === 200) {
          setClassified(response.result);
        }
      })
      .catch(() => {
      });

    apis.deviceProdcut
      .queryOrganization()
      .then((res: any) => {
        if (res.status === 200) {
          let orgList: any = [];
          res.result.map((item: any) => {
            orgList.push({id: item.id, pId: item.parentId, value: item.id, title: item.name});
          });
          setOrganizationList(orgList);
        }
      })
      .catch(() => {
      });

    // if (systemVersion === 'pro') {
    apis.deviceProdcut.storagePolicy().then(res => {
      if (res.status === 200) {
        setStoragePolicy(res.result);
      }
    });
    // }
    if (props.data && props.data.messageProtocol) {
      onMessageProtocolChange(props.data.messageProtocol);
    }
  }, []);

  const basicForm: FormItemConfig[] = [
    {
      label: '产品ID',
      key: 'id',
      styles: {
        lg: {span: 8},
        md: {span: 12},
        sm: {span: 24},
      },
      options: {
        initialValue: props.data?.id,
        rules: [
          {required: true, message: '请输入产品ID'},
          {max: 64, message: '产品ID不超过64个字符'},
          {
            pattern: new RegExp(/^[0-9a-zA-Z_\-]+$/, 'g'),
            message: '产品ID只能由数字、字母、下划线、中划线组成',
          },
        ],
      },
      component: <Input placeholder="请输入产品ID " disabled={!!props.data?.id}/>,
    },
    {
      label: '产品名称',
      key: 'name',
      options: {
        rules: [
          {required: true, message: '请输入产品名称'},
          {max: 200, message: '产品名称不超过200个字符'},
        ],
        initialValue: props.data?.name,
      },
      styles: {
        xl: {span: 8},
        lg: {span: 8},
        md: {span: 12},
        sm: {span: 24},
      },
      component: <Input style={{width: '100%'}} maxLength={200} placeholder="请输入"/>,
    },
    {
      label: '所属品类',
      key: 'classifiedId',
      options: {
        rules: [{required: true, message: '请选择所属品类'}],
      },
      styles: {
        xl: {span: 8},
        lg: {span: 8},
        md: {span: 12},
        sm: {span: 24},
      },
      component: (
        <Cascader
          fieldNames={{label: 'name', value: 'id', children: 'children'}}
          options={classified}
          popupVisible={false}
          onChange={value => {
            if (value.length === 0) {
              setClassifiedData({});
            }
          }}
          onClick={() => {
            setClassifiedVisible(true);
          }}
          placeholder="点击选择品类"
        />
      ),
    },
    {
      label: '所属机构',
      key: 'orgId',
      options: {
        initialValue: props.data?.orgId,
      },
      styles: {
        xl: {span: 8},
        lg: {span: 10},
        md: {span: 24},
        sm: {span: 24},
      },
      component: (
        <TreeSelect
          allowClear
          treeDataSimpleMode
          showSearch
          placeholder="所属机构"
          treeData={organizationList}
          treeNodeFilterProp="title"
          searchPlaceholder="根据机构名称模糊查询"
        />
      ),
    },
    {
      label: '消息协议',
      key: 'messageProtocol',
      options: {
        rules: [{required: true, message: '请选择消息协议'}],
        initialValue: props.data?.messageProtocol,
      },
      styles: {
        xl: {span: 8},
        lg: {span: 8},
        md: {span: 12},
        sm: {span: 24},
      },
      component: (
        <Select
          placeholder="请选择"
          showSearch
          optionFilterProp='label'
          onChange={(value: string) => {
            onMessageProtocolChange(value);
          }}
        >
          {protocolSupports.map(e => (
            <Select.Option value={e.id} key={e.id} label={e.name}>
              {e.name}
            </Select.Option>
          ))}
        </Select>
      ),
    },
    {
      label: '传输协议',
      key: 'transportProtocol',
      options: {
        rules: [{required: true, message: '请选择传输协议'}],
        initialValue: props.data?.transportProtocol,
      },
      styles: {
        xl: {span: 8},
        lg: {span: 10},
        md: {span: 24},
        sm: {span: 24},
      },
      component: (
        <Select
          placeholder="请选择"
          showSearch
          optionFilterProp='label'
          onChange={(value: string) => {
            if (
              value !== '' &&
              value !== undefined &&
              props.form.getFieldsValue().messageProtocol !== '' &&
              props.form.getFieldsValue().messageProtocol !== undefined
            ) {
              getDefaultModel(props.form.getFieldsValue().messageProtocol, value);
            }
          }}
        >
          {protocolTransports.map(e => (
            <Select.Option value={e.id} key={e.id} label={e.name}>
              {e.name}
            </Select.Option>
          ))}
        </Select>
      ),
    },
    {
      label: (
        <span>
          存储策略&nbsp;
          <Tooltip
            title={
              checkStorage.description
                ? checkStorage.description
                : '使用指定的存储策略来存储设备数据'
            }
          >
            <Icon type="question-circle-o"/>
          </Tooltip>
        </span>
      ),
      key: 'storePolicy',
      options: {},
      styles: {
        xl: {span: 8},
        lg: {span: 10},
        md: {span: 24},
        sm: {span: 24},
      },
      component: (
        <Select
          onChange={e => setCheckStorage(storagePolicy.find(i => i.id === e))}
          placeholder="请选择"
        >
          {storagePolicy.map(e => (
            <Select.Option value={e.id} key={e.id}>
              {e.name}
            </Select.Option>
          ))}
        </Select>
      ),
    },

    {
      label: '设备类型',
      key: 'deviceType',
      options: {
        rules: [{required: true, message: '请选择设备类型'}],
        initialValue:
          typeof props.data?.deviceType === 'string'
            ? props.data?.deviceType
            : (props.data?.deviceType || {}).value,
      },
      styles: {
        lg: {span: 8},
        md: {span: 12},
        sm: {span: 24},
      },
      component: (
        <Radio.Group>
          <Radio value="device">直连设备</Radio>
          <Radio value="childrenDevice">网关子设备</Radio>
          <Radio value="gateway">网关设备</Radio>
        </Radio.Group>
      ),
    },
    {
      label: '描述',
      key: 'describe',
      styles: {
        xl: {span: 24},
        lg: {span: 24},
        md: {span: 24},
        sm: {span: 24},
      },
      options: {
        initialValue: props.data?.describe,
      },
      component: <Input.TextArea rows={4} placeholder="请输入描述"/>,
    },
  ];

  const saveData = () => {
    const {form} = props;
    form.validateFields((err, fileValue) => {
      if (err) return;
      if (!fileValue.orgId) {
        fileValue.orgId = '';
      }

      const protocol: Partial<ProtocolItem> =
        protocolSupports.find(i => i.id === fileValue.messageProtocol) || {};

      apis.deviceProdcut
        .saveDeviceProduct({
          state: 0,
          ...fileValue,
          photoUrl,
          metadata: defaultMetadata, //'{"events":[],"properties":[],"functions":[],"tags":[]}',
          protocolName: protocol.name,
          classifiedId: classifiedData.id,
          classifiedName: classifiedData.name,
        })
        .then((response: any) => {
          if (response.status === 200) {
            message.success('保存成功');
            router.push(`/device/product/save/${response.result.id}`);
          }
        })
        .catch(() => {
        });
    });
  };

  const uploadProps: UploadProps = {
    action: '/jetlinks/file/static',
    headers: {
      'X-Access-Token': getAccessToken(),
    },
    showUploadList: false,
    onChange(info) {
      if (info.file.status === 'done') {
        setPhotoUrl(info.file.response.result);
        message.success('上传成功');
      }
    },
  };

  return (
    <PageHeaderWrapper>
      <Card title="基本信息" bordered={false}>
        <div className={styles.right}>
          <Spin spinning={false}>
            <div className={styles.baseView}>
              <div className={styles.left}>
                <Form labelCol={{span: 5}} wrapperCol={{span: 16}}>
                  <Row gutter={16}>
                    {basicForm.map(item => (
                      <Col key={item.key}>
                        <Form.Item label={item.label}>
                          {getFieldDecorator(item.key, item.options)(item.component)}
                        </Form.Item>
                      </Col>
                    ))}
                    {/* {(systemVersion === 'pro' ? basicForm : basicForm.filter(i => i.key !== 'storePolicy')).map(item => (
                      <Col key={item.key}>
                        <Form.Item label={item.label}>
                          {getFieldDecorator(item.key, item.options)(item.component)}
                        </Form.Item>
                      </Col>
                    ))} */}
                  </Row>
                </Form>
              </div>
              <div className={styles.right}>
                <>
                  <div className={styles.avatar_title}>图标</div>
                  <div className={styles.avatar}>
                    <Avatar size={144} src={photoUrl || props.data?.photoUrl || productImg}/>
                  </div>
                  <Upload {...uploadProps} showUploadList={false}>
                    <div className={styles.button_view}>
                      <Button>
                        <UploadOutlined/>
                        更换图片
                      </Button>
                    </div>
                  </Upload>
                </>
              </div>
            </div>
            <div
              style={{
                position: 'absolute',
                right: 0,
                bottom: 0,
                height: 32,
                lineHeight: 4,
                width: '100%',
                borderTop: '1px solid #e9e9e9',
                paddingRight: 16,
                background: '#fff',
                textAlign: 'right',
              }}
            >
              <Button
                onClick={() => {
                  router.push(`/device/product`);
                }}
                style={{marginRight: 8}}
              >
                返回
              </Button>
              <Button
                onClick={() => {
                  saveData();
                }}
                type="primary"
              >
                保存
              </Button>
            </div>
          </Spin>
        </div>
      </Card>
      {classifiedVisible && (
        <Classified
          choice={(item: any) => {
            const categoryId = item.categoryId;
            setFieldsValue({classifiedId: categoryId});
            setClassifiedData(item);
            setClassifiedVisible(false);
          }}
          close={() => {
            setClassifiedVisible(false);
          }}
          data={classifiedData}
        />
      )}
    </PageHeaderWrapper>
  );
}
Example #24
Source File: Detail.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Detail: React.FC<Props> = props => {
  const initState: State = {
    basicInfo: {},
    saveVisible: false,
    config: [],
    orgInfo: {},
    deviceCount: 0,
    spinning: true,
    units: {},
  };
  const {
    dispatch,
    location: { pathname },
  } = props;
  const [events, setEvents] = useState<any[]>([]);
  const [functions, setFunctions] = useState<any[]>([]);
  const [properties, setProperties] = useState<any[]>([]);
  const [tags, setTags] = useState<any[]>([]);
  const [basicInfo, setBasicInfo] = useState(initState.basicInfo);
  const [saveVisible, setSaveVisible] = useState(initState.saveVisible);
  const [config, setConfig] = useState(initState.config);
  const [orgInfo] = useState(initState.orgInfo);
  const [deviceCount, setDeviceCount] = useState(initState.deviceCount);
  const [spinning, setSpinning] = useState(initState.spinning);
  const [units, setUnits] = useState(initState.units);
  const [updateVisible, setUpdateVisible] = useState(false);
  const [productId, setProductId] = useState('');

  const handleSearch = (id?: string) => {
    const list = pathname.split('/');
    const temp = id || list[list.length - 1];
    dispatch({
      type: 'deviceProduct/queryById',
      payload: temp,
      callback: (r: any) => {
        if (r.status === 200) {
          const data = r.result;
          data.orgName = orgInfo[data.orgId];
          setBasicInfo(data);
          setSpinning(false);
          if (data.metadata) {
            const metadata = JSON.parse(data.metadata);
            setEvents(metadata.events);
            setFunctions(metadata.functions);
            setProperties(metadata.properties);
            setTags(metadata.tags);
          }
          // apis.deviceProdcut
          //   .protocolConfiguration(data.messageProtocol, data.transportProtocol)
          //   .then(resp => {
          //     setConfig(resp.result);
          //   });
          apis.deviceProdcut.productConfiguration(data.id).then(resp => {
            setConfig(resp.result);
          });
        }
      },
    });

    apis.deviceInstance
      .count(encodeQueryParam({ terms: { productId: id } }))
      .then(res => {
        if (res.status === 200) {
          setDeviceCount(res.result);
        }
      })
      .catch();
  };

  useEffect(() => {
    apis.deviceProdcut
      .queryOrganization()
      .then(res => {
        if (res.status === 200) {
          res.result.map((e: any) => (orgInfo[e.id] = e.name));
        }
      })
      .catch(() => {});

    apis.deviceProdcut
      .units()
      .then((response: any) => {
        if (response.status === 200) {
          setUnits(response.result);
        }
      })
      .catch(() => {});

    if (pathname.indexOf('save') > 0) {
      const list = pathname.split('/');
      handleSearch(list[list.length - 1]);
      setProductId(list[list.length - 1]);
    }
  }, []);

  const saveData = (item?: any) => {
    let data: Partial<DeviceProduct>;
    const metadata = JSON.stringify({ events, properties, functions, tags });

    // TODO 这个地方有疑惑,set数据之后此处数据还是未更新。原因待查
    if (item) {
      data = { ...item, metadata };
    } else {
      data = { ...basicInfo, metadata };
    }
    apis.deviceProdcut
      .saveOrUpdate(data)
      .then(r => {
        if (r.status === 200) {
          setSaveVisible(false);
          message.success('保存成功');
          const list = pathname.split('/');
          handleSearch(list[list.length - 1]);
        }
      })
      .catch(() => {});
  };

  const updateData = (type: string, item: any, onlySave: boolean) => {
    let metadata = JSON.stringify({ events, properties, functions, tags });
    if (type === 'event') {
      metadata = JSON.stringify({ events: item, properties, functions, tags });
    } else if (type === 'properties') {
      metadata = JSON.stringify({ events, properties: item, functions, tags });
    } else if (type === 'function') {
      metadata = JSON.stringify({ events, properties, functions: item, tags });
    } else if (type === 'tags') {
      metadata = JSON.stringify({ events, properties, functions, tags: item });
    }

    const data = { ...basicInfo, metadata };

    apis.deviceProdcut
      .saveOrUpdate(data)
      .then((re: any) => {
        if (re.status === 200) {
          message.success('保存成功');
          if (!onlySave) {
            deploy(data);
          }
        }
      })
      .catch(() => {})
      .finally(() => setBasicInfo({ ...data, state: onlySave ? basicInfo.state : 1 }));
  };

  const deploy = (record: any) => {
    setSpinning(true);
    dispatch({
      type: 'deviceProduct/deploy',
      payload: record.id,
      callback: response => {
        if (response.status === 200) {
          basicInfo.state = 1;
          setBasicInfo({ ...basicInfo });
          message.success('操作成功');
          setSpinning(false);
        }
      },
    });
  };

  const unDeploy = (record: any) => {
    setSpinning(true);
    dispatch({
      type: 'deviceProduct/unDeploy',
      payload: record.id,
      callback: response => {
        if (response.status === 200) {
          basicInfo.state = 0;
          setBasicInfo(basicInfo);
          message.success('操作成功');
          setSpinning(false);
        }
      },
    });
  };

  const updateInfo = (onlySave: boolean, item?: any) => {
    apis.deviceProdcut
      .update(item, basicInfo.id)
      .then((response: any) => {
        if (response.status === 200) {
          message.success('配置信息修改成功');
          setUpdateVisible(false);
          const list = pathname.split('/');
          handleSearch(list[list.length - 1]);
          if (!onlySave) {
            // deploy(basicInfo)
            setSpinning(true);
            dispatch({
              type: 'deviceProduct/deploy',
              payload: basicInfo.id,
              callback: res => {
                if (res.status === 200) {
                  basicInfo.state = 0;
                  setBasicInfo(basicInfo);
                  message.success('操作成功');
                  setSpinning(false);
                  const list = pathname.split('/');
                  handleSearch(list[list.length - 1]);
                }
              },
            });
          }
        }
      })
      .catch(() => {});
  };

  const content = (
    <div style={{ marginTop: 30 }}>
      <Descriptions column={4}>
        <Descriptions.Item label="设备数量">
          <div>
            {numeral(deviceCount).format('0,0')}
            <a
              style={{ marginLeft: 10 }}
              onClick={() => {
                router.push(`/device/instance?productId=${basicInfo.id}`);
              }}
            >
              查看
            </a>
          </div>
        </Descriptions.Item>
      </Descriptions>
    </div>
  );

  const titleInfo = (
    <Row>
      <div>
        <span>产品:{basicInfo.name}</span>
        <Badge
          style={{ marginLeft: 20 }}
          color={basicInfo.state === 1 ? 'green' : 'red'}
          text={basicInfo.state === 1 ? '已发布' : '未发布'}
        />
        {basicInfo.state === 1 ? (
          <Popconfirm
            title="确认停用?"
            onConfirm={() => {
              unDeploy(basicInfo);
            }}
          >
            <a style={{ fontSize: 12, marginLeft: 20 }}>停用</a>
          </Popconfirm>
        ) : (
          <Popconfirm
            title="确认发布?"
            onConfirm={() => {
              deploy(basicInfo);
            }}
          >
            <a style={{ fontSize: 12, marginLeft: 20 }}>发布</a>
          </Popconfirm>
        )}
      </div>
    </Row>
  );

  const action = (
    <Tooltip title="编辑产品信息后请重新应用配置">
      <Popconfirm
        title="确认重新应用该配置?"
        onConfirm={() => {
          deploy(basicInfo);
        }}
      >
        <Button icon="sync" type="primary">
          应用配置
        </Button>
      </Popconfirm>
    </Tooltip>
  );

  return (
    <Spin tip="加载中..." spinning={spinning}>
      <PageHeaderWrapper title={titleInfo} content={content} extra={action}>
        <Card>
          <Tabs>
            <Tabs.TabPane tab="产品信息" key="info">
              <Descriptions
                style={{ marginBottom: 20 }}
                bordered
                column={3}
                title={
                  <span>
                    产品信息
                    <Button
                      icon="edit"
                      style={{ marginLeft: 20 }}
                      type="link"
                      onClick={() => setSaveVisible(true)}
                    >
                      编辑
                    </Button>
                  </span>
                }
              >
                <Descriptions.Item label="产品ID" span={1}>
                  {basicInfo.id}
                </Descriptions.Item>
                {/*<Descriptions.Item label="产品名称" span={1}>
                  {basicInfo.name}
                </Descriptions.Item>*/}
                <Descriptions.Item label="所属品类" span={1}>
                  {basicInfo.classifiedName}
                </Descriptions.Item>
                <Descriptions.Item label="所属机构" span={1}>
                  {basicInfo.orgName}
                </Descriptions.Item>
                <Descriptions.Item label="消息协议" span={1}>
                  {basicInfo.protocolName || basicInfo.protocolId}
                </Descriptions.Item>
                <Descriptions.Item label="链接协议" span={1}>
                  {basicInfo.transportProtocol}
                </Descriptions.Item>
                <Descriptions.Item label="设备类型" span={1}>
                  {(basicInfo.deviceType || {}).text}
                </Descriptions.Item>
                <Descriptions.Item label="说明" span={3}>
                  {basicInfo.describe}
                </Descriptions.Item>
              </Descriptions>
              {config && config.length > 0 && (
                <div style={{ width: '100%' }}>
                  <Descriptions
                    title={
                      <span>
                        配置
                        <Button
                          icon="edit"
                          style={{ marginLeft: 20 }}
                          type="link"
                          onClick={() => setUpdateVisible(true)}
                        >
                          编辑
                        </Button>
                        {/* <Button
                          style={{ marginLeft: 10 }}
                          type="link"
                          onClick={() => setUpdateVisible(true)}
                        >
                          应用配置
                    </Button> */}
                      </span>
                    }
                  ></Descriptions>
                  {config.map((i: any) => (
                    <div style={{ marginBottom: '20px' }} key={i.name}>
                      <h3>{i.name}</h3>
                      <Descriptions bordered column={2} title="">
                        {i.properties &&
                          i.properties.map((item: any) => (
                            <Descriptions.Item
                              label={
                                item.description ? (
                                  <div>
                                    <span style={{ marginRight: '10px' }}>{item.name}</span>
                                    <Tooltip title={item.description}>
                                      <Icon type="question-circle-o" />
                                    </Tooltip>
                                  </div>
                                ) : (
                                  item.name
                                )
                              }
                              span={1}
                              key={item.property}
                            >
                              {basicInfo.configuration
                                ? item.type.type === 'password'
                                  ? basicInfo.configuration[item.property]?.length > 0
                                    ? '••••••'
                                    : null
                                  : basicInfo.configuration[item.property]
                                : null}
                            </Descriptions.Item>
                          ))}
                      </Descriptions>
                    </div>
                  ))}
                </div>
              )}
            </Tabs.TabPane>
            <Tabs.TabPane tab="物模型" key="metadata">
              <Definition
                basicInfo={basicInfo}
                eventsData={events}
                functionsData={functions}
                propertyData={properties}
                tagsData={tags}
                unitsData={units}
                saveEvents={(data: any, onlySave: boolean) => {
                  setEvents(data);
                  updateData('event', data, onlySave);
                }}
                saveFunctions={(data: any, onlySave: boolean) => {
                  setFunctions(data);
                  updateData('function', data, onlySave);
                }}
                saveProperty={(data: any[], onlySave: boolean) => {
                  setProperties(data);
                  updateData('properties', data, onlySave);
                }}
                saveTags={(data: any[], onlySave: boolean) => {
                  setTags(data);
                  updateData('tags', data, onlySave);
                }}
                update={() => handleSearch(productId)}
              />
            </Tabs.TabPane>
            <Tabs.TabPane tab="告警设置" key="metadata1">
              <Alarm
                target="product"
                productId={basicInfo.id}
                productName={basicInfo.name}
                targetId={basicInfo.id}
                metaData={basicInfo.metadata}
                name={basicInfo.name}
              />
            </Tabs.TabPane>
          </Tabs>
        </Card>

        {saveVisible && (
          <Save
            data={basicInfo}
            close={() => setSaveVisible(false)}
            save={(item: any) => {
              setBasicInfo(item);
              saveData(item);
            }}
          />
        )}

        {updateVisible && (
          <Configuration
            data={basicInfo}
            configuration={config}
            close={() => {
              setUpdateVisible(false);
            }}
            save={(onlySave: boolean, item: any) => {
              updateInfo(onlySave, item);
            }}
          />
        )}
      </PageHeaderWrapper>
    </Spin>
  );
}
Example #25
Source File: index-backups.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
DeviceModel: React.FC<Props> = props => {
  const { result } = props.deviceProduct;
  const initState: State = {
    data: result,
    searchParam: { pageSize: 10 },
    saveVisible: false,
  };

  const [searchParam, setSearchParam] = useState(initState.searchParam);
  const [saveVisible, setSaveVisible] = useState(initState.saveVisible);
  const [filterData, setFilterData] = useState({});
  const { dispatch } = props;

  const handleSearch = (params?: any) => {
    setSearchParam(params);
    dispatch({
      type: 'deviceProduct/query',
      payload: encodeQueryParam(params),
    });
  };

  const deploy = (record: any) => {
    dispatch({
      type: 'deviceProduct/deploy',
      payload: record.id,
      callback: response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        }
      },
    });
  };
  const unDeploy = (record: any) => {
    dispatch({
      type: 'deviceProduct/unDeploy',
      payload: record.id,
      callback: response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        }
      },
    });
  };

  const handleDelete = (params: any) => {
    dispatch({
      type: 'deviceProduct/remove',
      payload: params.id,
      callback: response => {
        if (response.status === 200) {
          message.success('删除成功');
          handleSearch(searchParam);
        }
      },
    });
  };

  const columns: ColumnProps<DeviceProduct>[] = [
    {
      title: 'ID',
      dataIndex: 'id',
      width: '250px',
    },
    {
      title: '产品名称',
      dataIndex: 'name',
    },
    {
      title: '类型',
      dataIndex: 'deviceType',
      width: '150px',
      align: 'center',
      render: (text: any) => (text || {}).text,
      sorter: true,
    },
    {
      title: '创建时间',
      dataIndex: 'createTime',
      width: '200px',
      align: 'center',
      render: (text: any) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
      sorter: true,
      defaultSortOrder: 'descend',
    },
    {
      title: '发布状态',
      dataIndex: 'state',
      align: 'center',
      filters: [{
        value: '0',
        text: '未发布'
      }, {
        value: '1',
        text: '已发布'
      }],
      render: (text: any) => {
        const color = text === 0 ? 'red' : 'green';
        const status = text === 0 ? '未发布' : '已发布';
        return <Badge color={color} text={status} />;
      },
    },
    {
      title: '操作',
      width: '300px',
      align: 'center',
      render: (record: DeviceProduct) => (
        <Fragment>
          <a
            onClick={() => {
              router.push(`/device/product/save/${record.id}`);
            }}
          >
            查看
          </a>
          <Divider type="vertical" />
          {record.state === 0 ? (
            <span>
              <Popconfirm
                title="确认发布?"
                onConfirm={() => {
                  deploy(record);
                }}
              >
                <a>发布</a>
              </Popconfirm>
              <Divider type="vertical" />
              <Popconfirm title="确定删除?" onConfirm={() => handleDelete(record)}>
                <a>删除</a>
              </Popconfirm>
            </span>
          ) : (
              <Popconfirm
                title="确认停用"
                onConfirm={() => {
                  unDeploy(record);
                }}
              >
                <a>停用</a>
              </Popconfirm>
            )}
          <Divider type="vertical" />
          <a
            onClick={() => {
              downloadObject(record, '产品');
            }}
          >
            下载配置
          </a>
          <Divider type="vertical" />
          <a
            onClick={() => {
              router.push(`/device/instance?productId=${record.id}`);
            }}
          >
            查看设备
          </a>
        </Fragment>
      ),
    },
  ];

  useEffect(() => {
    handleSearch(searchParam);
  }, []);

  const handeSave = (record: any) => {
    dispatch({
      type: 'deviceProduct/insert',
      payload: record,
      callback: response => {
        if (response.status === 200) {
          setSaveVisible(false);
          message.success('保存成功');
          router.push(`/device/product/save/${record.id}`);
        }
      },
    });
  };



  const onTableChange = (
    pagination: PaginationConfig,
    filters: any,
    sorter: SorterResult<DeviceProduct>
  ) => {
    const tempFilter = converObjectKey(filters, { state: 'state$IN' });
    setFilterData(tempFilter);
    handleSearch({
      pageIndex: Number(pagination.current) - 1,
      pageSize: pagination.pageSize,
      terms: { ...searchParam, ...tempFilter },
      sorts: sorter,
    });
  };
  const uploadProps: UploadProps = {
    accept: '.json',
    action: '/jetlinks/file/static',
    headers: {
      'X-Access-Token': getAccessToken(),
    },
    showUploadList: false,
    onChange(info) {
      if (info.file.status === 'done') {
        const fileUrl = info.file.response.result;
        request(fileUrl, { method: 'GET' }).then(e => {
          if (e || e !== null) {
            dispatch({
              type: 'deviceProduct/insert',
              payload: e,
              callback: (response: any) => {
                if (response.status === 200) {
                  message.success('导入成功');
                  handleSearch(searchParam);
                }
              },
            });
          }
        }).catch(() => {
          message.error('导入配置失败');
        });
      }
    },
  };


  // 消息协议
  const [protocolSupports, setProtocolSupports] = useState([]);

  useEffect(() => {
    apis.deviceProdcut
      .protocolSupport()
      .then(response => {
        if (response.status === 200) {
          setProtocolSupports(response.result.map((i: any) => ({ id: i.id, name: i.name })));
        }
      })
      .catch(() => { });
  }, []);

  return (
    <PageHeaderWrapper title="产品管理">
      <Card bordered={false}>
        <div className={styles.tableList}>
          <div>
            <SearchForm
              search={(params: any) => {
                handleSearch({
                  terms: { ...params, ...filterData },
                  pageSize: 10,
                  sorts: searchParam.sorts
                });
              }}
              formItems={[{
                label: '产品名称',
                key: 'name$LIKE',
                type: 'string',
              },
              {
                label: '设备类型',
                key: 'deviceType',
                type: 'list',
                props: {
                  data: [
                    { id: 'gateway', name: '网关' },
                    { id: 'device', name: '设备' }
                  ]
                }
              },
              {
                label: '消息协议',
                key: 'messageProtocol',
                type: 'list',
                props: {
                  data: protocolSupports
                }
              },]}
            />
          </div>
          <div className={styles.tableListOperator}>
            <Button icon="plus" type="primary" onClick={() => setSaveVisible(true)}>
              新建
            </Button>
            <Divider type="vertical" />
            <Upload {...uploadProps}>
              <Button>
                <Icon type="upload" /> 导入配置
              </Button>
            </Upload>
          </div>
          <div className={styles.StandardTable}>
            <Table
              loading={props.loading}
              dataSource={(result || {}).data}
              columns={columns}
              rowKey='id'
              onChange={onTableChange}
              pagination={{
                current: result.pageIndex + 1,
                total: result.total,
                pageSize: result.pageSize,
                showQuickJumper: true,
                showSizeChanger: true,
                pageSizeOptions: ['10', '20', '50', '100'],
                showTotal: (total: number) => (
                  `共 ${total} 条记录 第  ${
                  result.pageIndex + 1
                  }/${
                  Math.ceil(result.total / result.pageSize)
                  }页`
                ),
              }}
            />
          </div>
        </div>
      </Card>
      {saveVisible && <Save close={() => setSaveVisible(false)} save={item => handeSave(item)} />}
    </PageHeaderWrapper>
  );
}
Example #26
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
DeviceInstancePage: React.FC<Props> = props => {
  const { result } = props.deviceInstance;
  const { dispatch, location } = props;

  const map = new Map();
  map.set('id', 'id$like');
  map.set('name', 'name$like');
  map.set('orgId', 'orgId$in');
  map.set('devTag', 'id$dev-tag');
  map.set('devBind', 'id$dev-bind$any');
  map.set('devProd', 'productId$dev-prod-cat');
  map.set('productId', 'productId');

  const initState: State = {
    data: result,
    searchParam: {
      pageSize: 10,
      terms: location?.query?.terms,
      sorts: {
        order: 'desc',
        field: 'id',
      },
    },
    addVisible: false,
    currentItem: {},
    processVisible: false,
    importLoading: false,
    action: '',
    deviceCount: {
      notActiveCount: 0,
      offlineCount: 0,
      onlineCount: 0,
      deviceTotal: 0,
      loading: true,
    },
    productList: [],
    deviceIdList: [],
  };

  const [searchParam, setSearchParam] = useState(initState.searchParam);
  const [addVisible, setAddVisible] = useState(initState.addVisible);
  const [currentItem, setCurrentItem] = useState(initState.currentItem);
  const [importLoading, setImportLoading] = useState(initState.importLoading);
  const [action, setAction] = useState(initState.action);
  const [productList, setProductList] = useState(initState.productList);
  const [product, setProduct] = useState<string>();
  const [deviceCount, setDeviceCount] = useState(initState.deviceCount);
  const [deviceImport, setDeviceImport] = useState(false);
  const [deviceExport, setDeviceExport] = useState(false);
  const [deviceIdList, setDeviceIdLIst] = useState(initState.deviceIdList);

  const statusMap = new Map();
  statusMap.set('在线', 'success');
  statusMap.set('离线', 'error');
  statusMap.set('未激活', 'processing');
  statusMap.set('online', 'success');
  statusMap.set('offline', 'error');
  statusMap.set('notActive', 'processing');

  const handleSearch = (params?: any) => {
    setSearchParam(params);
    dispatch({
      type: 'deviceInstance/query',
      payload: encodeQueryParam(params),
    });
  };

  const delelteInstance = (record: any) => {
    apis.deviceInstance
      .remove(record.id)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          deviceIdList.splice(0, deviceIdList.length);
          handleSearch(searchParam);
        }
      })
      .catch(() => {});
  };

  const changeDeploy = (record: any) => {
    apis.deviceInstance
      .changeDeploy(record.id)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          deviceIdList.splice(0, deviceIdList.length);
          handleSearch(searchParam);
        }
      })
      .catch(() => {});
  };

  const unDeploy = (record: any) => {
    apis.deviceInstance
      .unDeploy(record.id)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          deviceIdList.splice(0, deviceIdList.length);
          handleSearch(searchParam);
        }
      })
      .catch(() => {});
  };
  const columns: ColumnProps<DeviceInstance>[] = [
    {
      title: 'ID',
      dataIndex: 'id',
    },
    {
      title: '设备名称',
      dataIndex: 'name',
      ellipsis: true,
    },
    {
      title: '产品名称',
      dataIndex: 'productName',
      ellipsis: true,
    },
    {
      title: '注册时间',
      dataIndex: 'registryTime',
      width: '200px',
      render: (text: any) => (text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : '/'),
      sorter: true,
    },
    {
      title: '状态',
      dataIndex: 'state',
      width: '90px',
      render: record =>
        record ? <Badge status={statusMap.get(record.value)} text={record.text} /> : '',
      filters: [
        {
          text: '未启用',
          value: 'notActive',
        },
        {
          text: '离线',
          value: 'offline',
        },
        {
          text: '在线',
          value: 'online',
        },
      ],
      filterMultiple: false,
    },
    {
      title: '说明',
      dataIndex: 'describe',
      width: '15%',
      ellipsis: true,
    },
    {
      title: '操作',
      width: '200px',
      align: 'center',
      render: (record: any) => (
        <Fragment>
          <a
            onClick={() => {
              router.push(`/device/instance/save/${record.id}`);
            }}
          >
            查看
          </a>
          <Divider type="vertical" />
          <a
            onClick={() => {
              setCurrentItem(record);
              setAddVisible(true);
            }}
          >
            编辑
          </a>
          <Divider type="vertical" />
          {record.state?.value === 'notActive' ? (
            <span>
              <Popconfirm
                title="确认启用?"
                onConfirm={() => {
                  changeDeploy(record);
                }}
              >
                <a>启用</a>
              </Popconfirm>
              <Divider type="vertical" />
              <Popconfirm
                title="确认删除?"
                onConfirm={() => {
                  delelteInstance(record);
                }}
              >
                <a>删除</a>
              </Popconfirm>
            </span>
          ) : (
            <Popconfirm
              title="确认禁用设备?"
              onConfirm={() => {
                unDeploy(record);
              }}
            >
              <a>禁用</a>
            </Popconfirm>
          )}
        </Fragment>
      ),
    },
  ];

  const stateCount = (productId: string) => {
    const map = {
      notActiveCount: 0,
      offlineCount: 0,
      onlineCount: 0,
      deviceTotal: 0,
      loading: true,
    };

    apis.deviceInstance
      .count(
        encodeQueryParam({
          terms: {
            state: 'notActive',
            productId,
            ...location?.query?.terms,
            ...(location?.query.iop && JSON.parse(location?.query.iop)?.terms),
          },
        }),
      )
      .then(res => {
        if (res.status === 200) {
          map.notActiveCount = res.result;
          setDeviceCount({ ...map });
        }
      })
      .catch();
    apis.deviceInstance
      .count(
        encodeQueryParam({
          terms: {
            state: 'offline',
            productId,
            ...location?.query?.terms,
            ...(location?.query.iop && JSON.parse(location?.query.iop)?.terms),
          },
        }),
      )
      .then(res => {
        if (res.status === 200) {
          map.offlineCount = res.result;
          setDeviceCount({ ...map });
        }
      })
      .catch();
    apis.deviceInstance
      .count(
        encodeQueryParam({
          terms: {
            state: 'online',
            productId,
            ...location?.query?.terms,
            ...(location?.query.iop && JSON.parse(location?.query.iop)?.terms),
          },
        }),
      )
      .then(res => {
        if (res.status === 200) {
          map.onlineCount = res.result;
          setDeviceCount({ ...map });
        }
      })
      .catch();
    apis.deviceInstance
      .count(
        encodeQueryParam({
          terms: {
            productId,
            ...location?.query?.terms,
            ...(location?.query.iop && JSON.parse(location?.query.iop)?.terms),
          },
        }),
      )
      .then(res => {
        if (res.status === 200) {
          map.deviceTotal = res.result;
          map.loading = false;
          setDeviceCount({ ...map });
        }
      })
      .catch();
  };

  useEffect(() => {
    // 获取下拉框数据
    apis.deviceProdcut
      .queryNoPagin(
        encodeQueryParam({
          paging: false,
        }),
      )
      .then(e => {
        setProductList(e.result);
      })
      .catch(() => {});

    const query: any = getPageQuery();
    if (query.hasOwnProperty('productId')) {
      const { productId } = query;
      setProduct(productId);
      handleSearch({
        terms: {
          productId: query.productId,
        },
        pageSize: 10,
      });
      stateCount(productId);
    } else if (location?.query) {
      let key = Object.keys(location?.query)[0];
      let params = {};
      params[map.get(key)] = location?.query[key];
      handleSearch({
        terms: { ...params, ...(location?.query.iop && JSON.parse(location?.query.iop)?.terms) },
        pageSize: 10,
        sorts: searchParam.sorts,
      });
      stateCount('');
    } else {
      handleSearch(searchParam);
      stateCount('');
    }
  }, []);

  const onTableChange = (
    pagination: PaginationConfig,
    filters: any,
    sorter: SorterResult<DeviceInstance>,
  ) => {
    let { terms } = searchParam;
    if (filters.state) {
      if (terms) {
        terms.state = filters.state[0];
      } else {
        terms = {
          state: filters.state[0],
        };
      }
    }
    handleSearch({
      pageIndex: Number(pagination.current) - 1,
      pageSize: pagination.pageSize,
      terms,
      sorts: sorter,
    });
  };

  const [processVisible, setProcessVisible] = useState(false);

  const [api, setAPI] = useState<string>('');

  const getSearchParam = () => {
    const data = encodeQueryParam(searchParam);
    let temp = '';
    Object.keys(data).forEach((i: string) => {
      if (data[i] && i !== 'pageSize' && i !== 'pageIndex') {
        temp += `${i}=${data[i]}&`;
      }
    });
    return encodeURI(temp.replace(/%/g, '%'));
  };
  // 激活全部设备
  const startImport = () => {
    setProcessVisible(true);
    const activeAPI = `/jetlinks/device-instance/deploy?${getSearchParam()}:X_Access_Token=${getAccessToken()} `;
    setAPI(activeAPI);
    setAction('active');
  };

  const startSync = () => {
    setProcessVisible(true);
    const syncAPI = `/jetlinks/device-instance/state/_sync/?${getSearchParam()}:X_Access_Token=${getAccessToken()}`;
    setAPI(syncAPI);
    setAction('sync');
  };

  const activeDevice = () => {
    Modal.confirm({
      title: `确认激活全部设备`,
      okText: '确定',
      okType: 'primary',
      cancelText: '取消',
      onOk() {
        startImport();
      },
    });
  };

  const syncDevice = () => {
    Modal.confirm({
      title: '确定同步设备真实状态?',
      okText: '确定',
      okType: 'primary',
      cancelText: '取消',
      onOk() {
        // 同步设备
        startSync();
      },
    });
  };

  const onDeviceProduct = (value: string) => {
    let { terms } = searchParam;
    if (terms) {
      terms.productId = value;
    } else {
      terms = {
        productId: value,
      };
    }

    handleSearch({
      pageIndex: searchParam.pageIndex,
      pageSize: searchParam.pageSize,
      terms,
      sorts: searchParam.sorter,
    });
    stateCount(value);
  };

  const rowSelection = {
    onChange: (selectedRowKeys: any) => {
      setDeviceIdLIst(selectedRowKeys);
    },
  };

  const _delete = (deviceId: any[]) => {
    Modal.confirm({
      title: `确认删除选中设备`,
      okText: '确定',
      okType: 'primary',
      cancelText: '取消',
      onOk() {
        apis.deviceInstance
          ._delete(deviceId)
          .then(response => {
            if (response.status === 200) {
              message.success('成功删除选中设备');
              deviceIdList.splice(0, deviceIdList.length);
              handleSearch(searchParam);
            }
          })
          .catch(() => {});
      },
    });
  };

  const _unDeploy = (deviceId: any[]) => {
    Modal.confirm({
      title: `确认注销选中设备`,
      okText: '确定',
      okType: 'primary',
      cancelText: '取消',
      onOk() {
        apis.deviceInstance
          ._unDeploy(deviceId)
          .then(response => {
            if (response.status === 200) {
              message.success('成功注销选中设备');
              deviceIdList.splice(0, deviceIdList.length);
              handleSearch(searchParam);
            }
          })
          .catch(() => {});
      },
    });
  };

  const _deploy = (deviceId: any[]) => {
    Modal.confirm({
      title: `确认激活选中设备`,
      okText: '确定',
      okType: 'primary',
      cancelText: '取消',
      onOk() {
        apis.deviceInstance
          ._deploy(deviceId)
          .then(response => {
            if (response.status === 200) {
              message.success('成功激活选中设备');
              deviceIdList.splice(0, deviceIdList.length);
              handleSearch(searchParam);
            }
          })
          .catch(() => {});
      },
    });
  };

  const Info: FC<{
    title: React.ReactNode;
    value: React.ReactNode;
  }> = ({ title, value }) => (
    <div>
      <span>{title}</span>
      <p style={{ fontSize: '26px' }}>{value}</p>
    </div>
  );

  const menu = (
    <Menu>
      <Menu.Item key="1">
        <Button
          icon="download"
          type="default"
          onClick={() => {
            setDeviceExport(true);
          }}
        >
          批量导出设备
        </Button>
      </Menu.Item>
      <Menu.Item key="2">
        <Button
          icon="upload"
          onClick={() => {
            setDeviceImport(true);
          }}
        >
          批量导入设备
        </Button>
      </Menu.Item>
      {deviceIdList.length > 0 && (
        <Menu.Item key="3">
          <Button
            icon="delete"
            onClick={() => {
              _delete(deviceIdList);
            }}
          >
            删除选中设备
          </Button>
        </Menu.Item>
      )}
      {deviceIdList.length > 0 && (
        <Menu.Item key="6">
          <Button
            icon="stop"
            onClick={() => {
              _unDeploy(deviceIdList);
            }}
          >
            注销选中设备
          </Button>
        </Menu.Item>
      )}

      {deviceIdList.length > 0 ? (
        <Menu.Item key="4">
          <Button icon="check-circle" type="danger" onClick={() => _deploy(deviceIdList)}>
            激活选中设备
          </Button>
        </Menu.Item>
      ) : (
        <Menu.Item key="4">
          <Button icon="check-circle" type="danger" onClick={() => activeDevice()}>
            激活全部设备
          </Button>
        </Menu.Item>
      )}

      <Menu.Item key="5">
        <Button icon="sync" type="danger" onClick={() => syncDevice()}>
          同步设备状态
        </Button>
      </Menu.Item>
    </Menu>
  );

  return (
    <PageHeaderWrapper title="设备管理">
      <div className={styles.standardList}>
        <Card bordered={false} style={{ height: 95 }}>
          <Spin spinning={deviceCount.loading}>
            <Row>
              <Col sm={7} xs={24}>
                <Select
                  placeholder="选择产品"
                  showSearch
                  optionFilterProp='label'
                  allowClear
                  style={{ width: '70%', marginTop: 7 }}
                  value={product}
                  onChange={(value: string) => {
                    let key = Object.keys(location?.query)[0];
                    let params = {};
                    if (location?.query) {
                      params[key] = location?.query[key];
                    }
                    params['productId'] = value;
                    router.push({ pathname: `/device/instance`, query: params });
                    setProduct(() => value);
                    setDeviceCount({ loading: true });
                    onDeviceProduct(value);
                  }}
                >
                  {productList?.map(item => (
                    <Select.Option key={item.id} label={item.name}>{item.name}</Select.Option>
                  ))}
                </Select>
              </Col>
              <Col sm={4} xs={24}>
                <Info title="全部设备" value={numeral(deviceCount.deviceTotal).format('0,0')} />
              </Col>
              <Col sm={4} xs={24}>
                <Info
                  title={<Badge status={statusMap.get('online')} text="在线" />}
                  value={numeral(deviceCount.onlineCount).format('0,0')}
                />
              </Col>
              <Col sm={4} xs={24}>
                <Info
                  title={<Badge status={statusMap.get('offline')} text="离线" />}
                  value={numeral(deviceCount.offlineCount).format('0,0')}
                />
              </Col>
              <Col sm={4} xs={24}>
                <Info
                  title={<Badge status={statusMap.get('notActive')} text="未启用" />}
                  value={numeral(deviceCount.notActiveCount).format('0,0')}
                />
              </Col>
              <Col sm={1} xs={24}>
                <Tooltip title="刷新">
                  <Icon
                    type="sync"
                    style={{ fontSize: 20 }}
                    onClick={() => {
                      setDeviceCount({ loading: true });
                      stateCount(product);
                    }}
                  />
                </Tooltip>
              </Col>
            </Row>
          </Spin>
        </Card>
        <br />
        <Card bordered={false}>
          <div className={styles.tableList}>
            <div className={styles.tableListForm}>
              <Search
                type={'device-instance'}
                search={(params: any) => {
                  if (Object.keys(params).length === 0) {
                    deviceIdList.splice(0, deviceIdList.length);
                  }
                  if (product) {
                    params.productId = product;
                  }
                  params.state = searchParam.terms?.state;
                  handleSearch({ terms: params, pageSize: 10, sorts: searchParam.sorts });
                }}
              />
            </div>
            <div className={styles.tableListOperator}>
              <Button
                icon="plus"
                type="primary"
                onClick={() => {
                  setCurrentItem({});
                  setAddVisible(true);
                }}
              >
                添加设备
              </Button>
              <Divider type="vertical" />
              <Dropdown overlay={menu}>
                <Button icon="menu">
                  其他批量操作
                  <Icon type="down" />
                </Button>
              </Dropdown>
            </div>
            <div className={styles.StandardTable}>
              <Table
                loading={props.loading}
                columns={columns}
                dataSource={(result || {}).data}
                rowKey="id"
                onChange={onTableChange}
                rowSelection={{
                  type: 'checkbox',
                  ...rowSelection,
                }}
                pagination={{
                  current: result.pageIndex + 1,
                  total: result.total,
                  pageSize: result.pageSize,
                  showQuickJumper: true,
                  showSizeChanger: true,
                  pageSizeOptions: ['10', '20', '50', '100'],
                  showTotal: (total: number) =>
                    `共 ${total} 条记录 第  ${result.pageIndex + 1}/${Math.ceil(
                      result.total / result.pageSize,
                    )}页`,
                }}
              />
            </div>
          </div>
        </Card>
        {addVisible && (
          <Save
            data={currentItem}
            close={() => {
              setAddVisible(false);
              setCurrentItem({});
            }}
          />
        )}
        {(processVisible || importLoading) && (
          <Process
            api={api}
            action={action}
            closeVisible={() => {
              setProcessVisible(false);
              setImportLoading(false);
              handleSearch(searchParam);
            }}
          />
        )}
        {deviceImport && (
          <Import
            productId={product}
            close={() => {
              setDeviceImport(false);
              handleSearch(searchParam);
            }}
          />
        )}
        {deviceExport && (
          <Export
            productId={product}
            searchParam={searchParam}
            close={() => {
              setDeviceExport(false);
              handleSearch(searchParam);
            }}
          />
        )}
      </div>
    </PageHeaderWrapper>
  );
}
Example #27
Source File: index-backups.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
DeviceInstancePage: React.FC<Props> = props => {
  const { result } = props.deviceInstance;
  const initState: State = {
    data: result,
    searchParam: { pageSize: 10 },
    addVisible: false,
    currentItem: {},
    processVisible: false,
    importLoading: false,
    action: '',
  };

  const [searchParam, setSearchParam] = useState(initState.searchParam);
  const [addVisible, setAddvisible] = useState(initState.addVisible);
  const [currentItem, setCurrentItem] = useState(initState.currentItem);
  const [importLoading, setImportLoading] = useState(initState.importLoading);
  const [action, setAction] = useState(initState.action);
  const { dispatch } = props;

  const statusMap = new Map();
  statusMap.set('在线', 'success');
  statusMap.set('离线', 'error');
  statusMap.set('未激活', 'processing');

  const handleSearch = (params?: any) => {
    setSearchParam(params);
    dispatch({
      type: 'deviceInstance/query',
      payload: encodeQueryParam(params),
    });
  };

  const delelteInstance = (record: any) => {
    apis.deviceInstance
      .remove(record.id)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        }
      })
      .catch(() => {});
  };

  const changeDeploy = (record: any) => {
    apis.deviceInstance
      .changeDeploy(record.id)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        }
      })
      .catch(() => {});
  };

  const unDeploy = (record: any) => {
    apis.deviceInstance
      .unDeploy(record.id)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        }
      })
      .catch(() => {});
  };
  const columns: ColumnProps<DeviceInstance>[] = [
    {
      title: 'ID',
      dataIndex: 'id',
    },
    {
      title: '设备名称',
      dataIndex: 'name',
    },
    {
      title: '产品名称',
      dataIndex: 'productName',
    },
    {
      title: '注册时间',
      dataIndex: 'registryTime',
      width: '200px',
      render: (text: any) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
      sorter: true,
      defaultSortOrder: 'descend',
    },
    {
      title: '状态',
      dataIndex: 'state',
      render: record =>
        record ? <Badge status={statusMap.get(record.text)} text={record.text} /> : '',
    },
    {
      title: '描述',
      dataIndex: 'describe',
    },
    {
      title: '操作',
      width: '200px',
      align: 'center',
      render: (record: any) => (
        <Fragment>
          <a
            onClick={() => {
              router.push(`/device/instance/save/${record.id}`);
            }}
          >
            查看
          </a>
          <Divider type="vertical" />
          <a
            onClick={() => {
              setCurrentItem(record);
              setAddvisible(true);
            }}
          >
            编辑
          </a>
          <Divider type="vertical" />
          {record.state?.value === 'notActive' ? (
            <span>
              <Popconfirm
                title="确认激活?"
                onConfirm={() => {
                  changeDeploy(record);
                }}
              >
                <a>激活</a>
              </Popconfirm>
              <Divider type="vertical" />
              <Popconfirm
                title="确认删除?"
                onConfirm={() => {
                  delelteInstance(record);
                }}
              >
                <a>删除</a>
              </Popconfirm>
            </span>
          ) : (
            <Popconfirm
              title="确认注销设备?"
              onConfirm={() => {
                unDeploy(record);
              }}
            >
              <a>注销</a>
            </Popconfirm>
          )}
        </Fragment>
      ),
    },
  ];

  useEffect(() => {
    handleSearch(searchParam);
  }, []);

  const saveDeviceInstance = (item: any) => {
    dispatch({
      type: 'deviceInstance/update',
      payload: encodeQueryParam(item),
      callback: (response:any) => {
        if (response.status === 200) {
          message.success('保存成功');
          setAddvisible(false);
          router.push(`/device/instance/save/${item.id}`);
        }
      },
    });
  };

  const onTableChange = (
    pagination: PaginationConfig,
    filters: any,
    sorter: SorterResult<DeviceInstance>,
  ) => {
    handleSearch({
      pageIndex: Number(pagination.current) - 1,
      pageSize: pagination.pageSize,
      terms: searchParam.terms,
      sorts: sorter,
    });
  };

  const [processVisible, setProcessVisible] = useState(false);

  const [api, setAPI] = useState<string>('');

  const getSearchParam = () => {
    const data = encodeQueryParam(searchParam);
    let temp = '';
    Object.keys(data).forEach((i: string) => {
      if (data[i] && i !== 'pageSize' && i !== 'pageIndex') {
        temp += `${i}=${data[i]}&`;
      }
    });
    return encodeURI(temp.replace(/%/g, '%'));
  };
  // 激活全部设备
  const startImport = () => {
    // let dt = 0;
    setProcessVisible(true);
    const activeAPI = `/jetlinks/device-instance/deploy?${getSearchParam()}:X_Access_Token=${getAccessToken()} `;
    setAPI(activeAPI);
    setAction('active');
  };

  const startSync = () => {
    setProcessVisible(true);
    const syncAPI = `/jetlinks/device-instance/state/_sync/?${getSearchParam()}:X_Access_Token=${getAccessToken()}`;
    setAPI(syncAPI);
    setAction('sync');
  };

  const activeDevice = () => {
    Modal.confirm({
      title: `确认激活全部设备`,
      okText: '确定',
      okType: 'primary',
      cancelText: '取消',
      onOk() {
        startImport();
      },
    });
  };

  const syncDevice = () => {
    Modal.confirm({
      title: '确定同步设备真实状态?',
      okText: '确定',
      okType: 'primary',
      cancelText: '取消',
      onOk() {
        // 同步设备
        startSync();
      },
    });
  };

  const [uploading, setUploading] = useState(false);
  const exportDevice = () => {
    const formElement = document.createElement('form');
    formElement.style.display = 'display:none;';
    formElement.method = 'post';
    formElement.action = `/jetlinks/device-instance/export?:X_Access_Token=${getAccessToken()}`;
    const params = encodeQueryParam(searchParam);
    Object.keys(params).forEach((key: string) => {
      const inputElement = document.createElement('input');
      inputElement.type = 'hidden';
      inputElement.name = key;
      inputElement.value = params[key];
      formElement.appendChild(inputElement);
    });
    document.body.appendChild(formElement);
    formElement.submit();
    document.body.removeChild(formElement);
  };

  const uploadProps: UploadProps = {
    accept: '.xlsx, .xls',
    action: '/jetlinks/file/static',
    headers: {
      'X-Access-Token': getAccessToken(),
    },
    showUploadList: false,
    onChange(info) {
      if (info.file.status === 'done') {
        setUploading(false);
        const fileUrl = info.file.response.result;
        const url = `/jetlinks/device-instance/import?fileUrl=${fileUrl}&:X_Access_Token=${getAccessToken()}`;
        setAPI(url);
        setAction('import');
        setImportLoading(true);
      }
      if (info.file.status === 'uploading') {
        setUploading(true);
      }
    },
  };

  return (
    <PageHeaderWrapper title="设备管理">
      <Spin spinning={uploading} tip="上传中...">
        <Card bordered={false}>
          <div className={styles.tableList}>
            <div className={styles.tableListForm}>
              <Search
                search={(params: any) => {
                  setSearchParam(params);
                  handleSearch({ terms: params, pageSize: 10 });
                }}
              />
            </div>
            <div className={styles.tableListOperator}>
              <Button
                icon="plus"
                type="primary"
                onClick={() => {
                  setCurrentItem({});
                  setAddvisible(true);
                }}
              >
                新建
              </Button>

              <Divider type="vertical" />

              <Button href={template} download="设备模版" icon="download">
                下载模版
              </Button>
              <Divider type="vertical" />
              <Button icon="download" type="default" onClick={() => exportDevice()}>
                导出设备
              </Button>
              <Divider type="vertical" />
              <Upload {...uploadProps}>
                <Button icon="upload">导入设备</Button>
              </Upload>
              <Divider type="vertical" />
              <Button icon="check-circle" type="danger" onClick={() => activeDevice()}>
                激活全部设备
              </Button>
              <Divider type="vertical" />
              <Button icon="sync" type="danger" onClick={() => syncDevice()}>
                同步设备状态
              </Button>
            </div>
            <div className={styles.StandardTable}>
              <Table
                loading={props.loading}
                columns={columns}
                dataSource={(result || {}).data}
                rowKey="id"
                onChange={onTableChange}
                pagination={{
                  current: result.pageIndex + 1,
                  total: result.total,
                  pageSize: result.pageSize,
                  showQuickJumper: true,
                  showSizeChanger: true,
                  pageSizeOptions: ['10', '20', '50', '100'],
                  showTotal: (total: number) =>
                    `共 ${total} 条记录 第  ${result.pageIndex + 1}/${Math.ceil(
                      result.total / result.pageSize,
                    )}页`,
                }}
              />
            </div>
          </div>
        </Card>
        {addVisible && (
          <Save
            data={currentItem}
            close={() => {
              setAddvisible(false);
              setCurrentItem({});
            }}
            save={(item: any) => {
              saveDeviceInstance(item);
            }}
          />
        )}
        {(processVisible || importLoading) && (
          <Process
            api={api}
            action={action}
            closeVisible={() => {
              setProcessVisible(false);
              setImportLoading(false);
              handleSearch(searchParam);
            }}
          />
        )}
      </Spin>
    </PageHeaderWrapper>
  );
}
Example #28
Source File: index.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Editor: React.FC<Props> = props => {
  const {
    location: { pathname },
  } = props;

  const initState: State = {
    activeKey: 'info',
    data: {},
    logs: {},
    orgInfo: {},
    config: {},
    spinning: true,
  };
  const [activeKey, setActiveKey] = useState(initState.activeKey);
  const [data, setData] = useState(initState.data);
  const [id, setId] = useState();
  const [config, setConfig] = useState(initState.config);
  const [orgInfo] = useState(initState.orgInfo);
  const [spinning, setSpinning] = useState(initState.spinning);
  const [tableList, setTableList] = useState();
  const [events, setEvents] = useState<any[]>([]);
  const [functions, setFunctions] = useState<any[]>([]);
  const [properties, setProperties] = useState<any[]>([]);
  const [tags, setTags] = useState<any[]>([]);
  const [units, setUnits] = useState({});
  const [deviceId, setDeviceId] = useState<string | null>();
  let deviceStatus: any;

  const tabList = [
    {
      key: 'info',
      tab: '实例信息',
    },
    {
      key: 'metadata',
      tab: '物模型',
    },
    {
      key: 'log',
      tab: '日志管理',
    },
    {
      key: 'alarm',
      tab: '告警设置',
    },
    {
      key: 'visualization',
      tab: '可视化',
    },
    {
      key: 'shadow',
      tab: '设备影子',
    },
  ];

  const subscribeDeviceState = (deviceData: any, deviceId: string) => {
    deviceStatus && deviceStatus.unsubscribe();

    deviceStatus = getWebsocket(
      `instance-editor-info-status-${deviceId}`,
      `/dashboard/device/status/change/realTime`,
      {
        deviceId: deviceId,
      },
    ).subscribe((resp: any) => {
      const { payload } = resp;
      if(payload.value.type === 'online'){
        deviceData.state = { value: 'online', text: '在线' }
      } else if(payload.value.type === 'notActive'){
        deviceData.state = { value: 'notActive', text: '未启用' }
      }else if(payload.value.type === 'offline'){
        deviceData.state = {
          value: 'offline',
          text: '离线'
        }
      }
      if (payload.value.type === 'online') {
        deviceData.onlineTime = payload.timestamp;
      } else {
        deviceData.offlineTime = payload.timestamp;
      }
      setData({ ...deviceData });
    });
  };

  const getInfo = (deviceId: string) => {
    setSpinning(true);
    setDeviceId(deviceId);
    apis.deviceInstance
      .info(deviceId)
      .then((response: any) => {
        if (response.status === 200) {
          const deviceData = response.result;
          if (deviceData.orgId) {
            deviceData.orgName = orgInfo[deviceData.orgId];
          }
          setData({ ...deviceData });
          
          if (deviceData.metadata) {
            const metadata = JSON.parse(deviceData.metadata);
            setEvents(metadata.events);
            setFunctions(metadata.functions);
            setProperties(metadata.properties);
            setTags(metadata.tags);
          }
          subscribeDeviceState(deviceData, deviceId);
          if (deviceData.metadata) {
            const deriveMetadata = JSON.parse(deviceData.metadata);
            if (
              (deriveMetadata.functions || []).length > 0 &&
              deviceData.state?.value !== 'notActive'
            ) {
              tabList.splice(2, 0, {
                key: 'functions',
                tab: '设备功能',
              });
            }
          }

          if (deviceData.deviceType.value === 'gateway') {
            tabList.push({
              key: 'gateway',
              tab: '子设备管理',
            });
          }
          if (deviceData.state?.value !== 'notActive') {
            tabList.splice(1, 0, {
              key: 'status',
              tab: '运行状态',
            });
          }
          // apis.deviceProdcut.protocolConfiguration(deviceData.protocol, deviceData.transport)
          //   .then(resp => {
          //     setConfig(resp.result);
          //   }).catch();

          apis.deviceProdcut
            .deviceConfiguration(deviceData.id)
            .then(resp => {
              setConfig(resp.result);
            })
            .catch();
          setTableList(tabList);
          setSpinning(false);
        }
      })
      .catch(() => {
        setSpinning(false);
        message.error('产品物模型数据错误');
      });
  };

  const statusMap = new Map();
  statusMap.set('online', <Badge status="success" text={'在线'} />);
  statusMap.set('offline', <Badge status="error" text={'离线'} />);
  statusMap.set('notActive', <Badge status="processing" text={'未启用'} />);

  useEffect(() => {
    apis.deviceProdcut
      .queryOrganization()
      .then(res => {
        if (res.status === 200) {
          res.result.map((e: any) => (orgInfo[e.id] = e.name));
        }
      })
      .catch(() => {});
    apis.deviceProdcut
      .units()
      .then((response: any) => {
        if (response.status === 200) {
          setUnits(response.result);
        }
      })
      .catch(() => {});

    return () => {
      deviceStatus && deviceStatus.unsubscribe();
    };
  }, []);

  useEffect(() => {
    setActiveKey('info');

    if (pathname.indexOf('save') > 0) {
      const list = pathname.split('/');
      getInfo(list[list.length - 1]);
      setId(list[list.length - 1]);
    }
    setTableList(tabList);
  }, [window.location.hash]);

  const disconnectDevice = (deviceId?: string) => {
    setSpinning(true);
    apis.deviceInstance
      .disconnectDevice(deviceId)
      .then(response => {
        if (response.status === 200) {
          message.success('断开连接成功');
          data.state = { value: 'offline', text: '离线' };
          setData(data);
          setSpinning(false);
        } else {
          message.error('断开连接失败');
          setSpinning(false);
        }
      })
      .catch();
  };

  const changeDeploy = (deviceId?: string) => {
    setSpinning(true);
    apis.deviceInstance
      .changeDeploy(deviceId)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          data.state = { value: 'offline', text: '离线' };
          setData(data);
          setSpinning(false);
        } else {
          message.error('操作失败');
          setSpinning(false);
        }
      })
      .catch(() => {});
  };

  const updateData = (type: string, item: any) => {
    let metadata = JSON.stringify({ events, properties, functions, tags });
    if (type === 'event') {
      metadata = JSON.stringify({ events: item, properties, functions, tags });
    } else if (type === 'properties') {
      metadata = JSON.stringify({ events, properties: item, functions, tags });
    } else if (type === 'function') {
      metadata = JSON.stringify({ events, properties, functions: item, tags });
    } else if (type === 'tags') {
      metadata = JSON.stringify({ events, properties, functions, tags: item });
    }
    apis.deviceInstance
      .saveOrUpdateMetadata(data.id, metadata)
      .then((re: any) => {
        if (re.status === 200) {
          message.success('保存成功');
        }
      })
      .catch(() => {})
      .finally(() => {
        getInfo(deviceId!);
      });
  };

  const action = (
    <Tooltip title="刷新">
      <Icon
        type="sync"
        style={{ fontSize: 20 }}
        onClick={() => {
          getInfo(deviceId!);
        }}
      />
    </Tooltip>
  );

  const info = {
    info: (
      <Info
        data={data}
        configuration={config}
        refresh={() => {
          getInfo(data.id);
        }}
      />
    ),
    metadata: (
      <Definition
        basicInfo={data}
        eventsData={events}
        functionsData={functions}
        propertyData={properties}
        tagsData={tags}
        unitsData={units}
        saveEvents={(data: any) => {
          setEvents(data);
          updateData('event', data);
        }}
        saveFunctions={(data: any) => {
          setFunctions(data);
          updateData('function', data);
        }}
        saveProperty={(data: any[]) => {
          setProperties(data);
          updateData('properties', data);
        }}
        saveTags={(data: any[]) => {
          setTags(data);
          updateData('tags', data); //handleSearch()
        }}
        update={() => {
          getInfo(data.id);
        }}
      />
    ),
    status: (
      <Status
        device={data}
        refresh={() => {
          getInfo(data.id);
        }}
      />
    ),
    functions: <Functions device={data} />,
    log: <Log deviceId={id} />,
    debugger: <Debugger />,
    gateway: <Gateway deviceId={id} loading={false} />,
    alarm: (
      <Alarm
        target="device"
        productId={data.productId}
        productName={data.productName}
        targetId={data.id}
        metaData={data.metadata}
        name={data.name}
      />
    ),
    shadow: <Shadow deviceId={data.id} />,
    visualization: (
      <Visualization
        type="device"
        target={data.id}
        name={data.name}
        productId={data.productId}
        metaData={data.metadata}
      />
    ),
  };

  const content = (
    <div style={{ marginTop: 30 }}>
      <Descriptions column={4}>
        <Descriptions.Item label="ID">{id}</Descriptions.Item>
        <Descriptions.Item label="产品">
          <div>
            {data.productName}
            <a
              style={{ marginLeft: 10 }}
              onClick={() => {
                router.push(`/device/product/save/${data.productId}`);
              }}
            >
              查看
            </a>
          </div>
        </Descriptions.Item>
      </Descriptions>
    </div>
  );

  const deviceStateStyle = {
    style: {
      fontSize: 12,
      marginLeft: 20,
    },
  };
  const titleInfo = (
    <Row>
      <div>
        <span style={{ paddingRight: 20 }}>设备:{data.name}</span>
        {statusMap.get(data.state?.value)}
        {data.state?.value === 'online' ? (
          <Popconfirm
            title="确认让此设备断开连接?"
            onConfirm={() => {
              disconnectDevice(data.id);
            }}
          >
            <a {...deviceStateStyle}>断开连接</a>
          </Popconfirm>
        ) : data.state?.value === 'notActive' ? (
          <Popconfirm
            title="确认启动此设备?"
            onConfirm={() => {
              changeDeploy(data.id);
            }}
          >
            <a {...deviceStateStyle}>启动设备</a>
          </Popconfirm>
        ) : (
          <span />
        )}
      </div>
    </Row>
  );

  const extra = <div className={styles.moreInfo} />;

  return (
    <Spin tip="加载中..." spinning={spinning}>
      <PageHeaderWrapper
        className={styles.instancePageHeader}
        style={{ marginTop: 0, backgroundColor: '#F0F2F5', paddingBottom: 10 }}
        onBack={() => window.history.back()}
        title={titleInfo}
        extra={action}
        content={content}
        extraContent={extra}
        tabList={tableList}
        tabActiveKey={activeKey}
        onTabChange={(key: string) => {
          if (!data.metadata) {
            message.error('请检查产品物模型');
            return;
          }
          setActiveKey(key);
        }}
      >
        {info[activeKey]}
      </PageHeaderWrapper>
    </Spin>
  );
}
Example #29
Source File: gateway.tsx    From jetlinks-ui-antd with MIT License 4 votes vote down vote up
Gateway: React.FC<Props> = (props) => {

  const initState: State = {
    data: {},
    searchParam: { pageSize: 10 },
    currentItem: {},
    spinning: false,
    bindVisible: false,
    addVisible: false,
  };

  const [searchParam, setSearchParam] = useState(initState.searchParam);
  const [data, setData] = useState(initState.data);
  const [spinning, setSpinning] = useState(initState.spinning);
  const [currentItem, setCurrentItem] = useState(initState.currentItem);
  const [addVisible, setAddVisible] = useState(initState.addVisible);
  const [bindVisible, setBindVisible] = useState(initState.bindVisible);

  const handleSearch = (params?: any) => {
    setSearchParam(params);
    apis.deviceInstance.list(encodeQueryParam(params))
      .then((response: any) => {
          if (response.status === 200) {
            setData(response.result);
          }
          setSpinning(false);
        },
      ).catch(() => {
    });

  };

  useEffect(() => {
    setSpinning(true);
    handleSearch({
      pageSize: 10,
      terms: {
        parentId: props.deviceId,
      },
    });
  }, []);

  const changeDeploy = (record: any) => {
    setSpinning(true);
    apis.deviceInstance
      .changeDeploy(record.id)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        } else {
          setSpinning(false);
        }
      })
      .catch(() => {
      });
  };

  const unDeploy = (record: any) => {
    setSpinning(true);
    apis.deviceInstance
      .unDeploy(record.id)
      .then(response => {
        if (response.status === 200) {
          message.success('操作成功');
          handleSearch(searchParam);
        } else {
          setSpinning(false);
        }
      })
      .catch(() => {
      });
  };

  const unBindGateway = (id: string, deviceId: string) => {
    setSpinning(true);
    apis.deviceGateway.unBind(id, deviceId)
      .then(response => {
        if (response.status === 200) {
          message.success('解绑成功');
          handleSearch(searchParam);
        } else {
          setSpinning(false);
        }
      }).catch(() => {
    });
  };

  const statusMap = new Map();
  statusMap.set('online', 'success');
  statusMap.set('offline', 'error');
  statusMap.set('notActive', 'processing');

  const columns: ColumnProps<DeviceInstance>[] = [
    {
      title: 'ID',
      dataIndex: 'id',
    },
    {
      title: '设备名称',
      dataIndex: 'name',
    },
    {
      title: '产品名称',
      dataIndex: 'productName',
    },
    {
      title: '注册时间',
      dataIndex: 'registryTime',
      width: '200px',
      render: (text: any) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
      sorter: true,
      defaultSortOrder: 'descend',
    },
    {
      title: '状态',
      dataIndex: 'state',
      render: record =>
        record ? <Badge status={statusMap.get(record.value)} text={record.text}/> : '',
    },
    {
      title: '操作',
      width: '200px',
      align: 'center',
      render: (record: any) => (
        <Fragment>
          <a
            onClick={() => {
              router.push(`/device/instance/save/${record.id}`);
            }}
          >
            查看
          </a>
          <Divider type="vertical"/>
          <a
            onClick={() => {
              setCurrentItem(record);
              setAddVisible(true);
            }}
          >
            编辑
          </a>
          <Divider type="vertical"/>
          {record.state?.value === 'notActive' ? (
            <Popconfirm
              title="确认激活?"
              onConfirm={() => {
                changeDeploy(record);
              }}
            >
              <a>激活</a>
            </Popconfirm>
          ) : (
            <Popconfirm
              title="确认注销设备?"
              onConfirm={() => {
                unDeploy(record);
              }}
            >
              <a>注销</a>
            </Popconfirm>
          )}

          <Divider type="vertical"/>
          <Popconfirm
            title="确认解绑?"
            onConfirm={() => {
              unBindGateway(props.deviceId, record.id);
            }}
          >
            <a>解绑</a>
          </Popconfirm>
        </Fragment>
      ),
    },
  ];

  const onTableChange = (
    pagination: PaginationConfig,
    filters: any,
    sorter: SorterResult<DeviceInstance>,
  ) => {
    handleSearch({
      pageIndex: Number(pagination.current) - 1,
      pageSize: pagination.pageSize,
      terms: searchParam.terms,
      sorts: sorter,
    });
  };

  const saveDeviceInstance = (item: any) => {
    setSpinning(true);
    apis.deviceInstance.saveOrUpdate(item)
      .then((response: any) => {
        if (response.status === 200) {
          message.success('保存成功');
          handleSearch(searchParam);
        } else {
          setSpinning(false);
        }
      }).catch(() => {
    });
  };

  const insert = (deviceData: any) => {
    setSpinning(true);
    apis.deviceGateway.bind(props.deviceId, deviceData).then(response => {
      if (response.status === 200) {
        message.success('保存成功');
        handleSearch(searchParam);
      } else {
        setSpinning(false);
      }
    }).catch(() => {
    });
  };

  const action = (
    <Button type="primary" icon="plus" onClick={() => setBindVisible(true)}>
      绑定子设备
    </Button>
  );

  return (
    <div>
      <Spin spinning={spinning}>
        <Card style={{ marginBottom: 20 }} title="子设备列表" extra={action}>
          <div className={styles.tableListForm}>
            <Search
              search={(params: any) => {
                setSearchParam(params);
                params.parentId = props.deviceId;
                handleSearch({ terms: params, pageSize: 10 });
              }}
            />
          </div>
          <Table
            loading={props.loading}
            columns={columns}
            dataSource={data?.data}
            rowKey="id"
            onChange={onTableChange}
            pagination={{
              current: data.pageIndex + 1,
              total: data.total,
              pageSize: data.pageSize,
              showQuickJumper: true,
              showSizeChanger: true,
              pageSizeOptions: ['10', '20', '50', '100'],
              showTotal: (total: number) =>
                `共 ${total} 条记录 第  ${data.pageIndex + 1}/${Math.ceil(
                  data.total / data.pageSize,
                )}页`,
            }}
          />
        </Card>
        {addVisible && (
          <Save
            data={currentItem}
            close={() => {
              setAddVisible(false);
              setCurrentItem({});
            }}
            save={(item: any) => {
              setAddVisible(false);
              saveDeviceInstance(item);
            }}
          />
        )}

        {bindVisible && (
          <Bind selectionType='checkbox'
                close={() => {
                  setBindVisible(false);
                }}
                save={(item: any) => {
                  setBindVisible(false);
                  insert(item);
                }}
          />
        )}
      </Spin>
    </div>
  );
}