From 08ae58ab3e9c241dc8afbfb5b19326319299d69f Mon Sep 17 00:00:00 2001 From: elliotxx <951376975@qq.com> Date: Mon, 6 May 2024 17:25:37 +0800 Subject: [PATCH] feat(ui): support adaptive homepage (#405) ## What type of PR is this? /kind feature ## What this PR does / why we need it: Support adaptive homepage. ![image](https://github.com/KusionStack/karpor/assets/9360247/b9bced00-d824-432a-bff1-dce09fa27c3b) ![image](https://github.com/KusionStack/karpor/assets/9360247/f2e949b6-72de-438c-8cd3-dab5c8e0cef2) ![image](https://github.com/KusionStack/karpor/assets/9360247/c6a910c4-f375-4b0c-a49b-bfd2b7ffd71f) Co-authored-by: hai-tian --- ui/src/components/sqlSearch/index.tsx | 164 +++++++++--------- .../components/sqlSearch/styles.module.less | 56 +++--- ui/src/pages/insight/index.tsx | 25 +-- .../components/exceptionDrawer/index.tsx | 30 +--- .../insightDetail/components/gauge/index.tsx | 86 --------- .../components/k8sStat/index.tsx | 3 +- .../components/summaryCard/index.tsx | 22 ++- ui/src/pages/search/index.tsx | 25 ++- ui/src/pages/search/styles.module.less | 14 +- ui/src/utils/request.ts | 2 +- ui/src/utils/tools.ts | 23 +++ 11 files changed, 199 insertions(+), 251 deletions(-) delete mode 100644 ui/src/pages/insightDetail/components/gauge/index.tsx diff --git a/ui/src/components/sqlSearch/index.tsx b/ui/src/components/sqlSearch/index.tsx index 889c91b8..011d1104 100644 --- a/ui/src/components/sqlSearch/index.tsx +++ b/ui/src/components/sqlSearch/index.tsx @@ -299,7 +299,7 @@ const SqlSearch = ({ sqlEditorValue, handleSearch }: SqlSearchIProps) => { labelSpan.style.textOverflow = 'ellipsis' labelSpan.style.whiteSpace = 'nowrap' labelSpan.style.padding = '0 10px' - labelSpan.style.fontSize = '16px' + labelSpan.style.fontSize = '14px' const btn = document.createElement('span') btn.innerText = '✕' btn.className = 'delete-btn' @@ -429,86 +429,90 @@ const SqlSearch = ({ sqlEditorValue, handleSearch }: SqlSearchIProps) => {
{searchSqlPrefix}
-
ul { - height: auto !important; - max-height: 500px !important; - overflow-y: auto !important; - } - .cm-tooltip.cm-tooltip-autocomplete > ul > li { - background-color: #f3f3f3 !important; - margin: 5px 0 !important; - padding: 10px 0 !important; - border-radius: 6px !important; - width: auto !important; - } +
+
ul { + height: auto !important; + max-height: 500px !important; + overflow-y: auto !important; + } + .cm-tooltip.cm-tooltip-autocomplete > ul > li { + background-color: #f3f3f3 !important; + margin: 5px 0 !important; + padding: 10px 0 !important; + border-radius: 6px !important; + width: auto !important; + } - .cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected='true'], - .cm-tooltip.cm-tooltip-autocomplete > ul > li:hover { - background-color: #97a9f5 !important; - color: white !important; - } - `} - /> -
- -
-
-
- + .cm-tooltip.cm-tooltip-autocomplete + > ul + > li[aria-selected='true'], + .cm-tooltip.cm-tooltip-autocomplete > ul > li:hover { + background-color: #97a9f5 !important; + color: white !important; + } + `} + /> +
+ +
+
+
+ +
diff --git a/ui/src/components/sqlSearch/styles.module.less b/ui/src/components/sqlSearch/styles.module.less index 6582c75c..b978185c 100644 --- a/ui/src/components/sqlSearch/styles.module.less +++ b/ui/src/components/sqlSearch/styles.module.less @@ -9,47 +9,55 @@ align-items: center; .karpor_editor_prefix { + width: 165px; height: 40px; padding-left: 10px; margin-right: 10px; - font-size: 16px; + font-size: 14px; line-height: 40px; color: #999; text-align: center; background: #f1f1f1; border-radius: 40px 0 0 40px; + box-sizing: border-box; } - .karpor_editor_divider { + .karpor_editor_content { display: flex; - height: 40px; - background: #fff; align-items: center; - } + flex: 1; - .karpor_editor_btn_container { - display: flex; - height: 40px; - padding-right: 10px; - background: #fff; - align-items: center; - border-radius: 0 40px 40px 0; + .karpor_editor_divider { + display: flex; + height: 40px; + background: #fff; + align-items: center; + } - .karpor_editor_btn { + .karpor_editor_btn_container { display: flex; - height: 28px; - padding: 0 15px; - color: #fff; - text-align: center; - cursor: pointer; - background: #2f54eb; - border-radius: 40px; - justify-content: center; + height: 40px; + padding-right: 10px; + background: #fff; align-items: center; + border-radius: 0 40px 40px 0; + + .karpor_editor_btn { + display: flex; + height: 28px; + padding: 0 15px; + color: #fff; + text-align: center; + cursor: pointer; + background: #2f54eb; + border-radius: 40px; + justify-content: center; + align-items: center; - img { - width: 20px; - height: 16px; + img { + width: 20px; + height: 14px; + } } } } diff --git a/ui/src/pages/insight/index.tsx b/ui/src/pages/insight/index.tsx index 134a565f..6ec30bcc 100644 --- a/ui/src/pages/insight/index.tsx +++ b/ui/src/pages/insight/index.tsx @@ -12,6 +12,7 @@ import queryString from 'query-string' import { useLocation, useNavigate } from 'react-router-dom' import Loading from '@/components/loading' import { + filterKeywordsOfArray, hasDuplicatesOfObjectArray, isEmptyObject, truncationPageData, @@ -33,28 +34,6 @@ const defalutPageParams = { total: 0, } -export function filterKeywordsOfArray(list, keywords) { - const result = [] - if (keywords?.length === 1) { - list?.forEach((item: any) => { - if (item?.title?.toLowerCase()?.includes(keywords?.[0])) { - result.push(item) - } - }) - } else { - list?.forEach((item: any) => { - if ( - keywords?.every((innerValue: string) => - item?.title?.toLowerCase()?.includes(innerValue), - ) - ) { - result.push(item) - } - }) - } - return result -} - const Insight = () => { const { t } = useTranslation() const location = useLocation() @@ -181,7 +160,7 @@ const Insight = () => { let tmp = allResourcesData?.groups if (keyword) { const keywords = keyword?.toLowerCase()?.trim()?.split(' ') - tmp = filterKeywordsOfArray(allResourcesData?.groups, keywords) + tmp = filterKeywordsOfArray(allResourcesData?.groups, keywords, 'title') } const pageList = truncationPageData({ list: tmp, diff --git a/ui/src/pages/insightDetail/components/exceptionDrawer/index.tsx b/ui/src/pages/insightDetail/components/exceptionDrawer/index.tsx index ccaeb1cd..eb6b354a 100644 --- a/ui/src/pages/insightDetail/components/exceptionDrawer/index.tsx +++ b/ui/src/pages/insightDetail/components/exceptionDrawer/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react' import { Collapse, Drawer, Empty, Input, Pagination, Tag } from 'antd' import { CaretRightOutlined, SearchOutlined } from '@ant-design/icons' import { useTranslation } from 'react-i18next' -import { truncationPageData } from '@/utils/tools' +import { filterKeywordsOfArray, truncationPageData } from '@/utils/tools' import { DEFALUT_PAGE_SIZE_10, SEVERITY_MAP } from '@/utils/constants' import ExceptionStat from '../exceptionStat' import MultiTag from '../multiTag' @@ -16,28 +16,6 @@ type IProps = { open: boolean } -export function filterKeywordsOfArray(list, keywords) { - const result = [] - if (keywords?.length === 1) { - list?.forEach((item: any) => { - if (item?.issue?.title?.toLowerCase()?.includes(keywords?.[0])) { - result.push(item) - } - }) - } else { - list?.forEach((item: any) => { - if ( - keywords?.every((innerValue: string) => - item?.issue?.title?.toLowerCase()?.includes(innerValue), - ) - ) { - result.push(item) - } - }) - } - return result -} - const ExceptionDrawer = ({ open, onClose, exceptionList }: IProps) => { const [pageParams, setPageParams] = useState({ pageNo: 1, @@ -59,7 +37,11 @@ const ExceptionDrawer = ({ open, onClose, exceptionList }: IProps) => { ) if (searchValue) { const keywords = searchValue?.toLowerCase().trim()?.split(' ') - tmp = filterKeywordsOfArray(exceptionList?.issueGroups, keywords) + tmp = filterKeywordsOfArray( + exceptionList?.issueGroups, + keywords, + 'issue.title', + ) } const pageList = truncationPageData({ list: tmp, diff --git a/ui/src/pages/insightDetail/components/gauge/index.tsx b/ui/src/pages/insightDetail/components/gauge/index.tsx deleted file mode 100644 index a8726ace..00000000 --- a/ui/src/pages/insightDetail/components/gauge/index.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useEffect } from 'react' -import { Gauge } from '@antv/g2plot' -import BigNumber from 'bignumber.js' -import { useTranslation } from 'react-i18next' - -type IProps = { - data: number | string -} - -const GaugeChart = ({ data }: IProps) => { - const { t, i18n } = useTranslation() - - useEffect(() => { - if (!data) { - return - } - const container = document.getElementById('gaugeConatiner') - const numData = Number(data) - const color = - numData < 0.6 ? '#F4664A' : numData < 0.8 ? '#FAAD14' : '#30BF78' - const gauge = new Gauge(container, { - percent: numData, - range: { - width: 12, - ticks: [0, 3 / 5, 4 / 5, 1], - color: ['#F4664A', '#FAAD14', '#30BF78'], - }, - gaugeStyle: {}, - indicator: { - pointer: { - style: { - stroke: color, - }, - }, - pin: { - style: { - stroke: color, - }, - }, - }, - axis: { - label: { - formatter(v) { - return Number(v) * 100 - }, - }, - subTickLine: { - count: 10, - }, - }, - statistic: { - title: { - offsetY: 10, - formatter: () => t('HealthScore'), - style: { - color: color, - fontSize: '16px', - fontWeight: 'bold', - }, - }, - content: { - offsetY: -10, - formatter: ({ percent }) => `${new BigNumber(percent).times(100)}`, - style: { - color: color, - fontSize: '16px', - fontWeight: 'bold', - }, - }, - }, - }) - - gauge.render() - - return () => { - if (gauge) { - gauge.destroy() - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [data, i18n.language]) - - return
-} - -export default GaugeChart diff --git a/ui/src/pages/insightDetail/components/k8sStat/index.tsx b/ui/src/pages/insightDetail/components/k8sStat/index.tsx index 21013ae1..d6b534c7 100644 --- a/ui/src/pages/insightDetail/components/k8sStat/index.tsx +++ b/ui/src/pages/insightDetail/components/k8sStat/index.tsx @@ -13,7 +13,8 @@ const K8sStat = ({ statData }: IProps) => { return (
- 全部{statData?.all} + {t('AllIssues')} + {statData?.all}
diff --git a/ui/src/pages/insightDetail/components/summaryCard/index.tsx b/ui/src/pages/insightDetail/components/summaryCard/index.tsx index 84f03a3a..5d04108f 100644 --- a/ui/src/pages/insightDetail/components/summaryCard/index.tsx +++ b/ui/src/pages/insightDetail/components/summaryCard/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect, useRef, useState } from 'react' import { Popover, Progress } from 'antd' import { useLocation } from 'react-router-dom' import queryString from 'query-string' @@ -61,15 +61,33 @@ type SummaryCardProps = { summary: any } const SummaryCard = ({ auditStat, summary }: SummaryCardProps) => { + const [score, setScore] = useState(0) const { t } = useTranslation() const location = useLocation() + const scoreRef = useRef(0) const { type } = queryString.parse(location?.search) + + useEffect(() => { + let timer + if (auditStat?.score) { + timer = setInterval(() => { + scoreRef.current = scoreRef?.current + 10 + if (scoreRef.current > auditStat?.score) { + scoreRef.current = auditStat?.score + } + setScore(scoreRef.current) + if (scoreRef?.current >= auditStat?.score) timer && clearInterval(timer) + }, 100) + } + return () => timer && clearInterval(timer) + }, [auditStat?.score]) + return (
`${percent}`} strokeWidth={12} /> diff --git a/ui/src/pages/search/index.tsx b/ui/src/pages/search/index.tsx index c4529821..253b8c01 100644 --- a/ui/src/pages/search/index.tsx +++ b/ui/src/pages/search/index.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { Tag } from 'antd' import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons' import { useTranslation } from 'react-i18next' @@ -32,6 +32,7 @@ const SearchPage = () => { const [searchType, setSearchType] = useState('sql') const [sqlEditorValue, setSqlEditorValue] = useState('') const [showAll, setShowAll] = useState(false) + const [scale, setScale] = useState(1) const toggleTags = () => { setShowAll(!showAll) @@ -72,8 +73,28 @@ const SearchPage = () => { return renderSqlExamples(sqlExamples) } + useEffect(() => { + const handleResize = () => { + const innerWidth = window.innerWidth + if (innerWidth >= 1200) { + setScale(1) + } else if (innerWidth < 1200 && innerWidth >= 1100) { + setScale(0.9) + } else if (innerWidth < 1100 && innerWidth >= 900) { + setScale(0.8) + } else { + setScale(0.6) + } + } + handleResize() + window.addEventListener('resize', handleResize) + return () => { + window.removeEventListener('resize', handleResize) + } + }, []) + return ( -
+
icon diff --git a/ui/src/pages/search/styles.module.less b/ui/src/pages/search/styles.module.less index 232cb043..c7d7a1aa 100644 --- a/ui/src/pages/search/styles.module.less +++ b/ui/src/pages/search/styles.module.less @@ -16,7 +16,6 @@ justify-content: center; align-items: center; margin-left: -32px; - margin-bottom: 18px; font-size: 28px; font-weight: bold; } @@ -29,34 +28,33 @@ display: flex; width: 650px; padding: 20px 0; - margin-top: 10px; justify-content: center; } .examples { width: 800px; - height: 250px; + height: 130px; + max-height: 260px; padding: 15px; - margin-top: 20px; + margin-top: 10px; border-radius: 8px; .keywords, .sql { display: flex; - margin-top: 7px; color: #000; flex-flow: row wrap; justify-content: center; .karpor_tag { - padding: 4px 8px; + padding: 2px 5px; margin: 4px; font-size: 12px; - font-weight: 500; + font-weight: 400; cursor: pointer; background: rgb(244 244 245); border: 1px solid rgb(228 228 231 / 50%); - border-radius: 8px; + border-radius: 6px; } .toggle_button { diff --git a/ui/src/utils/request.ts b/ui/src/utils/request.ts index 216c7496..e5a9c0c1 100644 --- a/ui/src/utils/request.ts +++ b/ui/src/utils/request.ts @@ -21,7 +21,7 @@ axios.interceptors.response.use( }, error => { try { - message.error(error?.response?.data?.message || '请求失败,请重试') + message.error(error?.response?.data?.message) throw new Error(error) } catch (error) {} }, diff --git a/ui/src/utils/tools.ts b/ui/src/utils/tools.ts index 6933b359..3728467b 100644 --- a/ui/src/utils/tools.ts +++ b/ui/src/utils/tools.ts @@ -1,5 +1,6 @@ import yaml from 'js-yaml' import moment from 'moment' +import _ from 'lodash' export function truncationPageData({ list, page, pageSize }) { return list && list?.length > 0 @@ -162,3 +163,25 @@ export function hasDuplicatesOfObjectArray(array) { } return false } + +export function filterKeywordsOfArray(list, keywords, attribute) { + const result = [] + if (keywords?.length === 1) { + list?.forEach((item: any) => { + if (_.get(item, attribute)?.toLowerCase()?.includes(keywords?.[0])) { + result.push(item) + } + }) + } else { + list?.forEach((item: any) => { + if ( + keywords?.every((innerValue: string) => + _.get(item, attribute)?.toLowerCase()?.includes(innerValue), + ) + ) { + result.push(item) + } + }) + } + return result +}