diff --git a/app/protos/grpc.proto b/app/protos/grpc.proto index 9c718314cc..024ca8c792 100644 --- a/app/protos/grpc.proto +++ b/app/protos/grpc.proto @@ -2950,6 +2950,8 @@ message QueryRisksRequest { string IsRead = 12; string Title = 13; repeated int64 Ids = 14; + + string RuntimeId = 15; } message QueryRisksResponse { diff --git a/app/renderer/src/main/src/components/TableTotalAndSelectNumber/TableTotalAndSelectNumber.module.scss b/app/renderer/src/main/src/components/TableTotalAndSelectNumber/TableTotalAndSelectNumber.module.scss new file mode 100644 index 0000000000..1402b131a0 --- /dev/null +++ b/app/renderer/src/main/src/components/TableTotalAndSelectNumber/TableTotalAndSelectNumber.module.scss @@ -0,0 +1,21 @@ +.table-total-select { + display: flex; + align-items: center; + justify-content: flex-end; + + .table-total-select-item { + display: flex; + align-items: center; + + .table-total-select-text { + color: var(--yakit-helper-text-color); + font-size: 12px; + margin-right: 4px; + } + + .table-total-select-number { + color: var(--yakit-primary-5); + font-size: 12px; + } + } +} \ No newline at end of file diff --git a/app/renderer/src/main/src/components/TableTotalAndSelectNumber/TableTotalAndSelectNumber.tsx b/app/renderer/src/main/src/components/TableTotalAndSelectNumber/TableTotalAndSelectNumber.tsx new file mode 100644 index 0000000000..d31f3a8783 --- /dev/null +++ b/app/renderer/src/main/src/components/TableTotalAndSelectNumber/TableTotalAndSelectNumber.tsx @@ -0,0 +1,28 @@ +import React from "react" +import styles from "./TableTotalAndSelectNumber.module.scss" +import {Divider} from "antd" + +interface TableTotalAndSelectNumberProps { + total: number + selectNum?: number +} +export const TableTotalAndSelectNumber: React.FC = React.memo((props) => { + const {total, selectNum} = props + return ( +
+
+ Total + {total} +
+ {selectNum !== undefined && ( + <> + +
+ Selected + {selectNum} +
+ + )} +
+ ) +}) diff --git a/app/renderer/src/main/src/pages/assetViewer/PortTable/PortTable.tsx b/app/renderer/src/main/src/pages/assetViewer/PortTable/PortTable.tsx index 88ea92fb99..99764470cb 100644 --- a/app/renderer/src/main/src/pages/assetViewer/PortTable/PortTable.tsx +++ b/app/renderer/src/main/src/pages/assetViewer/PortTable/PortTable.tsx @@ -25,6 +25,7 @@ import {isEnpriTraceAgent} from "@/utils/envfile" import cloneDeep from "lodash/cloneDeep" import emiter from "@/utils/eventBus/eventBus" import {YakitRoute} from "@/enums/yakitRoute" +import {TableTotalAndSelectNumber} from "@/components/TableTotalAndSelectNumber/TableTotalAndSelectNumber" const {ipcRenderer} = window.require("electron") const defLimit = 20 @@ -453,7 +454,8 @@ export const PortTable: React.FC = React.memo( } setSendPopoverVisible(false) }) - const onTableChange = useMemoizedFn((page: number, limit: number, sort: SortProps, filter: any) => { + const onTableChange = useMemoizedFn((page: number, limit: number, newSort: SortProps, filter: any) => { + let sort = {...newSort} if (sort.order === "none") { sort.order = "desc" sort.orderBy = "id" @@ -514,23 +516,7 @@ export const PortTable: React.FC = React.memo( {tableTitle ? ( tableTitle ) : ( -
-
- Total - - {total} - -
- -
- - Selected - - - {selectNum} - -
-
+ )}
= React.mem loading, defaultActiveKey, pluginExecuteResultWrapper = "", - PluginTabsRightNode, + PluginTabsRightNode } = props + const [allTotal, setAllTotal] = useState(0) + const [tempTotal, setTempTotal] = useState(0) // 在risk表没有展示之前得临时显示在tab上得小红点计数 + const [interval, setInterval] = useState(1000) + + useUpdateEffect(() => { + setAllTotal(0) + setTempTotal(0) + setInterval(1000) + }, [runtimeId]) + useInterval(() => { + if (runtimeId) getTotal() + }, interval) + + const getTotal = useMemoizedFn(() => { + const params: QueryRisksRequest = { + ...defQueryRisksRequest, + Pagination: { + ...defQueryRisksRequest.Pagination, + Page: 1, + Limit: 1 + }, + RuntimeId: runtimeId + } + apiQueryRisks(params).then((allRes) => { + if (+allRes.Total > 0) { + setTempTotal(+allRes.Total) + } + }) + }) + + /** + * 漏洞风险tab没有点击之前,tabContent不会渲染展示;不会请求数据 + * 强制渲染得话,组件内部不会请求数据 + * 采取:没有点击漏洞风险tab之前,由外面根据runtimeId查询是否有数据,有数据就展示对应得tab,以里面传出来得total为准,total>0后停止外面得useInterval, + */ + const onSetRiskTotal = useMemoizedFn((total) => { + if (total > 0) { + setAllTotal(total) + if (interval) setInterval(undefined) + } + }) + const renderTabContent = useMemoizedFn((ele: HoldGRPCStreamProps.InfoTab) => { switch (ele.type) { case "risk": - return + return !!runtimeId ? ( + + ) : ( + <> + ) case "port": return !!runtimeId ? : <> case "http": @@ -95,12 +149,11 @@ export const PluginExecuteResult: React.FC = React.mem }) const showTabs = useMemo(() => { - if (streamInfo.riskState.length === 0) { - return streamInfo.tabsState - .filter((item) => item.tabName !== "漏洞与风险") + if (!tempTotal) { + return streamInfo.tabsState.filter((item) => item.tabName !== "漏洞与风险") } return streamInfo.tabsState - }, [streamInfo.tabsState, streamInfo.riskState]) + }, [streamInfo.tabsState, tempTotal]) const tabBarRender = useMemoizedFn((tab: HoldGRPCStreamProps.InfoTab, length: number) => { if (tab.type === "risk") { @@ -117,9 +170,13 @@ export const PluginExecuteResult: React.FC = React.mem const cardState = useCreation(() => { return streamInfo.cardState.filter((item) => item.tag !== "no display") }, [streamInfo.cardState]) + const showRiskTotal = useCreation(() => { + if (allTotal > 0) return allTotal + return tempTotal + }, [allTotal, tempTotal]) return (
- {cardState.length > 0 && ( + {cardState.length > 0 && (
@@ -128,7 +185,7 @@ export const PluginExecuteResult: React.FC = React.mem {showTabs.map((ele) => ( @@ -285,91 +342,19 @@ const PluginExecuteLog: React.FC = React.memo((props) => ) }) -/**获取风险等级的展示tag类型 */ -const getSeverity = (type) => { - switch (type) { - case "低危": - return "warning" - case "中危": - return "info" - case "高危": - return "danger" - case "严重": - return "serious" - default: - return undefined - } -} + /**风险与漏洞tab表 */ const VulnerabilitiesRisksTable: React.FC = React.memo((props) => { - const {riskState} = props - - const columns: ColumnsTypeProps[] = useMemo(() => { - return [ - { - title: "标题", - dataKey: "TitleVerbose", - width: 400, - render: (_, i) => i.TitleVerbose || i.Title || "-" - }, - { - title: "类型", - dataKey: "RiskTypeVerbose" - }, - { - title: "等级", - dataKey: "Severity", - render: (severity) => { - const title = TitleColor.filter((item) => item.key.includes(severity || ""))[0] - const value = title ? title.name : severity || "-" - return {value} - } - }, - { - title: "IP", - dataKey: "IP" - }, - { - title: "发现时间", - dataKey: "CreatedAt", - // width: 160, - enableDrag: false, - render: (v) => formatTimestamp(v) - }, - { - title: "操作", - dataKey: "Action", - width: 70, - fixed: "right", - render: (_, i: Risk) => { - return ( - <> - { - e.stopPropagation() - onDetail(i) - }} - > - 详情 - - - ) - } - } - ] - }, []) - const onDetail = useMemoizedFn((i: Risk) => { - let m = showYakitModal({ - width: "80%", - title: "详情", - footer: null, - content: ( -
- m.destroy()} /> -
- ) - }) + const {runtimeId} = props + const [riskLoading, setRiskLoading] = useState(false) + const [allTotal, setAllTotal] = useControllableValue(props, { + defaultValue: 0, + valuePropName: "allTotal", + trigger: "setAllTotal" + }) + const [query, setQuery] = useState({ + ...defQueryRisksRequest, + RuntimeId: runtimeId }) const onJumpRisk = useMemoizedFn(() => { const info: RouteToPageProps = { @@ -379,28 +364,35 @@ const VulnerabilitiesRisksTable: React.FC = Reac }) return (
- - renderTitle={ -
- 风险与漏洞 - - 查看全部 - -
- } - enableDrag={true} - titleHeight={44} - data={riskState} - renderKey={"Hash"} - pagination={{ - page: 1, - limit: 50, - total: riskState.length, - onChange: () => {} - }} - columns={columns} - containerClassName={styles["table-container"]} - /> + + +
+ 风险与漏洞 + +
+ + 查看全部 + +
+ } + riskWrapperClassName={styles["risks-table-wrapper"]} + tableVirtualResizeProps={{ + containerClassName: styles["table-container"], + titleHeight: 44, + rowSelection: undefined + }} + yakitRiskDetailsBorder={false} + excludeColumnsKey={["action"]} + allTotal={allTotal} + setAllTotal={setAllTotal} + /> +
) }) diff --git a/app/renderer/src/main/src/pages/plugins/operator/pluginExecuteResult/PluginExecuteResultType.d.ts b/app/renderer/src/main/src/pages/plugins/operator/pluginExecuteResult/PluginExecuteResultType.d.ts index 143fa6170b..d8511511a2 100644 --- a/app/renderer/src/main/src/pages/plugins/operator/pluginExecuteResult/PluginExecuteResultType.d.ts +++ b/app/renderer/src/main/src/pages/plugins/operator/pluginExecuteResult/PluginExecuteResultType.d.ts @@ -12,7 +12,9 @@ export interface PluginExecuteResultProps { } export interface VulnerabilitiesRisksTableProps { - riskState: StreamResult.Risk[] + runtimeId: string + allTotal: number + setAllTotal: (n: number) => void } export interface PluginExecuteLogProps { diff --git a/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTable.module.scss b/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTable.module.scss index 67d8976746..364959141d 100644 --- a/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTable.module.scss +++ b/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTable.module.scss @@ -149,6 +149,9 @@ $keyColor: map-keys($tagColor); width: 100%; } } +.yakit-risk-details-content-no-border { + border: 0; +} .table-renderTitle { display: flex; diff --git a/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTable.tsx b/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTable.tsx index 633a5d1fa8..8b88992ea4 100644 --- a/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTable.tsx +++ b/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTable.tsx @@ -9,11 +9,19 @@ import { import styles from "./YakitRiskTable.module.scss" import {TableVirtualResize} from "@/components/TableVirtualResize/TableVirtualResize" import {Risk} from "../schema" -import {Descriptions, Divider, Form, Tooltip} from "antd" +import {Badge, Descriptions, Divider, Form, Tooltip} from "antd" import {YakScript, genDefaultPagination} from "@/pages/invoker/schema" import {YakitPopconfirm} from "@/components/yakitUI/YakitPopconfirm/YakitPopconfirm" import {YakitButton} from "@/components/yakitUI/YakitButton/YakitButton" -import {useControllableValue, useCreation, useDebounceEffect, useDebounceFn, useInViewport, useMemoizedFn} from "ahooks" +import { + useControllableValue, + useCreation, + useDebounceEffect, + useDebounceFn, + useInViewport, + useInterval, + useMemoizedFn +} from "ahooks" import {YakitMenuItemProps} from "@/components/yakitUI/YakitMenu/YakitMenu" import { OutlineChevrondownIcon, @@ -21,6 +29,7 @@ import { OutlineEyeIcon, OutlineOpenIcon, OutlinePlayIcon, + OutlineRefreshIcon, OutlineSearchIcon, OutlineTrashIcon } from "@/assets/icon/outline" @@ -42,6 +51,7 @@ import { apiQueryAvailableRiskType, apiQueryRiskTags, apiQueryRisks, + apiQueryRisksIncrementOrderDesc, apiSetTagForRisk } from "./utils" import {CopyComponents, YakitTag} from "@/components/yakitUI/YakitTag/YakitTag" @@ -73,6 +83,8 @@ import {Uint8ArrayToString} from "@/utils/str" import {YakitRoute} from "@/enums/yakitRoute" import {PluginHubPageInfoProps} from "@/store/pageInfo" import {grpcFetchLocalPluginDetail} from "@/pages/pluginHub/utils/grpc" +import ReactResizeDetector from "react-resize-detector" +import {serverPushStatus} from "@/utils/duplex/duplex" const batchExportMenuData: YakitMenuItemProps[] = [ { @@ -218,8 +230,18 @@ const yakitRiskCellStyle = { } } } +const defLimit = 20 export const YakitRiskTable: React.FC = React.memo((props) => { - const {advancedQuery, setAdvancedQuery, setRiskLoading} = props + const { + advancedQuery, + setAdvancedQuery, + setRiskLoading, + renderTitle, + riskWrapperClassName = "", + tableVirtualResizeProps, + yakitRiskDetailsBorder = true, + excludeColumnsKey = [] + } = props const [loading, setLoading] = useState(false) const [isRefresh, setIsRefresh] = useState(false) @@ -246,16 +268,31 @@ export const YakitRiskTable: React.FC = React.memo((props) const [tag, setTag] = useState([]) + const [interval, setInterval] = useState(undefined) // 控制 Interval + const [offsetDataInTop, setOffsetDataInTop] = useState([]) + const [allTotal, setAllTotal] = useControllableValue(props, { + defaultValue: 0, + valuePropName: "allTotal", + trigger: "setAllTotal" + }) + const riskTableRef = useRef(null) const [inViewport = true] = useInViewport(riskTableRef) + + const prePage = useRef(0) + const afterId = useRef(0) + const beforeId = useRef(0) + const tableRef = useRef(null) + const defLimitRef = useRef(defLimit) + const limitRef = useRef(defLimit) + const tableBodyHeightRef = useRef(0) + const isInitRequestRef = useRef(true) + // 选中插件的数量 const selectNum = useMemo(() => { - if (allCheck) return response.Total + if (allCheck) return allTotal else return selectList.length - }, [allCheck, selectList, response.Total]) - const total = useCreation(() => { - return +response.Total - }, [response.Total]) + }, [allCheck, selectList, allTotal]) useEffect(() => { if (inViewport) { getRiskTags() @@ -268,14 +305,99 @@ export const YakitRiskTable: React.FC = React.memo((props) }, [inViewport]) useDebounceEffect( () => { - if (inViewport) update(1) + // 初次不通过此处请求数据 + if (!isInitRequestRef.current) { + onRefRiskList() + } }, - [query, type, inViewport], + [query, type], { wait: 200, leading: true } ) + useEffect(() => { + // 组件存在既不卸载 + emiter.on("onRefreshQueryNewRisk", onStartInterval) + return () => { + emiter.off("onRefreshQueryNewRisk", onStartInterval) + } + }, []) + + useInterval(() => { + if (beforeId.current) { + getIncrementInTop() + } + }, interval) + + const intervalRedDot = useCreation(() => { + return offsetDataInTop.length && !interval ? 1000 : undefined + }, [offsetDataInTop, interval]) + useInterval(() => { + const scrollTop = getScrollTop() + if (inViewport && scrollTop < 10 && offsetDataInTop?.length > 0) { + // 滚动条滚动到顶部的时候,如果偏移缓存数据中有数据,第一次优先将缓存数据放在总的数据中 + setResponse({ + ...response, + Data: [...offsetDataInTop, ...response.Data] + }) + setOffsetDataInTop([]) + return + } + }, intervalRedDot) + /**开启实时数据刷新 */ + const onStartInterval = useMemoizedFn(() => { + setInterval(1000) + }) + const getScrollTop = useMemoizedFn(() => { + return tableRef.current?.containerRef?.scrollTop || 0 + }) + /**获取滚动条在顶部的数据 */ + const getIncrementInTop = useMemoizedFn(() => { + let params: QueryRisksRequest = { + ...getQuery(), + Pagination: { + Limit: 20, + Page: 1, + Order: query.Pagination.Order, + OrderBy: query.Pagination.OrderBy + }, + FromId: afterId.current ? afterId.current : 0 + } + if (params.Pagination.Order === "asc" || params.Pagination.OrderBy !== "id") { + // 升序时,顶部不实时刷新,避免数据混乱 + // 排序字段为Id才实时刷新数据 + return + } + const scrollTop = getScrollTop() + if (inViewport && scrollTop < 10 && offsetDataInTop?.length > 0) { + // 滚动条滚动到顶部的时候,如果偏移缓存数据中有数据,第一次优先将缓存数据放在总的数据中 + setResponse({ + ...response, + Data: [...offsetDataInTop, ...response.Data] + }) + setOffsetDataInTop([]) + return + } + apiQueryRisksIncrementOrderDesc(params).then((rsp) => { + if (rsp.Data.length > 0) { + afterId.current = rsp.Data[0].Id + } else { + serverPushStatus && setInterval(undefined) + } + const newData = getResData(rsp.Data) + const newTotal = allTotal + rsp.Data.length + if (scrollTop < 10) { + setResponse({ + ...response, + Data: [...newData, ...response.Data] + }) + } else { + setOffsetDataInTop([...newData, ...offsetDataInTop]) + } + setAllTotal(newTotal) + }) + }) const columns: ColumnsTypeProps[] = useCreation(() => { const tagTable = tag.map((item) => ({ value: item.Name, @@ -287,14 +409,18 @@ export const YakitRiskTable: React.FC = React.memo((props) label: item.Verbose, total: item.Total })) - return [ + const columnArr: ColumnsTypeProps[] = [ { title: "序号", dataKey: "Id", fixed: "left", ellipsis: false, width: 96, - enableDrag: false + enableDrag: false, + sorterProps: { + sorter: true, + sorterKey: "id" + } }, { title: "标题", @@ -440,7 +566,8 @@ export const YakitRiskTable: React.FC = React.memo((props) ) } ] - }, [riskTypeVerbose, tag]) + return columnArr.filter((ele) => !excludeColumnsKey.includes(ele.dataKey)) + }, [riskTypeVerbose, tag, excludeColumnsKey]) /**复测 */ const onRetest = useMemoizedFn((record: Risk) => { if (record.YakScriptUUID || record.FromYakScript) { @@ -462,9 +589,12 @@ export const YakitRiskTable: React.FC = React.memo((props) }) const onRefRiskList = useDebounceFn( () => { + limitRef.current = defLimitRef.current + setOffsetDataInTop([]) update(1) + getTotal() }, - {wait: 200} + {wait: 200, leading: true} ).run const getRiskTags = useMemoizedFn(() => { apiQueryRiskTags().then((res) => { @@ -640,7 +770,7 @@ export const YakitRiskTable: React.FC = React.memo((props) Pagination: { ...query.Pagination, Page: 1, - Limit: total + Limit: allTotal } } apiQueryRisks(exportQuery).then((res) => { @@ -680,7 +810,7 @@ export const YakitRiskTable: React.FC = React.memo((props) Pagination: { ...query.Pagination, Page: 1, - Limit: total + Limit: allTotal } } const res = await apiQueryRisks(exportQuery) @@ -709,7 +839,7 @@ export const YakitRiskTable: React.FC = React.memo((props) const onRefreshMenuSelect = useMemoizedFn((key: string) => { switch (key) { case "noResetRefresh": - update(1) + onRefRiskList() break case "resetRefresh": onResetRefresh() @@ -722,12 +852,13 @@ export const YakitRiskTable: React.FC = React.memo((props) const onResetRefresh = useMemoizedFn(() => { setQuery(cloneDeep(defQueryRisksRequest)) }) - const onTableChange = useMemoizedFn((page: number, limit: number, sort: SortProps, filter: any) => { + const onTableChange = useMemoizedFn((page: number, limit: number, newSort: SortProps, filter: any) => { + let sort = {...newSort} if (sort.order === "none") { sort.order = "desc" - sort.orderBy = "created_at" + sort.orderBy = "id" } - setQuery({ + const newQuery = { ...query, ...filter, Pagination: { @@ -735,7 +866,10 @@ export const YakitRiskTable: React.FC = React.memo((props) Order: sort.order, OrderBy: sort.orderBy } - }) + } + setOffsetDataInTop([]) // 排序条件变化,清空缓存的实时数据 + setQuery(newQuery) + limitRef.current = defLimitRef.current }) const getQuerySeverity = useMemoizedFn((list: string[]) => { return SeverityMapTag.filter((ele) => list.includes(ele.name)) @@ -761,45 +895,96 @@ export const YakitRiskTable: React.FC = React.memo((props) } return finalParams }) + const getResData = useMemoizedFn((data: Risk[]) => { + const resData = (data || []).map((ele) => ({ + ...ele, + cellClassName: ele.IsRead ? "" : styles["yakit-risk-table-cell-unread"] + })) + return resData + }) const update = useMemoizedFn((page?: number) => { - setLoading(true) const paginationProps = { ...query.Pagination, - Page: page || 1, - Limit: query.Pagination.Limit + Page: 1, + Limit: limitRef.current } const finalParams: QueryRisksRequest = { ...getQuery(), Pagination: paginationProps } + const isInit = page === 1 + if (query.Pagination.Order === "asc") { + finalParams.FromId = isInit ? 0 : afterId.current + } else { + finalParams.UntilId = isInit ? 0 : beforeId.current + } + if (isInit) { + setLoading(true) + prePage.current = 0 + } + apiQueryRisks(finalParams) .then((res) => { - const newPage = +res.Pagination.Page - const resData = res.Data.map((ele) => ({ - ...ele, - cellClassName: ele.IsRead ? "" : styles["yakit-risk-table-cell-unread"] - })) - const d = newPage === 1 ? resData : (response?.Data || []).concat(resData) + // const newPage = +res.Pagination.Page + const resData = getResData(res.Data) + const d = isInit ? resData : (response?.Data || []).concat(resData) + prePage.current += 1 setResponse({ ...res, - Data: d + Data: d, + Pagination: { + ...res.Pagination, + Page: prePage.current // 虚假的page,只是为了让表格滚动加载下一页数据 + } }) - if (newPage === 1) { + if (isInit) { setIsRefresh(!isRefresh) setSelectList([]) setAllCheck(false) + setCurrentSelectItem(undefined) } else { if (allCheck) { setSelectList(d) } } - if (+res.Total !== selectList.length) { - setAllCheck(false) + limitRef.current = defLimit + if (query.Pagination.Order === "asc") { + if (isInit) { + beforeId.current = (res.Data[0] && res.Data[0].Id) || 0 + onTableResize(undefined, tableBodyHeightRef.current) + } + afterId.current = (res.Data[res.Data.length - 1] && res.Data[res.Data.length - 1].Id) || 0 + } else { + if (isInit) { + afterId.current = (res.Data[0] && res.Data[0].Id) || 0 + onTableResize(undefined, tableBodyHeightRef.current) + } + beforeId.current = (res.Data[res.Data.length - 1] && res.Data[res.Data.length - 1].Id) || 0 } }) .finally(() => setTimeout(() => setLoading(false), 300)) }) + /** + * 1.获取所有数据,带查询条件 + * 2.获取数据总数,因为有FromId/UntilId字段查询回来的总数并不是真正的总数 + */ + const getTotal = useMemoizedFn(() => { + const params: QueryRisksRequest = { + ...getQuery(), + Pagination: { + ...query.Pagination, + Page: 1, + Limit: 1 + } + } + apiQueryRisks(params).then((allRes) => { + setAllTotal(+allRes.Total) + if (+allRes.Total !== selectList.length) { + setAllCheck(false) + } + }) + }) const onSearch = useMemoizedFn((val) => { setQuery({ ...query, @@ -855,12 +1040,12 @@ export const YakitRiskTable: React.FC = React.memo((props) }) const onAllRead = useMemoizedFn(() => { apiNewRiskRead({Ids: []}).then(() => { - update(1) + onRefRiskList() emiter.emit("onRefRisksRead", JSON.stringify({Id: "", isAllRead: true})) }) }) const onExpend = useMemoizedFn(() => { - setAdvancedQuery(true) + if (setAdvancedQuery) setAdvancedQuery(true) }) const onRowContextMenu = useMemoizedFn((rowData: Risk) => { if (!rowData) return @@ -919,8 +1104,39 @@ export const YakitRiskTable: React.FC = React.memo((props) const index = response?.Data.findIndex((item) => item.Id === info.Id) if (index !== -1) setScrollToIndex(index) }) + /**table所在的div大小发生变化 */ + const onTableResize = useMemoizedFn((width, height) => { + if (!height) { + return + } + const tableCellHeight = 28 + const limit = Math.trunc(height / tableCellHeight) + 10 + defLimitRef.current = limit + isInitRequestRef.current = false + tableBodyHeightRef.current = height + if (allTotal === 0) { + // init + onRefRiskList() + return + } else if (tableBodyHeightRef.current < height) { + // 窗口由小变大时 重新拉取数据 + const length = response.Data.length + const h = length * tableCellHeight + if (h < height) { + update() + } + return + } + }) return ( -
+
+ = React.memo((props) }} firstNode={ + ref={tableRef} scrollToIndex={scrollToIndex} - query={{...query}} + query={query} loading={loading} isRefresh={isRefresh} titleHeight={32} renderTitle={ -
-
- {!advancedQuery && ( - - } - > - - )} -
风险与漏洞
- { - setType(e.target.value) - }} - buttonStyle='solid' - options={[ - { - value: "all", - label: "全部" - }, - { - value: "false", - label: "未读" - } - ]} - /> -
-
- Total - - {response.Total} - -
- -
- Selected - - {selectNum} - + renderTitle ? ( + renderTitle + ) : ( +
+
+ {!advancedQuery && ( + + } + > + + )} +
风险与漏洞
+ { + setType(e.target.value) + }} + buttonStyle='solid' + options={[ + { + value: "all", + label: "全部" + }, + { + value: "false", + label: "未读" + } + ]} + /> +
+
+ Total + + {response.Total} + +
+ +
+ + Selected + + + {selectNum} + +
-
-
- setKeywords(e.target.value)} - placeholder='请输入关键词搜索' - onSearch={onSearch} - onPressEnter={onPressEnter} - /> - - } - onClick={onAllRead} - name='全部已读' - /> - { - onExportMenuSelect(key) - } - }} - dropdown={{ - trigger: ["hover"], - placement: "bottom", - disabled: total === 0 - }} - > - } - name=' 导出为...' - disabled={total === 0} +
+ setKeywords(e.target.value)} + placeholder='请输入关键词搜索' + onSearch={onSearch} + onPressEnter={onPressEnter} /> - - + } - disabled={total === 0} - name={selectNum === 0 ? "清空" : "删除"} + type='outline2' + icon={} + onClick={onAllRead} + name='全部已读' /> - - { - onRefreshMenuSelect(key) + { + onExportMenuSelect(key) + } + }} + dropdown={{ + trigger: ["hover"], + placement: "bottom", + disabled: allTotal === 0 + }} + > + } + name=' 导出为...' + disabled={allTotal === 0} + /> + + - } /> - + onConfirm={onRemove} + > + } + disabled={allTotal === 0} + name={selectNum === 0 ? "清空" : "删除"} + /> + + { + onRefreshMenuSelect(key) + } + }} + dropdown={{ + trigger: ["hover"], + placement: "bottom" + }} + > + 0} offset={[-5, 4]}> + } /> + + +
-
+ ) } renderKey='Id' data={response.Data} @@ -1069,7 +1294,7 @@ export const YakitRiskTable: React.FC = React.memo((props) onChangeCheckboxSingle }} pagination={{ - total, + total: allTotal, limit: response.Pagination.Limit, page: response.Pagination.Page, onChange: (page) => update(page) @@ -1080,6 +1305,7 @@ export const YakitRiskTable: React.FC = React.memo((props) useUpAndDown onChange={onTableChange} onRowContextMenu={onRowContextMenu} + {...(tableVirtualResizeProps || {})} /> } secondNode={ @@ -1088,6 +1314,7 @@ export const YakitRiskTable: React.FC = React.memo((props) info={currentSelectItem} className={styles["yakit-risk-details"]} onClickIP={onClickIP} + border={yakitRiskDetailsBorder} /> ) } @@ -1171,7 +1398,7 @@ const YakitRiskSelectTag: React.FC = React.memo((props) }) export const YakitRiskDetails: React.FC = React.memo((props) => { - const {info, isShowTime = true, quotedRequest, quotedResponse, onClose, className = ""} = props + const {info, isShowTime = true, quotedRequest, quotedResponse, onClose, className = "", border = true} = props const severityInfo = useCreation(() => { const severity = SeverityMapTag.filter((item) => item.key.includes(info.Severity || ""))[0] let icon = <> @@ -1255,7 +1482,16 @@ export const YakitRiskDetails: React.FC = React.memo((pro }) return ( <> -
+
{severityInfo.icon} diff --git a/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTableType.d.ts b/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTableType.d.ts index 6c80dec905..5587b76143 100644 --- a/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTableType.d.ts +++ b/app/renderer/src/main/src/pages/risks/YakitRiskTable/YakitRiskTableType.d.ts @@ -1,14 +1,24 @@ import {Paging} from "@/utils/yakQueryHTTPFlow" import {Risk} from "../schema" import {QueryGeneralResponse} from "@/pages/invoker/schema" +import {ReactNode} from "react" +import {TableVirtualResizeProps} from "@/components/TableVirtualResize/TableVirtualResizeType" export interface YakitRiskTableProps { setRiskLoading: (b: boolean) => void /**是否开启高级查询 */ - advancedQuery: boolean - setAdvancedQuery: (b: boolean) => void + advancedQuery?: boolean + setAdvancedQuery?: (b: boolean) => void query: QueryRisksRequest setQuery: (v: QueryRisksRequest) => void + renderTitle?: ReactNode + riskWrapperClassName?: string + tableVirtualResizeProps?: TableVirtualResizeProps + yakitRiskDetailsBorder?: boolean + excludeColumnsKey?: string[] + /**该字段会影响初次请求数据,建议外界只使用,不设值 */ + allTotal?: number + setAllTotal?: (b: number) => void } export interface QueryRisksRequest { @@ -35,19 +45,22 @@ export interface QueryRisksRequest { TagList?: string[] /**IP段 */ IPList?: string[] + + RuntimeId?: string } export type QueryRisksResponse = QueryGeneralResponse export interface YakitRiskDetailsProps { - className?:string + className?: string info: Risk isShowTime?: boolean shrink?: boolean quotedRequest?: string quotedResponse?: string onClose?: () => void - onClickIP?:(info: Risk)=>void + onClickIP?: (info: Risk) => void + border?: boolean } export interface YakitRiskSelectTagProps { diff --git a/app/renderer/src/main/src/pages/risks/YakitRiskTable/constants.ts b/app/renderer/src/main/src/pages/risks/YakitRiskTable/constants.ts index a1ccc29a0a..f41f27ec5c 100644 --- a/app/renderer/src/main/src/pages/risks/YakitRiskTable/constants.ts +++ b/app/renderer/src/main/src/pages/risks/YakitRiskTable/constants.ts @@ -2,7 +2,7 @@ import {genDefaultPagination} from "@/pages/invoker/schema" import {QueryRisksRequest} from "./YakitRiskTableType" export const defQueryRisksRequest: QueryRisksRequest = { - Pagination: {Page: 1, Limit: 50, OrderBy: "created_at", Order: "desc"}, + Pagination: {Page: 1, Limit: 20, OrderBy: "id", Order: "desc"}, Search: "", Network: "", Ports: "", @@ -18,5 +18,6 @@ export const defQueryRisksRequest: QueryRisksRequest = { RiskTypeList: [], SeverityList: [], TagList: [], - IPList: [] + IPList: [], + RuntimeId:'', } diff --git a/app/renderer/src/main/src/pages/risks/YakitRiskTable/utils.ts b/app/renderer/src/main/src/pages/risks/YakitRiskTable/utils.ts index 8c037673ca..436ae0fd75 100644 --- a/app/renderer/src/main/src/pages/risks/YakitRiskTable/utils.ts +++ b/app/renderer/src/main/src/pages/risks/YakitRiskTable/utils.ts @@ -16,7 +16,15 @@ export const apiQueryRisks: (query?: QueryRisksRequest) => Promise Promise = ( + params +) => { + const newParams:QueryRisksRequest = {...params, UntilId:0} + return apiQueryRisks(newParams) +} export interface NewRiskReadRequest { /**@deprecated */ AfterId?: string