ahooks#useThrottleFn TypeScript Examples

The following examples show how to use ahooks#useThrottleFn. 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: index.tsx    From scorpio-h5-design with MIT License 5 votes vote down vote up
export default function Code() {
  const { selectComponent, pageSchema, setStateByObjectKeys } = useModel('bridge');
  const jsonError = useThrottleFn(()=>{
    message.error('json格式错误');
  }, {
    wait: 3000,
  });

  const options = {
    selectOnLineNumbers: true,
    strike: true,
  };
  function editorDidMount(editor:any, monaco:any) {
    editor.focus();
  }
  function onChange(newValue:string, e:any) {
    try {
      const jsonValue = parseJson(newValue);
      pageSchema[0].components[0].generatorSchema = jsonValue;
      setStateByObjectKeys({
        pageSchema: [...pageSchema],
      });
    } catch (error) {
      // message.error('json格式错误');
      console.log('error: ', error);
      jsonError.run();
    }
  }
  const code = JSON.stringify(selectComponent.generatorSchema, null, 2);
  console.log('code: ', code);
  return (
    <div className="code-editor">
      <LoadableComponent
        language="json"
        theme="vs-light"
        value={code}
        options={options}
        onChange={onChange}
        editorDidMount={editorDidMount}
      />
    </div>
  );
}
Example #2
Source File: Title.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
export default function Title(props: IProps) {
  const { curCluster, clusters, setCurCluster, dashboard, setDashboard, refresh, range, setRange, step, setStep, refreshFlag, setRefreshFlag, refreshRef, onAddPanel } = props;
  const { id, name } = dashboard;
  const history = useHistory();
  const [titleEditing, setTitleEditing] = useState(false);
  const titleRef = useRef<any>(null);
  const handleModifyTitle = async (newName) => {
    updateDashboard(id, { ...dashboard, name: newName }).then(() => {
      setDashboard({ ...dashboard, name: newName });
      setTitleEditing(false);
    });
  };
  const { run } = useThrottleFn(
    () => {
      if ('start' in range && range.start && range.end) {
        const diff = range.end - range.start;
        const now = moment().unix();
        setRange({
          end: now,
          start: now - diff,
        });
      } else if ('unit' in range && range.unit) {
        const newRefreshFlag = _.uniqueId('refreshFlag_');
        setRange({
          ...range,
          refreshFlag: newRefreshFlag,
        });
        setRefreshFlag(newRefreshFlag);
      }
      refresh(false);
    },
    { wait: 1000 },
  );

  return (
    <div className='dashboard-detail-header'>
      <div className='dashboard-detail-header-left'>
        <RollbackOutlined className='back' onClick={() => history.push('/dashboards')} />
        {titleEditing ? (
          <Input
            ref={titleRef}
            defaultValue={name}
            onPressEnter={(e: any) => {
              handleModifyTitle(e.target.value);
            }}
          />
        ) : (
          <div className='title'>{dashboard.name}</div>
        )}
        {!titleEditing ? (
          <EditOutlined
            className='edit'
            onClick={() => {
              setTitleEditing(!titleEditing);
            }}
          />
        ) : (
          <>
            <Button size='small' style={{ marginRight: 5, marginLeft: 5 }} onClick={() => setTitleEditing(false)}>
              取消
            </Button>
            <Button
              size='small'
              type='primary'
              onClick={() => {
                handleModifyTitle(titleRef.current.state.value);
              }}
            >
              保存
            </Button>
          </>
        )}
      </div>
      <div className='dashboard-detail-header-right'>
        <Space>
          <div>
            <Dropdown
              trigger={['click']}
              overlay={
                <Menu>
                  {_.map([{ type: 'row', name: '分组' }, ...visualizations], (item) => {
                    return (
                      <Menu.Item
                        key={item.type}
                        onClick={() => {
                          onAddPanel(item.type);
                        }}
                      >
                        {item.name}
                      </Menu.Item>
                    );
                  })}
                </Menu>
              }
            >
              <Button type='primary' icon={<AddPanelIcon />}>
                添加图表
              </Button>
            </Dropdown>
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            集群:
            <Dropdown
              overlay={
                <Menu selectedKeys={[curCluster]}>
                  {clusters.map((cluster) => (
                    <Menu.Item
                      key={cluster}
                      onClick={(_) => {
                        setCurCluster(cluster);
                        localStorage.setItem('curCluster', cluster);
                        refresh();
                      }}
                    >
                      {cluster}
                    </Menu.Item>
                  ))}
                </Menu>
              }
            >
              <Button>
                {curCluster} <DownOutlined />
              </Button>
            </Dropdown>
          </div>
          <DateRangePicker value={range} onChange={setRange} />
          <Resolution onChange={(v) => setStep(v)} initialValue={step} />
          <Refresh onRefresh={run} ref={refreshRef} />
        </Space>
      </div>
    </div>
  );
}
Example #3
Source File: detail.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
export default function DashboardDetail() {
  const refreshRef = useRef<{ closeRefresh: Function }>();
  const { t } = useTranslation();
  const { id, busiId } = useParams<URLParam>();
  const [groupForm] = Form.useForm();
  const history = useHistory();
  const Ref = useRef<any>(null);
  const { clusters } = useSelector<CommonRootState, CommonStoreState>((state) => state.common);
  const localCluster = localStorage.getItem('curCluster');
  const [curCluster, setCurCluster] = useState<string>(localCluster || clusters[0]);
  if (!localCluster && clusters.length > 0) {
    setCurCluster(clusters[0]);
    localStorage.setItem('curCluster', clusters[0]);
  }
  const [dashboard, setDashboard] = useState<Dashboard>({
    create_by: '',
    favorite: 0,
    id: 0,
    name: '',
    tags: '',
    update_at: 0,
    update_by: '',
  });
  const [step, setStep] = useState<number | null>(null);
  const [titleEditing, setTitleEditing] = useState(false);
  const [chartGroup, setChartGroup] = useState<Group[]>([]);
  const [variableConfig, setVariableConfig] = useState<VariableType>();
  const [variableConfigWithOptions, setVariableConfigWithOptions] = useState<VariableType>();
  const [dashboardLinks, setDashboardLinks] = useState<ILink[]>();
  const [groupModalVisible, setGroupModalVisible] = useState(false);
  const [chartModalVisible, setChartModalVisible] = useState(false);
  const [chartModalInitValue, setChartModalInitValue] = useState<Chart | null>();
  const [range, setRange] = useState<Range>({
    start: 0,
    end: 0,
  });
  const [refreshFlag, setRefreshFlag] = useState(_.uniqueId('refreshFlag_'));
  const { run } = useThrottleFn(
    () => {
      if ('start' in range && range.start && range.end) {
        const diff = range.end - range.start;
        const now = moment().unix();
        setRange({
          end: now,
          start: now - diff,
        });
      } else if ('unit' in range && range.unit) {
        const newRefreshFlag = _.uniqueId('refreshFlag_');
        setRange({
          ...range,
          refreshFlag: newRefreshFlag,
        });
        setRefreshFlag(newRefreshFlag);
      }
      init(false);
    },
    { wait: 1000 },
  );

  useEffect(() => {
    init();
  }, []);

  const init = (needUpdateVariable = true) => {
    getSingleDashboard(busiId, id).then((res) => {
      setDashboard(res.dat);
      if (res.dat.configs) {
        const configs = JSON.parse(res.dat.configs);
        setVariableConfig(configs);
        setVariableConfigWithOptions(configs);
        setDashboardLinks(configs.links);
      }
    });
    getChartGroup(busiId, id).then((res) => {
      let arr = res.dat || [];
      setChartGroup(
        arr
          .sort((a, b) => a - b)
          .map((item) => {
            item.updateTime = Date.now(); // 前端拓展一个更新时间字段,用来主动刷新ChartGroup
            return item;
          }),
      );
    });
  };

  const handleDateChange = (e) => {
    setRange(e);
  };

  const handleEdit = () => {
    setTitleEditing(!titleEditing);
  };

  const handleModifyTitle = async (e) => {
    await updateSingleDashboard(busiId, id, { ...dashboard, name: e.target.value });
    // await init();
    setDashboard({ ...dashboard, name: e.target.value });
    setTitleEditing(false);
  };

  const handleAddChart = (gid: number) => {
    groupId = gid;
    editor({
      visible: true,
      variableConfig: variableConfigWithOptions,
      cluster: curCluster,
      busiId,
      groupId,
      id,
      initialValues: {
        type: 'timeseries',
        targets: [
          {
            refId: 'A',
            expr: '',
          },
        ],
      },
      onOK: () => {
        handleChartConfigVisibleChange(true);
      },
    });
    // setChartModalVisible(true);
  }; //group是为了让detail组件知道当前需要刷新的是哪个chartGroup,item是为了获取待编辑的信息

  const handleUpdateChart = (group: Group, item: Chart) => {
    groupId = group.id;
    setChartModalInitValue(item);

    if (semver.valid(item.configs.version)) {
      editor({
        visible: true,
        variableConfig,
        cluster: curCluster,
        busiId,
        groupId,
        id,
        initialValues: {
          ...item.configs,
          id: item.id,
        },
        onOK: () => {
          handleChartConfigVisibleChange(true);
        },
      });
    } else {
      setChartModalVisible(true);
    }
  };

  const handleDelChart = async (group: Group, item: Chart) => {
    groupId = group.id;
    await removeChart(busiId, item.id as any);
    refreshUpdateTimeByChartGroupId();
  };

  const handleCloneChart = async (group: Group, item: Chart) => {
    groupId = group.id;
    const configsClone = _.cloneDeep(item.configs);
    configsClone.layout = {
      w: configsClone.layout.w,
      h: configsClone.layout.h,
    };
    await createChart(busiId, {
      configs: JSON.stringify(configsClone),
      weight: 0,
      group_id: groupId,
    });
    refreshUpdateTimeByChartGroupId();
  };

  const handleShareChart = async (group: Group, item: any) => {
    const serielData = {
      dataProps: {
        ...item.configs,
        targets: _.map(item.configs.targets, (target) => {
          const realExpr = variableConfigWithOptions ? replaceExpressionVars(target.expr, variableConfigWithOptions, variableConfigWithOptions.var.length, id) : target.expr;
          return {
            ...target,
            expr: realExpr,
          };
        }),
        step,
        range,
      },
      curCluster: localStorage.getItem('curCluster'),
    };
    SetTmpChartData([
      {
        configs: JSON.stringify(serielData),
      },
    ]).then((res) => {
      const ids = res.dat;
      window.open('/chart/' + ids);
    });
  };

  const handleAddOrUpdateChartGroup = async () => {
    await groupForm.validateFields();
    let obj = groupForm.getFieldsValue();

    if (isAddGroup) {
      let weightArr = chartGroup.map((item) => item.weight);
      let weight = Math.max(...weightArr) + 1;
      await createChartGroup(busiId, { ...obj, weight, dashboard_id: Number(id) });
    } else {
      let group = chartGroup.find((item) => item.id === groupId);
      await updateChartGroup(busiId, [{ dashboard_id: Number(id), ...group, ...obj }]);
    }

    init();
    isAddGroup = true;
    setGroupModalVisible(false);
  };

  const handleUpdateChartGroup = (group: Group) => {
    groupId = group.id;
    isAddGroup = false;
    groupForm.setFieldsValue({ name: group.name });
    setGroupModalVisible(true);
  };

  const handleMoveUpChartGroup = async (group: Group) => {
    const { weight } = group;
    let lessWeightGroup = chartGroup.find((item) => item.weight === weight - 1);
    if (!lessWeightGroup) return;
    lessWeightGroup.weight = weight;
    group.weight = weight - 1;
    await updateChartGroup(busiId, [lessWeightGroup, group]);
    init();
  };

  const handleMoveDownChartGroup = async (group: Group) => {
    const { weight } = group;
    let lessWeightGroup = chartGroup.find((item) => item.weight === weight + 1);
    if (!lessWeightGroup) return;
    lessWeightGroup.weight = weight;
    group.weight = weight + 1;
    await updateChartGroup(busiId, [lessWeightGroup, group]);
    init();
  };

  const handleDelChartGroup = async (id: number) => {
    await delChartGroup(busiId, id);
    message.success(t('删除分组成功'));
    init();
    setGroupModalVisible(false);
  };

  const refreshUpdateTimeByChartGroupId = () => {
    let groupIndex = chartGroup.findIndex((item) => item.id === groupId);
    if (groupIndex < 0) return;
    let newChartGroup = [...chartGroup];
    newChartGroup[groupIndex].updateTime = Date.now();
    setChartGroup(newChartGroup);
  };

  const handleChartConfigVisibleChange = (b) => {
    setChartModalVisible(false);
    setChartModalInitValue(null);
    b && refreshUpdateTimeByChartGroupId();
  };

  const handleVariableChange = (value, b, valueWithOptions) => {
    let dashboardConfigs: any = {};
    try {
      if (dashboard.configs) {
        dashboardConfigs = JSON.parse(dashboard.configs);
      }
    } catch (e) {
      console.error(e);
    }
    dashboardConfigs.var = value.var;
    b && updateSingleDashboard(busiId, id, { ...dashboard, configs: JSON.stringify(dashboardConfigs) });
    setVariableConfig(dashboardConfigs);
    valueWithOptions && setVariableConfigWithOptions(valueWithOptions);
  };

  const stopAutoRefresh = () => {
    refreshRef.current?.closeRefresh();
  };
  const clusterMenu = (
    <Menu selectedKeys={[curCluster]}>
      {clusters.map((cluster) => (
        <Menu.Item
          key={cluster}
          onClick={(_) => {
            setCurCluster(cluster);
            localStorage.setItem('curCluster', cluster);
            init();
          }}
        >
          {cluster}
        </Menu.Item>
      ))}
    </Menu>
  );
  return (
    <PageLayout
      customArea={
        <div className='dashboard-detail-header'>
          <div className='dashboard-detail-header-left'>
            <RollbackOutlined className='back' onClick={() => history.push('/dashboards')} />
            {titleEditing ? <Input ref={Ref} defaultValue={dashboard.name} onPressEnter={handleModifyTitle} /> : <div className='title'>{dashboard.name}</div>}
            {!titleEditing ? (
              <EditOutlined className='edit' onClick={handleEdit} />
            ) : (
              <>
                <Button size='small' style={{ marginRight: 5, marginLeft: 5 }} onClick={() => setTitleEditing(false)}>
                  取消
                </Button>
                <Button
                  size='small'
                  type='primary'
                  onClick={() => {
                    handleModifyTitle({ target: { value: Ref.current.state.value } });
                  }}
                >
                  保存
                </Button>
              </>
            )}
          </div>
          <div className='dashboard-detail-header-right'>
            <Space>
              <div style={{ display: 'flex', alignItems: 'center' }}>
                集群:
                <Dropdown overlay={clusterMenu}>
                  <Button>
                    {curCluster} <DownOutlined />
                  </Button>
                </Dropdown>
              </div>
              <DateRangePicker onChange={handleDateChange} />
              <Resolution onChange={(v) => setStep(v)} initialValue={step} />
              <Refresh onRefresh={run} ref={refreshRef} />
            </Space>
          </div>
        </div>
      }
    >
      <div className='dashboard-detail-content'>
        <div className='dashboard-detail-content-header'>
          <div className='variable-area'>
            <VariableConfig onChange={handleVariableChange} value={variableConfig} cluster={curCluster} range={range} id={id} onOpenFire={stopAutoRefresh} />
          </div>
          <DashboardLinks
            value={dashboardLinks}
            onChange={(v) => {
              let dashboardConfigs: any = {};
              try {
                if (dashboard.configs) {
                  dashboardConfigs = JSON.parse(dashboard.configs);
                }
              } catch (e) {
                console.error(e);
              }
              dashboardConfigs.links = v;
              updateSingleDashboard(busiId, id, {
                ...dashboard,
                configs: JSON.stringify(dashboardConfigs),
              });
              setDashboardLinks(v);
            }}
          />
        </div>

        <div className='charts'>
          {chartGroup.map((item, i) => (
            <ChartGroup
              id={id}
              cluster={curCluster}
              busiId={busiId}
              key={i}
              step={step}
              groupInfo={item}
              onAddChart={handleAddChart}
              onUpdateChart={handleUpdateChart}
              onCloneChart={handleCloneChart}
              onShareChart={handleShareChart}
              onUpdateChartGroup={handleUpdateChartGroup}
              onMoveUpChartGroup={handleMoveUpChartGroup}
              onMoveDownChartGroup={handleMoveDownChartGroup}
              onDelChart={handleDelChart}
              onDelChartGroup={handleDelChartGroup}
              range={range}
              refreshFlag={refreshFlag}
              variableConfig={variableConfigWithOptions!}
              moveUpEnable={i > 0}
              moveDownEnable={i < chartGroup.length - 1}
            />
          ))}
          <Button
            block
            icon={<PlusOutlined />}
            style={{
              paddingRight: 0,
            }}
            onClick={() => {
              groupForm.setFieldsValue({ name: '' });
              setGroupModalVisible(true);
            }}
          >
            {t('新增图表分组')}
          </Button>
        </div>
      </div>
      <Modal
        title={isAddGroup ? t('新建分组') : t('更新分组名称')}
        visible={groupModalVisible}
        onOk={handleAddOrUpdateChartGroup}
        onCancel={() => {
          setGroupModalVisible(false);
        }}
      >
        <Form {...layout} form={groupForm}>
          <Form.Item
            label={t('分组名称')}
            name='name'
            rules={[
              {
                required: true,
                message: t('请输入名称'),
              },
            ]}
          >
            <Input />
          </Form.Item>
        </Form>
      </Modal>

      {chartModalVisible && (
        <ChartConfigModal
          id={id}
          cluster={curCluster}
          busiId={busiId}
          initialValue={chartModalInitValue}
          groupId={groupId}
          show={chartModalVisible}
          onVisibleChange={handleChartConfigVisibleChange}
          variableConfig={variableConfigWithOptions}
        />
      )}
    </PageLayout>
  );
}
Example #4
Source File: HTTPFlowMiniTable.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
HTTPFlowMiniTable: React.FC<HTTPFlowMiniTableProp> = (props) => {
    const [tableHeight, setTableHeight] = useState(400);
    const [response, setResponse] = useState<QueryGeneralResponse<HTTPFlow>>({
        Data: [],
        Pagination: genDefaultPagination(),
        Total: 0
    });
    const findHTTPFlowById = (Hash: string) => {
        return response.Data.filter(i => i.Hash === Hash).shift()
    }

    const pipeline = useTablePipeline({
        components: antd,
    }).input({
        dataSource: response.Data,
        columns: props.simple ? [
            {
                code: "Hash", name: "状态", render: (i: any) => {
                    const flow: HTTPFlow | undefined = findHTTPFlowById(i)

                    return <div style={{overflow: "hidden"}}>
                        {flow && <Space size={4}>
                            <div style={{width: 35, textAlign: "right"}}>{flow.Method}</div>
                            <Tag style={{
                                width: 30,
                                textAlign: "left",
                                paddingLeft: 3, paddingRight: 3,
                            }} color={StatusCodeToColor(flow.StatusCode)}>{flow.StatusCode}</Tag>
                        </Space>}
                    </div>
                },
                width: 100, lock: true,
            },
            {
                code: "Hash", name: "URL", render: (i: any) => {
                    const flow: HTTPFlow | undefined = findHTTPFlowById(i)

                    return <div style={{overflow: "hidden"}}>
                        {flow && <Space>
                            <CopyableField
                                text={flow.Url} tooltip={false} noCopy={true}
                            />
                        </Space>}
                    </div>
                },
                width: 700,
            },
            {
                code: "Hash", name: "操作", render: (i: any) => {
                    return <>
                        <Space>
                            <Button
                                type={"link"} size={"small"}
                                onClick={() => {
                                    let m = showDrawer({
                                        width: "80%",
                                        content: onExpandHTTPFlow(
                                            findHTTPFlowById(i),
                                            () => m.destroy()
                                            ),
                                    })
                                }}
                            >详情</Button>
                        </Space>
                    </>
                },
                width: 80, lock: true,
            }
        ] : [
            {
                code: "Method", name: "Method",
                render: (i: any) => <Tag color={"geekblue"}>{i}</Tag>,
                width: 80,
            },
            {
                code: "StatusCode",
                name: "StatusCode",
                render: (i: any) => <Tag color={StatusCodeToColor(i)}>{i}</Tag>,
                width: 80
            },
            {
                code: "GetParamsTotal",
                name: "Get 参数",
                render: (i: any) => i > 0 ? <Tag color={"orange"}>{i}个</Tag> : "-",
                width: 60
            },
            {
                code: "PostParamsTotal",
                name: "Post 参数",
                render: (i: any) => i > 0 ? <Tag color={"orange"}>{i}个</Tag> : "-",
                width: 60, lock: true,
            },
            {
                code: "Url", name: "URL", render: (i: any) => <CopyableField
                    text={i} tooltip={false} noCopy={true}
                />,
                width: 450, features: {sortable: true}
            },
            {
                code: "Hash", name: "操作", render: (i: any) => {
                    return <>
                        <Space>
                            {props.onSendToWebFuzzer && <Button
                                type={"link"} size={"small"}
                                onClick={() => {
                                    const req = findHTTPFlowById(i);
                                    if (req) {
                                        ipcRenderer.invoke("send-to-tab", {
                                            type: "fuzzer",
                                            data:{
                                                isHttps: req.IsHTTPS, 
                                                request: new Buffer(req.Request).toString()
                                            }
                                        })
                                    }
                                }}
                            >发送到Fuzzer</Button>}
                            <Button
                                type={"link"} size={"small"}
                                onClick={() => {
                                    let m = showDrawer({
                                        width: "80%",
                                        content: onExpandHTTPFlow(
                                            findHTTPFlowById(i), 
                                            () => m.destroy()
                                        )
                                    })
                                }}
                            >详情</Button>
                        </Space>
                    </>
                },
                width: props.onSendToWebFuzzer ? 180 : 80, lock: true,
            },
        ],
    }).primaryKey("uuid").use(features.columnResize({
        minSize: 60,
    })).use(features.columnHover()).use(features.tips())

    if (!props.simple) {
        pipeline.use(
            features.sort({
                mode: 'single',
                highlightColumnWhenActive: true,
            }),
        )
    }


    const update = () => {
        ipcRenderer.invoke("QueryHTTPFlows", props.filter).then((data: QueryGeneralResponse<HTTPFlow>) => {
            // if ((data.Data || []).length > 0 && (response.Data || []).length > 0) {
            //     if (data.Data[0].Id === response.Data[0].Id) {
            //         props.onTotal(data.Total)
            //         return
            //     }
            // }
            setResponse(data)
            props.onTotal(data.Total)
        })
    }
    const updateThrottle = useThrottleFn(update, {wait: 1000})


    useEffect(() => {
        updateThrottle.run()
    }, [props.filter])

    useEffect(() => {
        if (props.simple) {

            if (!props.autoUpdate) {
                return
            }
            const id = setInterval(() => {
                updateThrottle.run()
            }, 1000)
            return () => {
                clearInterval(id)
            }
        }
    }, [props.simple, props.autoUpdate])

    return <div style={{width: "100%", height: "100%", overflow: "auto"}}>
        <ReactResizeDetector
            onResize={(width, height) => {
                if (!width || !height) {
                    return
                }
                setTableHeight(height)
            }}
            handleWidth={true} handleHeight={true} refreshMode={"debounce"} refreshRate={50}
        />
        <BaseTable
            {...pipeline.getProps()} style={{width: "100%", height: tableHeight, overflow: "auto"}}
        />
    </div>
}
Example #5
Source File: HTTPFlowTable.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
HTTPFlowTable: React.FC<HTTPFlowTableProp> = (props) => {
    const [data, setData, getData] = useGetState<HTTPFlow[]>([])
    const [params, setParams] = useState<YakQueryHTTPFlowRequest>(
        props.params || {SourceType: "mitm"}
    )
    const [pagination, setPagination] = useState<PaginationSchema>({
        Limit: OFFSET_LIMIT,
        Order: "desc",
        OrderBy: "created_at",
        Page: 1
    });

    // const [autoReload, setAutoReload, getAutoReload] = useGetState(false);
    const autoReloadRef = useRef<boolean>(false);
    const autoReload = autoReloadRef.current;
    const setAutoReload = (b: boolean) => {
        autoReloadRef.current = b
    };
    const getAutoReload = () => autoReloadRef.current;

    const [total, setTotal] = useState<number>(0)
    const [loading, setLoading] = useState(false)
    const [selected, setSelected, getSelected] = useGetState<HTTPFlow>()
    const [_lastSelected, setLastSelected, getLastSelected] = useGetState<HTTPFlow>()

    const [compareLeft, setCompareLeft] = useState<CompateData>({content: '', language: 'http'})
    const [compareRight, setCompareRight] = useState<CompateData>({content: '', language: 'http'})
    const [compareState, setCompareState] = useState(0)
    const [tableContentHeight, setTableContentHeight, getTableContentHeight] = useGetState<number>(0);
    // 用于记录适合
    const [_scrollY, setScrollYRaw, getScrollY] = useGetState(0)
    const setScrollY = useThrottleFn(setScrollYRaw, {wait: 300}).run

    // 如果这个大于等于 0 ,就 Lock 住,否则忽略
    const [_trigger, setLockedScroll, getLockedScroll] = useGetState(-1);
    const lockScrollTimeout = (size: number, timeout: number) => {
        setLockedScroll(size)
        setTimeout(() => setLockedScroll(-1), timeout)
    }

    const tableRef = useRef(null)

    const ref = useHotkeys('ctrl+r, enter', e => {
        const selected = getSelected()
        if (selected) {
            ipcRenderer.invoke("send-to-tab", {
                type: "fuzzer",
                data: {
                    isHttps: selected?.IsHTTPS,
                    request: new Buffer(selected.Request).toString()
                }
            })
        }
    })

    // 使用上下箭头
    useHotkeys("up", () => {
        setLastSelected(getSelected())
        const data = getData();
        if (data.length <= 0) {
            return
        }
        if (!getSelected()) {
            setSelected(data[0])
            return
        }
        const expected = parseInt(`${parseInt(`${(getSelected()?.Id as number)}`) + 1}`);
        // 如果上点的话,应该是选择更新的内容
        for (let i = 0; i < data.length; i++) {
            let current = parseInt(`${data[i]?.Id}`);
            if (current === expected) {
                setSelected(data[i])
                return
            }
        }
        setSelected(undefined)
    })
    useHotkeys("down", () => {
        setLastSelected(getSelected())
        const data = getData();

        if (data.length <= 0) {
            return
        }
        if (!getSelected()) {
            setSelected(data[0])
            return
        }
        // 如果上点的话,应该是选择更新的内容
        for (let i = 0; i < data.length; i++) {
            if (data[i]?.Id == (getSelected()?.Id as number) - 1) {
                setSelected(data[i])
                return
            }
        }
        setSelected(undefined)
    })

    // 向主页发送对比数据
    useEffect(() => {
        if (compareLeft.content) {
            const params = {info: compareLeft, type: 1}
            setCompareState(compareState === 0 ? 1 : 0)

            ipcRenderer.invoke("add-data-compare", params)
        }
    }, [compareLeft])

    useEffect(() => {
        if (compareRight.content) {
            const params = {info: compareRight, type: 2}
            setCompareState(compareState === 0 ? 2 : 0)

            ipcRenderer.invoke("add-data-compare", params)
        }
    }, [compareRight])

    const update = useMemoizedFn((
        page?: number,
        limit?: number,
        order?: string,
        orderBy?: string,
        sourceType?: string,
        noLoading?: boolean
    ) => {
        const paginationProps = {
            Page: page || 1,
            Limit: limit || pagination.Limit,
            Order: order || "desc",
            OrderBy: orderBy || "id"
        }
        if (!noLoading) {
            setLoading(true)
            // setAutoReload(false)
        }
        // yakQueryHTTPFlow({
        //     SourceType: sourceType, ...params,
        //     Pagination: {...paginationProps},
        // })
        ipcRenderer
            .invoke("QueryHTTPFlows", {
                SourceType: sourceType,
                ...params,
                Pagination: {...paginationProps}
            })
            .then((rsp: YakQueryHTTPFlowResponse) => {
                setData((rsp?.Data || []))
                setPagination(rsp.Pagination)
                setTotal(rsp.Total)
            })
            .catch((e: any) => {
                failed(`query HTTP Flow failed: ${e}`)
            })
            .finally(() => setTimeout(() => setLoading(false), 300))
    })

    const getNewestId = useMemoizedFn(() => {
        let max = 0;
        (getData() || []).forEach(e => {
            const id = parseInt(`${e.Id}`)
            if (id >= max) {
                max = id
            }
        })
        return max
    })

    const getOldestId = useMemoizedFn(() => {
        if (getData().length <= 0) {
            return 0
        }
        let min = parseInt(`${getData()[0].Id}`);
        (getData() || []).forEach(e => {
            const id = parseInt(`${e.Id}`)
            if (id <= min) {
                min = id
            }
        })
        return min
    })

    // 第一次启动的时候加载一下
    useEffect(() => {
        update(1)
    }, [])

    const scrollTableTo = useMemoizedFn((size: number) => {
        if (!tableRef || !tableRef.current) return
        const table = tableRef.current as unknown as {
            scrollTop: (number) => any,
            scrollLeft: (number) => any,
        }
        table.scrollTop(size)
    })

    const scrollUpdateTop = useDebounceFn(useMemoizedFn(() => {
        const paginationProps = {
            Page: 1,
            Limit: OFFSET_STEP,
            Order: "desc",
            OrderBy: "id"
        }

        const offsetId = getNewestId()
        console.info("触顶:", offsetId)
        // 查询数据
        ipcRenderer
            .invoke("QueryHTTPFlows", {
                SourceType: "mitm",
                ...params,
                AfterId: offsetId,  // 用于计算增量的
                Pagination: {...paginationProps}
            })
            .then((rsp: YakQueryHTTPFlowResponse) => {
                const offsetDeltaData = (rsp?.Data || [])
                if (offsetDeltaData.length <= 0) {
                    // 没有增量数据
                    return
                }
                setLoading(true)
                let offsetData = offsetDeltaData.concat(data);
                if (offsetData.length > MAX_ROW_COUNT) {
                    offsetData = offsetData.splice(0, MAX_ROW_COUNT)
                }
                setData(offsetData);
                scrollTableTo((offsetDeltaData.length + 1) * ROW_HEIGHT)
            })
            .catch((e: any) => {
                failed(`query HTTP Flow failed: ${e}`)
            })
            .finally(() => setTimeout(() => setLoading(false), 200))
    }), {wait: 600, leading: true, trailing: false}).run
    const scrollUpdateButt = useDebounceFn(useMemoizedFn((tableClientHeight: number) => {
        const paginationProps = {
            Page: 1,
            Limit: OFFSET_STEP,
            Order: "desc",
            OrderBy: "id"
        }

        const offsetId = getOldestId();
        console.info("触底:", offsetId)

        // 查询数据
        ipcRenderer
            .invoke("QueryHTTPFlows", {
                SourceType: "mitm",
                ...params,
                BeforeId: offsetId,  // 用于计算增量的
                Pagination: {...paginationProps}
            })
            .then((rsp: YakQueryHTTPFlowResponse) => {
                const offsetDeltaData = (rsp?.Data || [])
                if (offsetDeltaData.length <= 0) {
                    // 没有增量数据
                    return
                }
                setLoading(true)
                const originDataLength = data.length;
                let offsetData = data.concat(offsetDeltaData);
                let metMax = false
                const originOffsetLength = offsetData.length;
                if (originOffsetLength > MAX_ROW_COUNT) {
                    metMax = true
                    offsetData = offsetData.splice(originOffsetLength - MAX_ROW_COUNT, MAX_ROW_COUNT)
                }
                setData(offsetData);
                setTimeout(() => {
                    if (!metMax) {
                        // 没有丢结果的裁剪问题
                        scrollTableTo((originDataLength + 1) * ROW_HEIGHT - tableClientHeight)
                    } else {
                        // 丢了结果之后的裁剪计算
                        const a = originOffsetLength - offsetDeltaData.length;
                        scrollTableTo((originDataLength + 1 + MAX_ROW_COUNT - originOffsetLength) * ROW_HEIGHT - tableClientHeight)
                    }
                }, 50)
            })
            .catch((e: any) => {
                failed(`query HTTP Flow failed: ${e}`)
            }).finally(() => setTimeout(() => setLoading(false), 60))
    }), {wait: 600, leading: true, trailing: false}).run

    const sortFilter = useMemoizedFn((column: string, type: any) => {
        const keyRelation: any = {
            UpdatedAt: "updated_at",
            BodyLength: "body_length",
            StatusCode: "status_code"
        }

        if (column && type) {
            update(1, OFFSET_LIMIT, type, keyRelation[column])
        } else {
            update(1, OFFSET_LIMIT)
        }
    })

    // 这是用来设置选中坐标的,不需要做防抖
    useEffect(() => {
        if (!getLastSelected() || !getSelected()) {
            return
        }

        const lastSelected = getLastSelected() as HTTPFlow;
        const up = parseInt(`${lastSelected?.Id}`) < parseInt(`${selected?.Id}`)
        // if (up) {
        //     console.info("up")
        // } else {
        //     console.info("down")
        // }
        // console.info(lastSelected.Id, selected?.Id)
        const screenRowCount = Math.floor(getTableContentHeight() / ROW_HEIGHT) - 1

        if (!autoReload) {
            let count = 0;
            const data = getData();
            for (let i = 0; i < data.length; i++) {
                if (data[i].Id != getSelected()?.Id) {
                    count++
                } else {
                    break
                }
            }

            let minCount = count
            if (minCount < 0) {
                minCount = 0
            }
            const viewHeightMin = getScrollY() + tableContentHeight
            const viewHeightMax = getScrollY() + tableContentHeight * 2
            const minHeight = minCount * ROW_HEIGHT;
            const maxHeight = minHeight + tableContentHeight
            const maxHeightBottom = minHeight + tableContentHeight + 3 * ROW_HEIGHT
            // console.info("top: ", minHeight, "maxHeight: ", maxHeight, "maxHeightBottom: ", maxHeightBottom)
            // console.info("viewTop: ", viewHeightMin, "viewButtom: ", viewHeightMax)
            if (maxHeight < viewHeightMin) {
                // 往下滚动
                scrollTableTo(minHeight)
                return
            }
            if (maxHeightBottom > viewHeightMax) {
                // 上滚动
                const offset = minHeight - (screenRowCount - 2) * ROW_HEIGHT;
                // console.info(screenRowCount, minHeight, minHeight - (screenRowCount - 1) * ROW_HEIGHT)
                if (offset > 0) {
                    scrollTableTo(offset)
                }
                return
            }
        }
    }, [selected])

    // 给设置做防抖
    useDebounceEffect(() => {
        props.onSelected && props.onSelected(selected)
    }, [selected], {wait: 400, trailing: true, leading: true})

    useEffect(() => {
        if (autoReload) {
            const id = setInterval(() => {
                update(1, undefined, "desc", undefined, undefined, true)
            }, 1000)
            return () => {
                clearInterval(id)
            }
        }
    }, [autoReload])

    return (
        // <AutoCard bodyStyle={{padding: 0, margin: 0}} bordered={false}>
        <div ref={ref as Ref<any>} tabIndex={-1}
             style={{width: "100%", height: "100%", overflow: "hidden"}}
        >
            <ReactResizeDetector
                onResize={(width, height) => {
                    if (!width || !height) {
                        return
                    }
                    setTableContentHeight(height - 38)
                }}
                handleWidth={true} handleHeight={true} refreshMode={"debounce"} refreshRate={50}/>
            {!props.noHeader && (
                <PageHeader
                    title={"HTTP History"}
                    subTitle={
                        <Space>
                            {"所有相关请求都在这里"}
                            <Button
                                icon={<ReloadOutlined/>}
                                type={"link"}
                                onClick={(e) => {
                                    update(1)
                                }}
                            />
                        </Space>
                    }
                    extra={[
                        <Space>
                            <Form.Item label={"选择 HTTP History 类型"} style={{marginBottom: 0}}>
                                <Select
                                    mode={"multiple"}
                                    value={params.SourceType}
                                    style={{minWidth: 200}}
                                    onChange={(e) => {
                                        setParams({...params, SourceType: e})
                                        setLoading(true)
                                        setTimeout(() => {
                                            update(1, undefined, undefined, undefined, e)
                                        }, 200)
                                    }}
                                >
                                    <Select.Option value={"mitm"}>mitm: 中间人劫持</Select.Option>
                                    <Select.Option value={"fuzzer"}>
                                        fuzzer: 模糊测试分析
                                    </Select.Option>
                                </Select>
                            </Form.Item>
                            <Popconfirm
                                title={"确定想要删除所有记录吗?不可恢复"}
                                onConfirm={(e) => {
                                    ipcRenderer.invoke("delete-http-flows-all")
                                    setLoading(true)
                                    info("正在删除...如自动刷新失败请手动刷新")
                                    setTimeout(() => {
                                        update(1)
                                        if (props.onSelected) props.onSelected(undefined)
                                    }, 400)
                                }}
                            >
                                <Button danger={true}>清除全部历史记录?</Button>
                            </Popconfirm>
                        </Space>
                    ]}
                />
            )}
            <Row style={{margin: "5px 0 5px 5px"}}>
                <Col span={12}>
                    <Space>
                        <span>HTTP History</span>
                        <Button
                            icon={<ReloadOutlined/>}
                            type={"link"}
                            size={"small"}
                            onClick={(e) => {
                                update(1, undefined, "desc")
                            }}
                        />
                        {/* <Space>
                            自动刷新:
                            <Switch size={"small"} checked={autoReload} onChange={setAutoReload}/>
                        </Space> */}
                        <Input.Search
                            placeholder={"URL关键字"}
                            enterButton={true}
                            size={"small"}
                            style={{width: 170}}
                            value={params.SearchURL}
                            onChange={(e) => {
                                setParams({...params, SearchURL: e.target.value})
                            }}
                            onSearch={(v) => {
                                update(1)
                            }}
                        />
                        {props.noHeader && (
                            <Popconfirm
                                title={"确定想要删除所有记录吗?不可恢复"}
                                onConfirm={(e) => {
                                    ipcRenderer.invoke("delete-http-flows-all")
                                    setLoading(true)
                                    info("正在删除...如自动刷新失败请手动刷新")
                                    setCompareLeft({content: '', language: 'http'})
                                    setCompareRight({content: '', language: 'http'})
                                    setCompareState(0)
                                    setTimeout(() => {
                                        update(1)
                                        if (props.onSelected) props.onSelected(undefined)
                                    }, 400)
                                }}
                            >
                                <Button danger={true} size={"small"}>
                                    删除历史记录
                                </Button>
                            </Popconfirm>
                        )}
                        {/*{autoReload && <Tag color={"green"}>自动刷新中...</Tag>}*/}
                    </Space>
                </Col>
                <Col span={12} style={{textAlign: "right"}}>
                    <Tag>{total} Records</Tag>
                </Col>
            </Row>
            <TableResizableColumn
                tableRef={tableRef}
                virtualized={true}
                className={"httpFlowTable"}
                loading={loading}
                columns={[
                    {
                        dataKey: "Id",
                        width: 80,
                        headRender: () => "序号",
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            return `${rowData[dataKey] <= 0 ? "..." : rowData[dataKey]}`
                        }
                    },
                    {
                        dataKey: "Method",
                        width: 70,
                        headRender: (params1: any) => {
                            return (
                                <div
                                    style={{display: "flex", justifyContent: "space-between"}}
                                >
                                    方法
                                    <Popover
                                        placement='bottom'
                                        trigger='click'
                                        content={
                                            params &&
                                            setParams && (
                                                <HTTLFlowFilterDropdownForms
                                                    label={"搜索方法"}
                                                    params={params}
                                                    setParams={setParams}
                                                    filterName={"Methods"}
                                                    autoCompletions={["GET", "POST", "HEAD"]}
                                                    submitFilter={() => update(1)}
                                                />
                                            )
                                        }
                                    >
                                        <Button
                                            style={{
                                                paddingLeft: 4, paddingRight: 4, marginLeft: 4,
                                                color: !!params.Methods ? undefined : "gray",
                                            }}
                                            type={!!params.Methods ? "primary" : "link"} size={"small"}
                                            icon={<SearchOutlined/>}
                                        />
                                    </Popover>
                                </div>
                            )
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            // return (
                            //     <Tag color={"geekblue"} style={{marginRight: 20}}>
                            //         {rowData[dataKey]}
                            //     </Tag>
                            // )
                            return rowData[dataKey]
                        }
                    },
                    {
                        dataKey: "StatusCode",
                        width: 100,
                        sortable: true,
                        headRender: () => {
                            return (
                                <div
                                    style={{display: "inline-flex"}}
                                >
                                    状态码
                                    <Popover
                                        placement='bottom'
                                        trigger='click'
                                        content={
                                            params &&
                                            setParams && (
                                                <HTTLFlowFilterDropdownForms
                                                    label={"搜索状态码"}
                                                    params={params}
                                                    setParams={setParams}
                                                    filterName={"StatusCode"}
                                                    autoCompletions={[
                                                        "200",
                                                        "300-305",
                                                        "400-404",
                                                        "500-502",
                                                        "200-299",
                                                        "300-399",
                                                        "400-499"
                                                    ]}
                                                    submitFilter={() => update(1)}
                                                />
                                            )
                                        }
                                    >
                                        <Button
                                            style={{
                                                paddingLeft: 4, paddingRight: 4, marginLeft: 4,
                                                color: !!params.StatusCode ? undefined : "gray",
                                            }}
                                            type={!!params.StatusCode ? "primary" : "link"} size={"small"}
                                            icon={<SearchOutlined/>}
                                        />
                                    </Popover>
                                </div>
                            )
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            return (
                                <div style={{color: StatusCodeToColor(rowData[dataKey])}}>
                                    {rowData[dataKey] === 0 ? "" : rowData[dataKey]}
                                </div>
                            )
                        }
                    },
                    {
                        dataKey: "Url",
                        resizable: true,
                        headRender: () => {
                            return (
                                <div
                                    style={{display: "flex", justifyContent: "space-between"}}
                                >
                                    URL
                                    <Popover
                                        placement='bottom'
                                        trigger='click'
                                        content={
                                            params &&
                                            setParams && (
                                                <HTTLFlowFilterDropdownForms
                                                    label={"搜索URL关键字"}
                                                    params={params}
                                                    setParams={setParams}
                                                    filterName={"SearchURL"}
                                                    pureString={true}
                                                    submitFilter={() => update(1)}
                                                />
                                            )
                                        }
                                    >
                                        <Button
                                            style={{
                                                paddingLeft: 4, paddingRight: 4, marginLeft: 4,
                                                color: !!params.SearchURL ? undefined : "gray",
                                            }}
                                            type={!!params.SearchURL ? "primary" : "link"} size={"small"}
                                            icon={<SearchOutlined/>}
                                        />
                                    </Popover>
                                </div>
                            )
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            if (rowData.IsPlaceholder) {
                                return <div style={{color: "#888585"}}>{"滚轮上滑刷新..."}</div>
                            }
                            return (
                                <div style={{width: "100%", display: "flex"}}>
                                    <div className='resize-ellipsis' title={rowData.Url}>
                                        {!params.SearchURL ? (
                                            rowData.Url
                                        ) : (
                                            rowData.Url
                                        )}
                                    </div>
                                </div>
                            )
                        },
                        width: 600
                    },
                    {
                        dataKey: "HtmlTitle",
                        width: 120,
                        resizable: true,
                        headRender: () => {
                            return "Title"
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            return rowData[dataKey] ? rowData[dataKey] : ""
                        }
                    },
                    {
                        dataKey: "Tags",
                        width: 120,
                        resizable: true,
                        headRender: () => {
                            return "Tags"
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            return rowData[dataKey] ? (
                                `${rowData[dataKey]}`.split("|").filter(i => !i.startsWith("YAKIT_COLOR_")).join(", ")
                            ) : ""
                        }
                    },
                    {
                        dataKey: "IPAddress",
                        width: 140, resizable: true,
                        headRender: () => {
                            return "IP"
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            return rowData[dataKey] ? rowData[dataKey] : ""
                        }
                    },
                    {
                        dataKey: "BodyLength",
                        width: 120,
                        sortable: true,
                        headRender: () => {
                            return (
                                <div style={{display: "inline-block", position: "relative"}}>
                                    响应长度
                                    <Popover
                                        placement='bottom'
                                        trigger='click'
                                        content={
                                            params &&
                                            setParams && (
                                                <HTTLFlowFilterDropdownForms
                                                    label={"是否存在Body?"}
                                                    params={params}
                                                    setParams={setParams}
                                                    filterName={"HaveBody"}
                                                    pureBool={true}
                                                    submitFilter={() => update(1)}
                                                />
                                            )
                                        }
                                    >
                                        <Button
                                            style={{
                                                paddingLeft: 4, paddingRight: 4, marginLeft: 4,
                                                color: !!params.HaveBody ? undefined : "gray",
                                            }}
                                            type={!!params.HaveBody ? "primary" : "link"} size={"small"}
                                            icon={<SearchOutlined/>}
                                        />
                                    </Popover>
                                </div>
                            )
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            return (
                                <div style={{width: 100}}>
                                    {/* 1M 以上的话,是红色*/}
                                    {rowData.BodyLength !== -1 ?
                                        (<div style={{color: rowData.BodyLength > 1000000 ? "red" : undefined}}>
                                            {rowData.BodySizeVerbose
                                                ? rowData.BodySizeVerbose
                                                : rowData.BodyLength}
                                        </div>)
                                        :
                                        (<div></div>)
                                    }
                                </div>
                            )
                        }
                    },
                    // {
                    //     dataKey: "UrlLength",
                    //     width: 90,
                    //     headRender: () => {
                    //         return "URL 长度"
                    //     },
                    //     cellRender: ({rowData, dataKey, ...props}: any) => {
                    //         const len = (rowData.Url || "").length
                    //         return len > 0 ? <div>{len}</div> : "-"
                    //     }
                    // },
                    {
                        dataKey: "GetParamsTotal",
                        width: 65,
                        align: "center",
                        headRender: () => {
                            return (
                                <div
                                    style={{display: "flex", justifyContent: "space-between"}}
                                >
                                    参数
                                    <Popover
                                        placement='bottom'
                                        trigger='click'
                                        content={
                                            params &&
                                            setParams && (
                                                <HTTLFlowFilterDropdownForms
                                                    label={"过滤是否存在基础参数"}
                                                    params={params}
                                                    setParams={setParams}
                                                    filterName={"HaveCommonParams"}
                                                    pureBool={true}
                                                    submitFilter={() => update(1)}
                                                />
                                            )
                                        }
                                    >
                                        <Button
                                            style={{
                                                paddingLeft: 4, paddingRight: 4, marginLeft: 4,
                                                color: !!params.HaveCommonParams ? undefined : "gray",
                                            }}
                                            type={!!params.HaveCommonParams ? "primary" : "link"} size={"small"}
                                            icon={<SearchOutlined/>}
                                        />
                                    </Popover>
                                </div>
                            )
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            return (
                                <Space>
                                    {(rowData.GetParamsTotal > 0 ||
                                        rowData.PostParamsTotal > 0) && <CheckOutlined/>}
                                </Space>
                            )
                        }
                    },
                    {
                        dataKey: "ContentType",
                        resizable: true, width: 80,
                        headRender: () => {
                            return "响应类型"
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            let contentTypeFixed = rowData.ContentType.split(";")
                                .map((el: any) => el.trim())
                                .filter((i: any) => !i.startsWith("charset"))
                                .join(",") || "-"
                            if (contentTypeFixed.includes("/")) {
                                const contentTypeFixedNew = contentTypeFixed.split("/").pop()
                                if (!!contentTypeFixedNew) {
                                    contentTypeFixed = contentTypeFixedNew
                                }
                            }
                            return (
                                <div>
                                    {contentTypeFixed === "null" ? "" : contentTypeFixed}
                                </div>
                            )
                        }
                    },
                    {
                        dataKey: "UpdatedAt",
                        sortable: true,
                        width: 110,
                        headRender: () => {
                            return "请求时间"
                        },
                        cellRender: ({rowData, dataKey, ...props}: any) => {
                            return <Tooltip
                                title={rowData[dataKey] === 0 ? "" : formatTimestamp(rowData[dataKey])}
                            >
                                {rowData[dataKey] === 0 ? "" : formatTime(rowData[dataKey])}
                            </Tooltip>
                        }
                    },
                    {
                        dataKey: "operate",
                        width: 90,
                        headRender: () => "操作",
                        cellRender: ({rowData}: any) => {
                            if (!rowData.Hash) return <></>
                            return (
                                <a
                                    onClick={(e) => {
                                        let m = showDrawer({
                                            width: "80%",
                                            content: onExpandHTTPFlow(
                                                rowData,
                                                () => m.destroy()
                                            )
                                        })
                                    }}
                                >
                                    详情
                                </a>
                            )
                        }
                    }
                ]}
                data={autoReload ? data : [TableFirstLinePlaceholder].concat(data)}
                autoHeight={tableContentHeight <= 0}
                height={tableContentHeight}
                sortFilter={sortFilter}
                renderRow={(children: ReactNode, rowData: any) => {
                    if (rowData)
                        return (
                            <div
                                id='http-flow-row'
                                ref={(node) => {
                                    const color =
                                        rowData.Hash === selected?.Hash ?
                                            "rgba(78, 164, 255, 0.4)" :
                                            rowData.Tags.indexOf("YAKIT_COLOR") > -1 ?
                                                TableRowColor(rowData.Tags.split("|").pop().split('_').pop().toUpperCase()) :
                                                "#ffffff"
                                    if (node) {
                                        if (color) node.style.setProperty("background-color", color, "important")
                                        else node.style.setProperty("background-color", "#ffffff")
                                    }
                                }}
                                style={{height: "100%"}}
                            >
                                {children}
                            </div>
                        )
                    return children
                }}
                onRowContextMenu={(rowData: HTTPFlow | any, event: React.MouseEvent) => {
                    if (rowData) {
                        setSelected(rowData);
                    }
                    showByCursorMenu(
                        {
                            content: [
                                {
                                    title: '发送到 Web Fuzzer',
                                    onClick: () => {
                                        ipcRenderer.invoke("send-to-tab", {
                                            type: "fuzzer",
                                            data: {
                                                isHttps: rowData.IsHTTPS,
                                                request: new Buffer(rowData.Request).toString("utf8")
                                            }
                                        })
                                    }
                                },
                                {
                                    title: '发送到 数据包扫描',
                                    onClick: () => {
                                        ipcRenderer
                                            .invoke("GetHTTPFlowByHash", {Hash: rowData.Hash})
                                            .then((i: HTTPFlow) => {
                                                ipcRenderer.invoke("send-to-packet-hack", {
                                                    request: i.Request,
                                                    ishttps: i.IsHTTPS,
                                                    response: i.Response
                                                })
                                            })
                                            .catch((e: any) => {
                                                failed(`Query Response failed: ${e}`)
                                            })
                                    }
                                },
                                {
                                    title: '复制 URL',
                                    onClick: () => {
                                        callCopyToClipboard(rowData.Url)
                                    },
                                },
                                {
                                    title: '复制为 Yak PoC 模版', onClick: () => {
                                    },
                                    subMenuItems: [
                                        {
                                            title: "数据包 PoC 模版", onClick: () => {
                                                const flow = rowData as HTTPFlow;
                                                if (!flow) return;
                                                generateYakCodeByRequest(flow.IsHTTPS, flow.Request, code => {
                                                    callCopyToClipboard(code)
                                                }, RequestToYakCodeTemplate.Ordinary)
                                            }
                                        },
                                        {
                                            title: "批量检测 PoC 模版", onClick: () => {
                                                const flow = rowData as HTTPFlow;
                                                if (!flow) return;
                                                generateYakCodeByRequest(flow.IsHTTPS, flow.Request, code => {
                                                    callCopyToClipboard(code)
                                                }, RequestToYakCodeTemplate.Batch)
                                            }
                                        },
                                    ]
                                },
                                {
                                    title: '标注颜色',
                                    subMenuItems: availableColors.map(i => {
                                        return {
                                            title: i.title,
                                            render: i.render,
                                            onClick: () => {
                                                const flow = rowData as HTTPFlow
                                                if (!flow) {
                                                    return
                                                }

                                                const existedTags = flow.Tags ? flow.Tags.split("|").filter(i => !!i && !i.startsWith("YAKIT_COLOR_")) : []
                                                existedTags.push(`YAKIT_COLOR_${i.color.toUpperCase()}`)
                                                ipcRenderer.invoke("SetTagForHTTPFlow", {
                                                    Id: flow.Id, Hash: flow.Hash,
                                                    Tags: existedTags,
                                                }).then(() => {
                                                    info(`设置 HTTPFlow 颜色成功`)
                                                    if (!autoReload) {
                                                        setData(data.map(item => {
                                                            if (item.Hash === flow.Hash) {
                                                                item.Tags = `YAKIT_COLOR_${i.color.toUpperCase()}`
                                                                return item
                                                            }
                                                            return item
                                                        }))
                                                    }
                                                })
                                            }
                                        }
                                    }),
                                    onClick: () => {
                                    }
                                },
                                {
                                    title: '移除颜色',
                                    onClick: () => {
                                        const flow = rowData as HTTPFlow
                                        if (!flow) return

                                        const existedTags = flow.Tags ? flow.Tags.split("|").filter(i => !!i && !i.startsWith("YAKIT_COLOR_")) : []
                                        existedTags.pop()
                                        ipcRenderer.invoke("SetTagForHTTPFlow", {
                                            Id: flow.Id, Hash: flow.Hash,
                                            Tags: existedTags,
                                        }).then(() => {
                                            info(`清除 HTTPFlow 颜色成功`)
                                            if (!autoReload) {
                                                setData(data.map(item => {
                                                    if (item.Hash === flow.Hash) {
                                                        item.Tags = ""
                                                        return item
                                                    }
                                                    return item
                                                }))
                                            }
                                        })
                                        return
                                    },
                                },
                                {
                                    title: "发送到对比器", onClick: () => {
                                    },
                                    subMenuItems: [
                                        {
                                            title: '发送到对比器左侧',
                                            onClick: () => {
                                                setCompareLeft({
                                                    content: new Buffer(rowData.Request).toString("utf8"),
                                                    language: 'http'
                                                })
                                            },
                                            disabled: [false, true, false][compareState]
                                        },
                                        {
                                            title: '发送到对比器右侧',
                                            onClick: () => {
                                                setCompareRight({
                                                    content: new Buffer(rowData.Request).toString("utf8"),
                                                    language: 'http'
                                                })
                                            },
                                            disabled: [false, false, true][compareState]
                                        }
                                    ]
                                },
                            ]
                        },
                        event.clientX,
                        event.clientY
                    )
                }}
                onRowClick={(rowDate: any) => {
                    if (!rowDate.Hash) return
                    if (rowDate.Hash !== selected?.Hash) {
                        setSelected(rowDate)
                    } else {
                        // setSelected(undefined)
                    }
                }}
                onScroll={(scrollX, scrollY) => {
                    setScrollY(scrollY)
                    // 防止无数据触发加载
                    if (data.length === 0 && !getAutoReload()) {
                        setAutoReload(true)
                        return
                    }

                    // 根据页面展示内容决定是否自动刷新
                    let contextHeight = (data.length + 1) * ROW_HEIGHT // +1 是要把表 title 算进去
                    let offsetY = scrollY + tableContentHeight;
                    if (contextHeight < tableContentHeight) {
                        setAutoReload(true)
                        return
                    }
                    setAutoReload(false)

                    // 向下刷新数据
                    if (contextHeight <= offsetY) {
                        setAutoReload(false)
                        scrollUpdateButt(tableContentHeight)
                        return
                    }

                    // 锁住滚轮
                    if (getLockedScroll() > 0 && getLockedScroll() >= scrollY) {
                        if (scrollY === getLockedScroll()) {
                            return
                        }
                        // scrollTableTo(getLockedScroll())
                        return
                    }
                    const toTop = scrollY <= 0;
                    if (toTop) {
                        lockScrollTimeout(ROW_HEIGHT, 600)
                        scrollUpdateTop()
                    }
                }}
            />
        </div>
        // </AutoCard>
    )
}