ahooks#useDebounce TypeScript Examples

The following examples show how to use ahooks#useDebounce. 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: SimplePluginList.tsx    From yakit with GNU Affero General Public License v3.0 5 votes vote down vote up
SimplePluginList: React.FC<SimplePluginListProp> = React.memo((props) => {
    const [scripts, setScripts, getScripts] = useGetState<YakScript[]>([])
    const [total, setTotal] = useState(0)
    const [listNames, setListNames] = useState<string[]>([...(props.initialSelected || [])]);

    // const [params, setParams] = useState<{ ScriptNames: string[] }>({ScriptNames: [...props.initialSelected || []]})
    const [pluginLoading, setPluginLoading] = useState<boolean>(false)

    const allSelectYakScript = useMemoizedFn((flag: boolean) => {
        if (flag) {
            const newSelected = [...scripts.map((i) => i.ScriptName), ...listNames]
            setListNames([...newSelected.filter((e, index) => newSelected.indexOf(e) === index)])
        } else {
            setListNames([])
        }
    })
    const selectYakScript = useMemoizedFn((y: YakScript) => {
        listNames.push(y.ScriptName)
        setListNames([...listNames])
    })
    const unselectYakScript = useMemoizedFn((y: YakScript) => {
        const names = listNames.splice(listNames.indexOf(y.ScriptName), 1);
        setListNames([...listNames])
    })

    useEffect(() => {
        if (props.onSelected) {
            props.onSelected([...listNames])
        }
    }, [listNames])


    const search = useMemoizedFn((searchParams?: { limit: number; keyword: string }) => {
        const {limit, keyword} = searchParams || {}

        setPluginLoading(true)
        queryYakScriptList(
            props.pluginTypes ? props.pluginTypes : "",
            (data, total) => {
                setTotal(total || 0)
                setScripts(data)
                setListNames([...(data || []).filter(i => i.IsGeneralModule).map(i => i.ScriptName)])
            },
            () => setTimeout(() => setPluginLoading(false), 300),
            limit || 200,
            undefined,
            keyword || "",
            props.initialQuery,
        )
    })

    useEffect(() => {
        search()
    }, [useDebounce(props.initialQuery, {wait: 500})])

    return <PluginList
        readOnly={props.readOnly}
        bordered={props.bordered}
        loading={pluginLoading}
        lists={(scripts || []).sort((a: YakScript, b: YakScript) => {
            return (b.IsGeneralModule ? 1 : 0) - (a.IsGeneralModule ? 1 : 0)
        })}
        disabled={props.disabled}
        getLists={getScripts}
        total={total}
        selected={listNames}
        allSelectScript={allSelectYakScript}
        selectScript={selectYakScript}
        unSelectScript={unselectYakScript}
        search={search}
        title={props?.verbose || "选择插件"}
        bodyStyle={{
            padding: "0 4px",
            overflow: "hidden"
        }}
    />
})
Example #2
Source File: index.tsx    From scorpio-h5-design with MIT License 4 votes vote down vote up
export default function() {
  // @ts-expect-error
  const { _id } = history.location.query;
  const { setStateByObjectKeys } = useModel('bridge');
  const queryPageDetailReq = useRequest(service.queryPageDetail, {
    manual: true,
  });
  const [loading, setLoading] = useBoolean(true);

  useEffect(() => {
    initData();
    return ()=>{
      window.postmate_mobile.destroy();
    };
  }, []);

  /**
   * 初始化数据、编辑页面初始数据
   */
  const initData = async function() {
    setLoading.setTrue();
    // 加载iframe、发送请求、更新state会导致页面短时间卡顿,延时进行这些任务
    await sleep(100);
    let state = {
      pageId: undefined,
      pageSchema: [],
      selectPageIndex: -1,
      selectComponentId: undefined,
    };
    if (_id) {
      const res = await queryPageDetailReq.run({
        _id,
      });
      state = {
        pageId: res._id,
        pageSchema: res.pageSchema,
        selectPageIndex: 0,
        selectComponentId: undefined,
      };
    }
    setStateByObjectKeys(state, false);
    await sleep(100);
    const handshake = new Postmate({
      container: document.getElementById('mobile-content'),
      url: `${config.base}/#/mobile`,
      name: 'mobile',
      classListArray: ['mobile'],
    });
    handshake.then((child) => {
      window.postmate_mobile = child;
      syncState({
        payload: state,
        type: IMessageType.syncState,
      });
      // 注册事件
      child.on(childrenModel.SYNC_STATE, (message) => {
        setStateByObjectKeys(message, false);
      });
      setLoading.setFalse();
    });
  };

  return (
    <Spin
      spinning={useDebounce(loading, { wait: 500 })}
      wrapperClassName="blur-loading"
      indicator={<Loading />}
    >
      <div className="design">
        <Header />
        <div className="side-left">
          {!loading && <SideLeft />}
        </div>
        <div className="side-right">
          {!loading && <SideRight />}
        </div>
        <div className="center">
          <MobileSimulator loading={loading}/>
        </div>
      </div>
    </Spin>
  );
}
Example #3
Source File: index.tsx    From scorpio-h5-design with MIT License 4 votes vote down vote up
ComponentDetail = function() {
  const { setStateByObjectKeys, pageSchema, selectComponent } = useModel('bridge');
  const component = selectComponent as IComponentSchema;
  const { onSubmit, loading } = Model.useContainer();
  const SchemaRef = useRef<{ getValue: () => any }>(null);

  function onTabChange(key: string) {
    if (key === 'form') {
      component.generatorSchema = SchemaRef.current?.getValue();
      const state = {
        pageSchema: [...pageSchema],
      };
      setStateByObjectKeys(state);
      onChildrenReady(() => {
        syncState({
          payload: state,
          type: IMessageType.syncState,
        });
      });
    }
  }

  async function handelSubmit() {
    component.generatorSchema = SchemaRef.current?.getValue();
    const dataURL = await window.postmate_mobile.get(childrenModel.CAPTURE);
    if (dataURL) {
      const file = dataURLtoFile(dataURL, new Date().getTime().toString());
      const fileName = `${uuidv4()}.png`;
      await ossClient.put(`design/${fileName}`, file);
      component.cover = `https://scorpio-design.lxzyl.cn/design/${fileName}`;
    }
    onSubmit();
  }

  const OperationsSlot = {
    right: (
      <Space className="manage-component-detail-tabs-extBtn">
        <Button onClick={() => history.goBack()}>返回</Button>
        <Button type="primary" onClick={handelSubmit}>保存</Button>
      </Space>
    ),
  };

  const debouncedloading = useDebounce(loading, { wait: 500 });

  return (

    <Spin
      spinning={debouncedloading}
      wrapperClassName="blur-loading"
      indicator={<Loading />}
    >
      <div className="manage-component-detail">
        <div className="left">
          <MobileSimulator loading={loading}/>
        </div>
        <div className="right">
          <Tabs
            className="manage-component-detail-tabs"
            defaultActiveKey="1"
            onChange={onTabChange}
            tabBarExtraContent={OperationsSlot}
          >
            <TabPane tab="schema可视化配置" key="schema">
              <Schema ref={SchemaRef} />
            </TabPane>
            <TabPane tab="schema手动编辑" key="code">
              <Code />
            </TabPane>
            <TabPane tab="组件属性配置" key="form">
              <Form />
            </TabPane>
            <TabPane tab="容器属性配置" key="container">
              <BaseLayoutConfig />
            </TabPane>
          </Tabs>
        </div>
      </div>
    </Spin>
  );
}
Example #4
Source File: ICMPSizeLoggerPage.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
ICMPSizeLoggerPage: React.FC<ICMPSizeLoggerPageProp> = (props) => {
    const [size, setSize] = useState<number>(0);
    const [records, setRecords] = useState<ICMPSizeLoggerInfo[]>([]);
    const [loading, setLoading] = useState(false);
    const [host, setHost] = useState("");

    const sizeNow = useDebounce(size, {maxWait: 500});

    const update = useMemoizedFn(() => {
        ipcRenderer.invoke("QueryICMPTrigger", {
            Length: sizeNow,
        }).then((data: { Notification?: ICMPSizeLoggerInfo[] }) => {
            if (data?.Notification) {
                setRecords(data.Notification)
            }
        }).catch(e => {
            setRecords([])
        })
    })

    const refreshSize = useMemoizedFn(() => {
        setLoading(true)
        ipcRenderer.invoke("RequireICMPRandomLength", {}).then((d: { Length: number } | any) => {
            setSize(d.Length)
            setRecords([])
        }).catch(() => {
        }).finally(() => {
            setTimeout(() => setLoading(false), 100)
        })
    })

    useEffect(() => {
        if (sizeNow < 100) {
            refreshSize()
            return
        }

        update()
        let id = setInterval(update, 4000)
        return () => {
            clearInterval(id)
        }
    }, [sizeNow])

    return <AutoCard title={<Space>
        ICMP Size Logger
        <div style={{color: "#999", fontSize: 12}}>
            使用 ping 携带特定长度数据包判定 ICMP 反连
        </div>
        <Divider type={"vertical"}/>
        <Form onSubmitCapture={e => {
            e.preventDefault()

            setLoading(true)
            ipcRenderer.invoke("RequireICMPRandomLength", {}).then((d: { Length: number, ExternalHost: string } | any) => {
                setSize(d.Length)
                setHost(d.ExternalHost)
                setRecords([])
            }).finally(() => {
                setTimeout(() => setLoading(false), 300)
            })
        }} layout={"inline"} size={"small"}>
            <InputInteger
                disable={true}
                label={"设置 Ping 包大小"}
                setValue={setSize}
                value={size}
            />
            <Form.Item colon={false} label={" "}>
                <Space>
                    <Button disabled={loading} type="primary" htmlType="submit"> 随机生成可用长度 </Button>
                    <Button disabled={loading} type="link" onClick={() => {
                        update()
                    }} icon={<ReloadOutlined/>}> 刷新 </Button>
                </Space>
            </Form.Item>
        </Form>
    </Space>} bordered={false}>
        <Space style={{width: "100%"}} direction={"vertical"}>
            <Alert type={"success"} message={<Space direction={"vertical"} style={{width: "100%"}}>
                <h4>ICMP Size Logger 是一个通过 Ping 包大小来判断 ICMP 反连的 ICMP 记录器</h4>
                <Space>
                    <div>在 Windows 系统中,使用</div>
                    {host === "" || sizeNow <= 0 ? <Spin/> :
                        <CopyableField mark={true} text={`ping -l ${sizeNow} ${host}`}/>}
                    <div>命令</div>
                </Space>
                <Space>
                    <div>在 MacOS/Linux/*nix 系统中,使用</div>
                    {host === "" || sizeNow <= 0 ? <Spin/> :
                        <CopyableField mark={true} text={`ping -c 4 -s ${sizeNow} ${host}`}/>}
                    <div>命令</div>
                </Space>
            </Space>}>

            </Alert>
            <Table<ICMPSizeLoggerInfo>
                size={"small"}
                dataSource={records}
                rowKey={i => `${i.CurrentRemoteAddr}`}
                pagination={false}
                columns={[
                    {title: "ICMP/Ping 长度", render: (i: ICMPSizeLoggerInfo) => <Tag color={"geekblue"}>{sizeNow}</Tag>},
                    {title: "远端IP", dataIndex: "CurrentRemoteAddr"},
                    {
                        title: "触发时间",
                        render: (i: ICMPSizeLoggerInfo) => <Tag
                            color={"geekblue"}>{formatTimestamp(i.TriggerTimestamp)}</Tag>
                    },
                ]}
            >

            </Table>
        </Space>
    </AutoCard>
}
Example #5
Source File: BatchExecuteByFilter.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
BatchExecuteByFilter: React.FC<BatchExecuteByFilterProp> = React.memo((props) => {
    const [total, setTotal] = useState(0);
    const [loading, setLoading] = useState(false);
    const [token, setToken] = useState(randomString(20))
    const [executing, setExecuting] = useState(false);
    const [percent, setPercent] = useState(0);

    // 执行任务历史列表
    const [taskHistory, setTaskHistory] = useState<NewTaskHistoryProps[]>([])

    // 计算插件数量
    useEffect(() => {
        setLoading(true)
        const result = simpleQueryToFull(props.isAll, props.simpleQuery, props.allTag);

        ipcRenderer.invoke("QueryYakScript", result).then((data: QueryYakScriptsResponse) => {
            setTotal(data.Total)
        }).catch(e => console.info(e))
            .finally(() => setTimeout(() => setLoading(false), 300))
    }, [
        useDebounce(props.simpleQuery, {wait: 500}),
        useDebounce(props.isAll, {wait: 500})
    ])

    // 回复缓存
    useEffect(() => {
        setLoading(true)
        ipcRenderer
            .invoke("get-value", ExecuteTaskHistory)
            .then((res: any) => {
                setTaskHistory(res ? JSON.parse(res) : [])
            })
            .catch(() => {
            })
            .finally(() => {
                setTimeout(() => setLoading(false), 300)
            })
    }, [])

    // 执行批量任务
    const run = useMemoizedFn((t: TargetRequest) => {
        setPercent(0)

        //@ts-ignore
        const time = Date.parse(new Date()) / 1000
        const obj: NewTaskHistoryProps = {
            target: t,
            simpleQuery: props.simpleQuery,
            isAll: props.isAll,
            time: formatTimestamp(time)
        }
        const arr = [...taskHistory]
        if (taskHistory.length === 10) arr.pop()
        arr.unshift(obj)
        setTaskHistory(arr)
        ipcRenderer.invoke("set-value", ExecuteTaskHistory, JSON.stringify(arr))

        const tokens = randomString(40)
        setToken(tokens)
        StartExecBatchYakScriptWithFilter(
            t, simpleQueryToFull(props.isAll, props.simpleQuery, props.allTag),
            tokens).then(() => {
            setExecuting(true)
        }).catch(e => {
            failed(`启动批量执行插件失败:${e}`)
        })
    });

    const cancel = useMemoizedFn(() => {
        CancelBatchYakScript(token).then()
    })
    useEffect(() => {
        return cancel()
    }, [])

    const executeHistory = useMemoizedFn((item: NewTaskHistoryProps) => {
        setLoading(true)
        props.executeHistory(item)
        setTimeout(() => setLoading(false), 300);
    })

    useEffect(() => {
        if (!token) return

        ipcRenderer.on(`${token}-end`, async (e) => {
            console.info("call finished by token filter")
            setTimeout(() => setExecuting(false), 300)
        })
        return () => {
            ipcRenderer.removeAllListeners(`${token}-end`)
        }
    }, [token])

    return <AutoCard
        title={<Space>
            {"已选插件"}
            <Tag>{`${total}`}</Tag>
        </Space>}
        size={"small"} bordered={false}
        extra={<Space>
            {(percent > 0 || executing) && <div style={{width: 200}}>
                <Progress status={executing ? "active" : undefined} percent={
                    parseInt((percent * 100).toFixed(0))
                }/>
            </div>}
        </Space>}
        bodyStyle={{display: "flex", flexDirection: "column", padding: '0 5px', overflow: "hidden"}}
    >
        <ExecSelectedPlugins
            disableStartButton={total <= 0}
            onSubmit={run}
            onCancel={cancel}
            executing={executing}
            loading={loading}
            history={taskHistory}
            executeHistory={executeHistory}
        />
        <Divider style={{margin: 4}}/>
        <div style={{flex: '1', overflow: "hidden"}}>
            <BatchExecutorResultByFilter token={token} executing={executing} setPercent={setPercent}/>
        </div>
    </AutoCard>
})
Example #6
Source File: QueryYakScriptParam.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
QueryYakScriptParamSelector: React.FC<QueryYakScriptParamProp> = React.memo((props) => {
    const {loading, allTag, onAllTag, isAll, onIsAll, historyTask} = props
    // 下拉框选中tag值
    const selectRef = useRef(null)
    const [itemSelects, setItemSelects] = useState<string[]>([])
    const [selectLoading, setSelectLoading] = useState<boolean>(true)
    useEffect(() => {
        setTimeout(() => setSelectLoading(false), 300)
    }, [selectLoading])

    // 设置本地搜索 tags 的状态
    const [searchTag, setSearchTag] = useState("");
    // 用于存储 tag 的搜索与结果
    const [topTags, setTopTags] = useState<FieldName[]>([]);
    const [topN, setTopN] = useState(15);
    const [selectedTags, setSelectedTags] = useState<string[]>([]);

    // 更新搜索,这个也可以用于后端保存
    const [params, setParams] = useState<SimpleQueryYakScriptSchema>(props.params)

    useEffect(() => {
        props.onParams(params)
    }, [params])

    // 辅助变量
    const [updateTagsSelectorTrigger, setUpdateTagsSelector] = useState(false);

    // 设置最大最小值
    const [minTagWeight, setMinTagWeight] = useState(1);
    const [maxTagWeight, setMaxTagWeight] = useState(2000);

    useEffect(() => {
        let count = 0;
        const showTags = allTag.filter(d => {
            if (
                count <= topN // 限制数量
                && d.Total >= minTagWeight && d.Total <= maxTagWeight
                && !selectedTags.includes(d.Name)
                && d.Name.toLowerCase().includes(searchTag.toLowerCase()) // 设置搜索结果
            ) {
                count++
                return true
            }
            return false
        })
        setTopTags([...showTags])
    }, [
        allTag,
        useDebounce(minTagWeight, {wait: 500}),
        useDebounce(maxTagWeight, {wait: 500}),
        useDebounce(searchTag, {wait: 500}),
        useDebounce(selectedTags, {wait: 500}),
        useDebounce(topN, {wait: 500}),
        updateTagsSelectorTrigger,
    ])

    const updateTagsSelector = () => {
        setUpdateTagsSelector(!updateTagsSelectorTrigger)
    }
    const syncTags = useMemoizedFn(() => {
        setParams({
            type: params.type,
            tags: selectedTags.join(","),
            include: params.include,
            exclude: [],
        })
    })

    useEffect(() => {
        const tags = historyTask ? historyTask.split(",") : []
        setSelectedTags(tags)
    }, [historyTask])

    // 更新 params Tags
    useEffect(() => {
        syncTags()
    }, [useDebounce(selectedTags, {wait: 300})])

    useEffect(() => {
        setTopN(10)
    }, [searchTag])

    const selectedAll = useMemoizedFn(() => {
        if (!selectRef || !selectRef.current) return
        const ref = selectRef.current as unknown as HTMLDivElement
        ref.blur()
        setTimeout(() => {
            onIsAll(true)
            setItemSelects([])
            setSearchTag("")
            setParams({type: params.type, tags: "", include: [], exclude: []})
        }, 200);
    })
    const selectDropdown = useMemoizedFn((originNode: React.ReactNode) => {
        return (
            <div>
                <Spin spinning={selectLoading}>
                    <div className="select-render-all" onClick={selectedAll}>全选</div>
                    {originNode}
                </Spin>
            </div>
        )
    })

    const saveConfigTemplate = useMemoizedFn(() => {
        let m = showModal({
            title: "导出批量扫描配置",
            width: "50%",
            content: (
                <>
                    <SaveConfig QueryConfig={params} onSave={filename => {
                        info(`保存到 ${filename}`)
                        m.destroy()
                    }}/>
                </>
            ),
        })
    })
    useHotkeys("alt+p", saveConfigTemplate)
    const importConfigTemplate = useMemoizedFn(() => {
        let m = showModal({
            title: "导出批量扫描配置",
            width: "50%",
            content: (
                <>
                    <ImportConfig/>
                </>
            ),
        })
    })
    useHotkeys("alt+u", importConfigTemplate)

    return (
        <AutoCard
            size={"small"}
            bordered={true}
            title={"选择插件"}
            extra={
                <Space>
                    <Popconfirm
                        title={"强制更新讲重新构建 Tags 索引"}
                        onConfirm={() => onAllTag()}
                    >
                        <a href={"#"}>强制更新 Tags</a>
                    </Popconfirm>
                    <Popconfirm
                        title={"清空已选 Tag?"}
                        onConfirm={() => {
                            onIsAll(false)
                            setSelectedTags([])
                            setParams({type: params.type, tags: "", include: [], exclude: []})
                            updateTagsSelector()
                        }}
                    >
                        <Button size={"small"} type={"link"} danger={true}>
                            清空
                        </Button>
                    </Popconfirm>
                </Space>
            }
            loading={loading}
            bodyStyle={{display: "flex", flexDirection: "column", overflow: "hidden"}}
        >
            <div className="div-width-100" style={{maxHeight: 237}}>
                <div className="div-width-100">
                    <Form size="small">
                        <ItemSelects
                            item={{
                                style: {marginBottom: 0},
                                label: "设置Tag"
                            }}
                            select={{
                                ref: selectRef,
                                className: "div-width-100",
                                allowClear: true,
                                autoClearSearchValue: false,
                                maxTagCount: "responsive",
                                mode: "multiple",
                                data: topTags,
                                optValue: "Name",
                                optionLabelProp: "Name",
                                renderOpt: (info: FieldName) => {
                                    return <div style={{display: "flex", justifyContent: "space-between"}}>
                                        <span>{info.Name}</span><span>{info.Total}</span></div>
                                },
                                value: itemSelects,
                                onSearch: (keyword: string) => setSearchTag(keyword),
                                setValue: (value) => setItemSelects(value),
                                onDropdownVisibleChange: (open) => {
                                    if (open) {
                                        setItemSelects([])
                                        setSearchTag("")
                                    } else {
                                        const filters = itemSelects.filter(item => !selectedTags.includes(item))
                                        setSelectedTags(selectedTags.concat(filters))
                                        setItemSelects([])
                                        setSearchTag("")
                                    }
                                },
                                onPopupScroll: (e) => {
                                    const {target} = e
                                    const ref: HTMLDivElement = target as unknown as HTMLDivElement
                                    if (ref.scrollTop + ref.offsetHeight === ref.scrollHeight) {
                                        setSelectLoading(true)
                                        setTopN(topN + 10)
                                    }
                                },
                                dropdownRender: (originNode: React.ReactNode) => selectDropdown(originNode)
                            }}
                        ></ItemSelects>
                    </Form>
                </div>

                <Divider style={{margin: "6px 0"}}/>

                {(isAll || selectedTags.length !== 0) && (
                    <div className='div-width-100 div-height-100' style={{maxHeight: 200}}>
                        <AutoCard
                            size='small'
                            bordered={false}
                            bodyStyle={{overflow: "hidden auto", padding: 2}}
                        >
                            {isAll ? (
                                <Tag
                                    style={{marginBottom: 2}}
                                    color={"blue"}
                                    onClose={() => {
                                        onIsAll(false)
                                        setSelectedTags([])
                                        setParams({type: params.type, tags: "", include: [], exclude: []})
                                        updateTagsSelector()
                                    }}
                                    closable={true}
                                >
                                    全选
                                </Tag>
                            ) : (
                                selectedTags.map((i) => {
                                    return (
                                        <Tag
                                            key={i}
                                            style={{marginBottom: 2}}
                                            color={"blue"}
                                            onClose={() => {
                                                setSelectedTags(selectedTags.filter((element) => i !== element))
                                            }}
                                            closable={true}
                                        >
                                            {i}
                                        </Tag>
                                    )
                                })
                            )}
                        </AutoCard>
                    </div>
                )}
            </div>

            <div style={{flex: 1, overflow: "hidden", paddingTop: 0, marginTop: 2}}>
                <SearchYakScriptForFilter
                    simpleFilter={params}
                    isAll={isAll}
                    onInclude={(i) => {
                        setParams({
                            ...params,
                            include: isAll ? [...params.include] : [...params.include, i.ScriptName],
                            exclude: [...params.exclude.filter((target) => i.ScriptName != target)]
                        })
                    }}
                    onExclude={(i) => {
                        const existedInTag = isAll ? true :
                            params.tags
                                .split(",")
                                .filter((tag) => !!tag)
                                .filter((tag) => i.Tags.includes(tag)).length > 0

                        if (existedInTag) {
                            setParams({
                                ...params,
                                exclude: [...params.exclude, i.ScriptName],
                                include: [...params.include.filter((target) => i.ScriptName != target)]
                            })
                        } else {
                            setParams({
                                ...params,
                                include: [...params.include.filter((target) => i.ScriptName != target)]
                            })
                        }
                    }}
                />
            </div>
        </AutoCard>
    )
})
Example #7
Source File: QueryYakScriptParam.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
SearchYakScriptForFilter: React.FC<SearchYakScriptForFilterProp> = React.memo((props) => {
    const [params, setParams] = useState<QueryYakScriptRequest>({
        ExcludeNucleiWorkflow: true,
        ExcludeScriptNames: [],
        Keyword: "",
        Pagination: genDefaultPagination(20),
        Type: "mitm,port-scan,nuclei"
    });
    const [response, setResponse] = useState<QueryYakScriptsResponse>({
        Data: [],
        Pagination: genDefaultPagination(20),
        Total: 0
    });
    const [loading, setLoading] = useState(false)
    const data = response.Data;

    const update = useMemoizedFn(() => {
        setLoading(true)
        ipcRenderer.invoke("QueryYakScript", {...params,}).then((data: QueryYakScriptsResponse) => {
            setResponse(data)
        }).catch(e => {
            console.info(e)
        }).finally(() => setTimeout(() => setLoading(false), 300))
    })

    const onSelect = useMemoizedFn((selected: boolean, item: YakScript) => {
        if (selected) {
            props.onExclude(item)
        } else {
            props.onInclude(item)
        }
    })

    useEffect(() => {
        update()
    }, [useDebounce(params.Keyword, {wait: 500})])

    return (
        <AutoCard
            title={
                <Input
                    allowClear={true}
                    prefix={<SearchOutlined/>}
                    placeholder="搜索插件"
                    value={params.Keyword}
                    onChange={(e) => setParams({...params, Keyword: e.target.value})}
                />
            }
            loading={loading}
            size={"small"}
            bordered={false}
            headStyle={{padding: 0, borderBottom: "0px"}}
            bodyStyle={{padding: "0 0 12px 0", overflow: "hidden auto"}}
        >
            <List
                pagination={false}
                dataSource={data}
                split={false}
                renderItem={(item: YakScript) => {
                    const haveBeenExcluded = props.simpleFilter.exclude.includes(item.ScriptName)
                    let selected = false

                    if (!haveBeenExcluded) {
                        if (props.isAll) {
                            selected = true
                        } else {
                            props.simpleFilter.tags.split(",").forEach((e) => {
                                if (!e) return

                                if (item.Tags.includes(e)) {
                                    selected = true
                                    return
                                }
                            })
                            if (!selected) {
                                selected = props.simpleFilter.include.includes(item.ScriptName)
                            }
                        }
                    }

                    return (
                        <AutoCard size={"small"} style={{marginBottom: 4}} bodyStyle={{padding: "6px 12px"}}>
                            <PluginListOptInfo
                                info={item}
                                selected={selected}
                                onSelect={() => onSelect(selected, item)}
                            />
                        </AutoCard>
                    )
                }}
            ></List>
        </AutoCard>
    )
})