diff --git a/src/forge/Server/Index.jsx b/src/forge/Server/Index.jsx index 45f6c587a..6712c499e 100644 --- a/src/forge/Server/Index.jsx +++ b/src/forge/Server/Index.jsx @@ -28,6 +28,12 @@ const Quality = Loadable({ loader: () => import('./quality/index'), loading: Loading, }) +// 代码质量分析详情 +const QualityDetail = Loadable({ + loader: () => import('./quality/detail'), + loading: Loading, +}) + function ServerIndex(props){ const { projectDetail } = props; const { owner , projectsId } = props.match.params; @@ -52,7 +58,12 @@ function ServerIndex(props){ () => () } > - () + } + > + () } diff --git a/src/forge/Server/List.jsx b/src/forge/Server/List.jsx index 280c07f56..23e6ddf54 100644 --- a/src/forge/Server/List.jsx +++ b/src/forge/Server/List.jsx @@ -17,13 +17,13 @@ function Main(props){ } }, [projectDetail]) - useEffect(()=>{ - if(current_user && !current_user.login){ - props.history.push(`/login?go_page=/${owner}/${projectsId}/service`); - }else{ - setHas_trace_user(current_user.has_trace_user); - } - },[current_user]) + // useEffect(()=>{ + // if(current_user && !current_user.login){ + // props.history.push(`/login?go_page=/${owner}/${projectsId}/service`); + // }else{ + // setHas_trace_user(current_user.has_trace_user); + // } + // },[current_user]) function onOk(){ setVisible(false); @@ -64,6 +64,16 @@ function Main(props){ 查看详情 +
  • +
    + + 代码质量分析工具 +
    +

    提供自动化的静态代码检查能力,能够识别代码中的缺陷、安全漏洞、代码异味,同时产出代码覆盖率和重复度的详细报告

    + + 查看详情 + +
  • {/* 是站点仓库则显示,否则隐藏 */} {projectDetail && projectDetail.web_site && projectDetail.author.type === "User" &&
  • diff --git a/src/forge/Server/api.js b/src/forge/Server/api.js index 0047ee2e8..d5daf53de 100644 --- a/src/forge/Server/api.js +++ b/src/forge/Server/api.js @@ -1,9 +1,10 @@ import fetch from './fetch'; // 获取列表 -export function getList(params) { +export function getList(params, owner, repo) { return fetch({ - url: `/api/issues/search?components=kingchanx-fluid-cloudnative_fluid&s=FILE_LINE&issueStatuses=CONFIRMED%2COPEN&ps=100&&additionalFields=_all&timeZone=Asia%2FShanghai`, + // url: `/api/v1/${owner}/${repo}/sonarqubes/issues_search.json`, + url: `/api/issues/search?components=kingchanx-fluid-cloudnative_fluid&s=FILE_LINE&issueStatuses=CONFIRMED%2COPEN&&additionalFields=_all&timeZone=Asia%2FShanghai`, method: 'get', params }); diff --git a/src/forge/Server/constants/quality.js b/src/forge/Server/constants/quality.js index 6921f9d55..2cf44037a 100644 --- a/src/forge/Server/constants/quality.js +++ b/src/forge/Server/constants/quality.js @@ -1,3 +1,10 @@ +import bugs from '../img/quality/bugs.png' +import vulnerabilities from '../img/quality/vulnerabilities.png' +import code_smells from '../img/quality/code_smells.png' +import low from '../img/quality/low.png' +import medium from '../img/quality/medium.png' +import high from '../img/quality/high.png' + export const issueType = { BUG: 'BUG', VULNERABILITY: '漏洞', @@ -13,4 +20,16 @@ export const maintainability = { LOW: '低', MEDIUM: '中', HIGH: '高' +} + +export const issueTypeIcon = { + BUG: bugs, + VULNERABILITY: vulnerabilities, + CODE_SMELL: code_smells +} + +export const maintainabilityIcon = { + LOW: low, + MEDIUM: medium, + HIGH: high } \ No newline at end of file diff --git a/src/forge/Server/img/logo3-1.png b/src/forge/Server/img/logo3-1.png new file mode 100644 index 000000000..c2a6c85f0 Binary files /dev/null and b/src/forge/Server/img/logo3-1.png differ diff --git a/src/forge/Server/img/logo3.png b/src/forge/Server/img/logo3.png index c2a6c85f0..66c617f97 100644 Binary files a/src/forge/Server/img/logo3.png and b/src/forge/Server/img/logo3.png differ diff --git a/src/forge/Server/img/logo4-1.png b/src/forge/Server/img/logo4-1.png new file mode 100644 index 000000000..37e43bacc Binary files /dev/null and b/src/forge/Server/img/logo4-1.png differ diff --git a/src/forge/Server/img/logo4.png b/src/forge/Server/img/logo4.png new file mode 100644 index 000000000..cf9d0317f Binary files /dev/null and b/src/forge/Server/img/logo4.png differ diff --git a/src/forge/Server/img/quality/back.png b/src/forge/Server/img/quality/back.png new file mode 100644 index 000000000..460e55a99 Binary files /dev/null and b/src/forge/Server/img/quality/back.png differ diff --git a/src/forge/Server/img/quality/bugs.png b/src/forge/Server/img/quality/bugs.png new file mode 100644 index 000000000..a921c64b5 Binary files /dev/null and b/src/forge/Server/img/quality/bugs.png differ diff --git a/src/forge/Server/img/quality/code_lines.png b/src/forge/Server/img/quality/code_lines.png new file mode 100644 index 000000000..7ffec82cf Binary files /dev/null and b/src/forge/Server/img/quality/code_lines.png differ diff --git a/src/forge/Server/img/quality/code_smells.png b/src/forge/Server/img/quality/code_smells.png new file mode 100644 index 000000000..ca7434e39 Binary files /dev/null and b/src/forge/Server/img/quality/code_smells.png differ diff --git a/src/forge/Server/img/quality/cover.png b/src/forge/Server/img/quality/cover.png new file mode 100644 index 000000000..44a53a6cb Binary files /dev/null and b/src/forge/Server/img/quality/cover.png differ diff --git a/src/forge/Server/img/quality/folder.png b/src/forge/Server/img/quality/folder.png new file mode 100644 index 000000000..2ce02b8ac Binary files /dev/null and b/src/forge/Server/img/quality/folder.png differ diff --git a/src/forge/Server/img/quality/high.png b/src/forge/Server/img/quality/high.png new file mode 100644 index 000000000..ce67e4739 Binary files /dev/null and b/src/forge/Server/img/quality/high.png differ diff --git a/src/forge/Server/img/quality/label.png b/src/forge/Server/img/quality/label.png new file mode 100644 index 000000000..e2c06300e Binary files /dev/null and b/src/forge/Server/img/quality/label.png differ diff --git a/src/forge/Server/img/quality/low.png b/src/forge/Server/img/quality/low.png new file mode 100644 index 000000000..25c563d8e Binary files /dev/null and b/src/forge/Server/img/quality/low.png differ diff --git a/src/forge/Server/img/quality/medium.png b/src/forge/Server/img/quality/medium.png new file mode 100644 index 000000000..1b8814cec Binary files /dev/null and b/src/forge/Server/img/quality/medium.png differ diff --git a/src/forge/Server/img/quality/repeat.png b/src/forge/Server/img/quality/repeat.png new file mode 100644 index 000000000..2b0bad6c0 Binary files /dev/null and b/src/forge/Server/img/quality/repeat.png differ diff --git a/src/forge/Server/img/quality/vulnerabilities.png b/src/forge/Server/img/quality/vulnerabilities.png new file mode 100644 index 000000000..6030bdd26 Binary files /dev/null and b/src/forge/Server/img/quality/vulnerabilities.png differ diff --git a/src/forge/Server/quality/detail.jsx b/src/forge/Server/quality/detail.jsx index 252dd0dab..172f9f122 100644 --- a/src/forge/Server/quality/detail.jsx +++ b/src/forge/Server/quality/detail.jsx @@ -2,40 +2,40 @@ import React, {useState, useEffect, useRef } from "react"; import { getList, getCodeSnippets, getProblemDetail } from "../api"; import './detail.scss'; import ProblemDetail from "./component/ProblemDetail"; -import { issueType } from "../constants/quality" +import { issueType, issueTypeIcon } from "../constants/quality" +import folderImg from '../img/quality/folder.png' +import backImg from '../img/quality/back.png' +import { Divider } from 'antd'; function issueDetail(props){ const { owner , projectsId } = props.match.params; const [ snippetsDetail, setSnippetsDetail ] = useState(null); - const [ issues, setIssues ] = useState(null); + const [ issues, setIssues ] = useState({}); const [ selectedIssue, setSelectedIssue ] = useState(null); const [ selectedFlow, setSelectedFlow ] = useState(null); const [ detailVisible, setDetailVisible ] = useState(true); const [ issueInfo, setIssueInfo ] = useState(null); const [reload, setReload] = useState(); - const { projectDetail } = props; + const [pageNum, setPageNum] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [total, setTotal] = useState() + const { projectDetail, history } = props; const codeRef = useRef(null); useEffect(()=>{ // 更新网页标题 if(projectDetail){ - const { author, name} = projectDetail; - document.title = `reposyncer-${author.name}/${name}`; + if (history.location.state?.baseInfo) { + handleIssues(history.location.state?.baseInfo) + } else { + getIssueList() + } + if (history.location.state?.selectedIssue) { + setSelectedIssue(history.location.state?.selectedIssue) + } } - getList().then(res => { - let issues = res.issues - let issuesHandled = {} - issues.forEach(e => { - if (issuesHandled[e.component]) { - issuesHandled[e.component].push(e) - } else { - issuesHandled[e.component] = [e] - } - }); - setIssues(issuesHandled) - }) - }, [projectDetail]) + }, [projectDetail, pageNum]) useEffect(() => { if (selectedIssue) { @@ -49,7 +49,31 @@ function issueDetail(props){ useEffect(()=>{ },[reload]) - function addSpecialClassToRange(code, textRange, className) { + const getIssueList = (owner, name) => { + let params = { + p: pageNum, + ps: pageSize + }; + getList(params).then(res => { + handleIssues(res) + }) + }; + + const handleIssues = (data) => { + let newIssues = data.issues + let issuesHandled = JSON.parse(JSON.stringify(issues)) + setTotal(data.total) + newIssues.forEach(e => { + if (issuesHandled[e.component]) { + issuesHandled[e.component].push(e) + } else { + issuesHandled[e.component] = [e] + } + }); + setIssues(issuesHandled) + } + + const addSpecialClassToRange = (code, textRange, className) => { let textOnly = code.replace(/<[^>]+>/g, ''); // Remove HTML tags to get text only let start = 0; let end = textOnly.length; @@ -145,14 +169,16 @@ function issueDetail(props){ return
    setDetailVisible(false)}>
    + history.goBack()}/> + { issues && Object.entries(issues).map(([key, item]) =>
    -
    { key.split(`:`)[1] }
    +
    { key.split(`:`)[1] }
    { item.map(e => (
    setSelectedIssue(e)}>

    { e.message }

    -

    { issueType[e.type] }{ e.flows.length > 0 && +{e.flows.length} }

    +

    { issueType[e.type] }{ e.flows.length > 0 && +{e.flows.length} }

    { e.key === selectedIssue?.key && e.flows && e.flows.length > 0 && e.flows.map((flow, index) => { return
    selectAndJumpRepeat(flow.locations[0].textRange.startLine) }> @@ -167,10 +193,11 @@ function issueDetail(props){
    ) } + { pageNum * pageSize < total &&
    setPageNum(pageNum + 1)}>查看更多
    }
    { snippetsDetail &&
    -
    { snippetsDetail.component.path }
    +
    { snippetsDetail.component.path }
    {snippetsDetail.sources.map((item, index) => ( diff --git a/src/forge/Server/quality/detail.scss b/src/forge/Server/quality/detail.scss index e01990acc..a49bfb454 100644 --- a/src/forge/Server/quality/detail.scss +++ b/src/forge/Server/quality/detail.scss @@ -5,14 +5,42 @@ overflow-y: auto; width: 350px; flex-shrink: 0; + .back-icon { + width: 24px; + } + .ant-divider { + padding-right: 10px; + width: 330px; + } + .load-more { + text-align: center; + padding: 4px; + margin-right: 10px; + margin-right: 10px; + background: #FAFCFF; + border-radius: 3px 3px 3px 3px; + border: 1px solid rgba(42,97,255,0.23); + font-size: 14px; + color: #111010; + cursor: pointer; + } .doc-title { - width: 250px; - overflow: hidden; - white-space: nowrap; /* 防止文本换行 */ - overflow: hidden; /* 隐藏溢出的文本 */ - text-overflow: ellipsis; /* 显示省略号 */ - direction: rtl; /* 文字从右到左排列 */ - text-align: left; /* 文字从左边开始剪裁 */ + display: flex; + align-items: center; + img { + width: 18px; + margin-right: 10px; + } + span { + display: inline-block; + width: 240px; + overflow: hidden; + white-space: nowrap; /* 防止文本换行 */ + overflow: hidden; /* 隐藏溢出的文本 */ + text-overflow: ellipsis; /* 显示省略号 */ + direction: rtl; /* 文字从右到左排列 */ + text-align: left; /* 文字从左边开始剪裁 */ + } margin-top: 15px; margin-bottom: 20px; } @@ -23,7 +51,6 @@ border: 1px solid rgba(42,97,255,0.23); margin-bottom: 15px; margin-right: 10px; - font-size: 14px; word-break: break-word; font-family: PingFang SC, PingFang SC; font-size: 14px; @@ -32,6 +59,14 @@ &:hover { border: 1px solid #466AFF; } + .issue-typeline { + display: flex; + align-items: center; + img { + width: 14px; + margin-right: 5px; + } + } .issue-type { font-weight: 400; color: #5D6790; @@ -96,6 +131,12 @@ color: #111010; padding-left: 20px; line-height: 42px; + display: flex; + align-items: center; + img { + width: 18px; + margin-right: 10px; + } } .content { display: flex; @@ -174,36 +215,69 @@ border-radius: 4px 4px 4px 4px; border: 2px solid #FFFFFF; } + .count-title { + font-weight: bold; + font-size: 20px; + color: #333333; + margin: 28px 0; + } .issue-list { display: flex; .left-content { width: 218px; flex-shrink: 0; - .filter-title { - font-weight: bold; - font-size: 17px; - color: #000000; - padding: 20px; - } - .filter-item { - padding: 0 20px; - margin: 2px 0; - display: flex; - justify-content: space-between; - align-items: center; - height: 50px; + .clear-button { + margin: 17px 0 0 19px; + padding: 0 7px; + height: 28px; + border-radius: 3px 3px 3px 3px; + border: 1px solid #D4333F; + font-weight: 400; + font-size: 14px; + color: #D4333F; cursor: pointer; - &:hover { - background: #466bff0c; + } + .filters { + .filter-title { + font-weight: bold; + font-size: 17px; + color: #000000; + padding: 20px; + line-height: 28px; + .clear-button { + float: right; + margin: 0; + } + } + .filter-item { + padding: 0 20px; + margin: 2px 0; + display: flex; + justify-content: space-between; + align-items: center; + height: 50px; + cursor: pointer; + &:hover { + background: #466bff0c; + } + .item-type { + display: flex; + align-items: center; + img { + width: 16px; + margin-right: 5px; + } + } + } + .selected-item { + background: #466bff17; + } + .ant-divider { + margin: 20px; + margin-bottom: 0px; + width: auto; + min-width: unset; } - } - .selected-item { - background: #466bff17; - } - .ant-divider { - margin: 20px; - width: auto; - min-width: unset; } } .right-content { @@ -228,6 +302,7 @@ font-size: 14px; color: #5D6790; margin-top: 10px; + display: flex; > span:nth-child(2), > span:nth-child(3) { &::before { content: "|"; @@ -237,7 +312,7 @@ } } span:nth-of-type(n + 4) { - float: right; + margin-left: auto; } } .issue-message { @@ -261,6 +336,11 @@ } .total-info { padding: 20px; + p { + font-weight: bold; + font-size: 18px; + color: #333333; + } .info-list { display: flex; .total-item { @@ -270,6 +350,7 @@ font-weight: 400; font-size: 15px; color: #525151; + margin-top: 18px; > span:nth-child(2) { font-weight: bold; font-size: 18px; @@ -295,10 +376,48 @@ left: -40px; content: ''; width: 0px; - height: 53px; + height: 100%; border-left: 1px solid rgba(187,189,197,0.61); } } } } +} + +.headBox{ + height:60px; + line-height: 60px; + background-color:#fafcff; + border:1px solid rgba(42, 97, 255, 0.23); + border-radius:3px 3px 0px 0px; + color:#333333; + position: relative; +} + +.nullStoreBox{ + background-color:#fafcff; + border-radius:4px 4px 0px 0px; + text-align: center; + color:#333333; + padding-bottom: 65px; + .loBox{width: 68px;} + .introBox{ + color:#666666; + width: 57%; + margin: 15px auto; + } + .borBox{ + width: 45%; + margin: 0 auto 20px; + border-bottom: 1px solid rgba(90, 117, 193, 0.23); + } +} + +.icon-text { + display: flex; + align-items: center; + img { + width: 16px; + margin-right: 5px; + } } \ No newline at end of file diff --git a/src/forge/Server/quality/index.jsx b/src/forge/Server/quality/index.jsx index 95d6696b5..f1b4de18d 100644 --- a/src/forge/Server/quality/index.jsx +++ b/src/forge/Server/quality/index.jsx @@ -1,38 +1,48 @@ -import React, {useState, useEffect, useRef } from "react"; +import React, { useState, useEffect, Fragment } from "react"; import { getList, getCodeSnippets, getProblemDetail, getProblemData } from "../api"; import './detail.scss'; import ProblemDetail from "./component/ProblemDetail"; -import { issueType, issueTypeKeys, maintainability } from "../constants/quality" -import { Divider } from 'antd'; -import { formatNumber } from '../../Utils/utils' +import { issueType, maintainability, issueTypeIcon, maintainabilityIcon } from "../constants/quality"; +import { Divider, Pagination, Button } from 'antd'; +import { formatNumber, formatNumberWithCommas } from '../../Utils/utils'; +import logo from '../img/logo4-1.png'; +import labelIcon from '../img/quality/label.png'; +import code_lines from '../img/quality/code_lines.png'; +import repeat from '../img/quality/repeat.png'; +import cover from '../img/quality/cover.png'; +import { Link } from "react-router-dom"; + +function IssueList(props) { + const [issues, setIssues] = useState({}); + const [baseInfo, setBaseInfo] = useState({}); + const [baseData, setBaseData] = useState({}); + const [listFilter, setListFilter] = useState({}); + const [selectedIssue, setSelectedIssue] = useState(null); + const [detailVisible, setDetailVisible] = useState(true); + const [issueInfo, setIssueInfo] = useState({}); + const [selectedType, setSelectedType] = useState([]); + const [selectedSeverity, setSelectedSeverity] = useState([]); + const [selectedTag, setSelectedTag] = useState([]); + const [pageNum, setPageNum] = useState(1); + const [pageSize, setPageSize] = useState(10); + const { projectDetail, history } = props; + const metricKeys = 'lines,coverage,lines_to_cover,duplicated_lines_density,maintainability_issues,vulnerabilities,bugs,code_smells'; + const facets = 'impactSeverities,tags,types'; + const { owner , projectsId } = props.match.params; -function issueList(props){ - const [ issues, setIssues ] = useState({}); - const [ baseInfo, setBaseInfo ] = useState({}); - const [ baseData, setBaseData ] = useState({}); - const [ listFilter, setListFilter ] = useState({}); - const [ selectedIssue, setSelectedIssue ] = useState(null); - const [ detailVisible, setDetailVisible ] = useState(true); - const [ issueInfo, setIssueInfo ] = useState({}); - const [ selectedType, setSelectedType ] = useState([]); - const [ selectedSeverity, setSelectedSeverity ] = useState([]); - const [ selectedTag, setSelectedTag ] = useState([]); - const { projectDetail } = props; - const metricKeys = 'lines,coverage,lines_to_cover,duplicated_lines_density,maintainability_issues,vulnerabilities,bugs,code_smells' - const facets = 'impactSeverities,tags,types' useEffect(() => { - let newContainer = document.getElementsByClassName("newContainer")[0] - newContainer.style.backgroundColor = '#F7F9FCFF' + let newContainer = document.getElementsByClassName("newContainer")[0]; + newContainer.style.backgroundColor = '#F7F9FCFF'; return () => { - newContainer.style.backgroundColor = '#FFFFFF' - } - },[]) + newContainer.style.backgroundColor = '#FFFFFF'; + }; + }, []); - useEffect(()=>{ - // 更新网页标题 - if(projectDetail){ - const { author, name} = projectDetail; - document.title = `reposyncer-${author.name}/${name}`; + useEffect(() => { + if (projectDetail) { + const { author, name } = projectDetail; + document.title = `代码质量分析-${author.name}/${name}`; + getIssueList(author.name, name); } getProblemData('kingchanx-fluid-cloudnative_fluid', metricKeys).then(res => { const measures = res.component.measures; @@ -40,181 +50,195 @@ function issueList(props){ measures.forEach(measure => { result[measure.metric] = measure.value; }); - setBaseData(result) - }) - getIssueList() - }, [projectDetail]) + setBaseData(result); + }); + }, [projectDetail]); - function getIssueList() { + const getIssueList = (owner, name) => { let params = { facets: facets, types: selectedType.join(','), impactSeverities: selectedSeverity.join(','), tags: selectedTag.join(','), - } - getList(params).then(res => { - let issues = res.issues - let issuesHandled = {} - issues.forEach(e => { - if (issuesHandled[e.component]) { - issuesHandled[e.component].push(e) + p: pageNum, + ps: pageSize + }; + getList(params, owner, name).then(res => { + const issues = res.issues.reduce((acc, issue) => { + if (acc[issue.component]) { + acc[issue.component].push(issue); } else { - issuesHandled[e.component] = [e] + acc[issue.component] = [issue]; } - }); - const filter = {} - res.facets.forEach(e => { - filter[e.property] = e.values - }) - setListFilter(filter) - setBaseInfo(res) - setIssues(issuesHandled) - }) - } + return acc; + }, {}); + + const filter = res.facets.reduce((acc, facet) => { + acc[facet.property] = facet.values; + return acc; + }, {}); + + setListFilter(filter); + setBaseInfo(res); + setIssues(issues); + }); + }; useEffect(() => { - getIssueList() - },[selectedType, selectedSeverity, selectedTag]) + setPageNum(1) + }, [selectedType, selectedSeverity, selectedTag]); - function detail(e) { - setSelectedIssue(e) + useEffect(() => { + getIssueList(); + }, [pageNum, pageSize]); + + const detail = (e) => { + setSelectedIssue(e); getProblemDetail(e.rule).then(res => { - setIssueInfo(res?.rule) - setDetailVisible(true) + setIssueInfo(res?.rule); + setDetailVisible(true); + }); + }; + + const toggleSelection = (state, setter, value) => { + const index = state.indexOf(value); + if (index > -1) { + state.splice(index, 1); + setter([...state]); + } else { + setter([...state, value]); + } + }; + + const renderFilterItems = (items, state, setter, label) => ( +
    +
    + {label} + { state.length > 0 && } +
    + {items && items.map(e => ( +
    toggleSelection(state, setter, e.val)} + > + {label === "问题类型" ? issueType[e.val] : maintainability[e.val] || e.val} + {e.count} +
    + ))} + +
    + ); + + const renderTotalItem = (label, value, icon) => ( +
    + {label} + {value} +
    + ); + + const renderDetailedItem = (label, value, percent, moreValue, icon) => ( +
    + {label} + + {percent}% + {moreValue}{formatNumber(value)} + +
    + ); + + const toDetail = (e) => { + history.push({ + pathname: `/${owner}/${projectsId}/service/quality/detail`, + state: { + baseInfo, + selectedIssue: e + } }) } - function selectType(e) { - let index = selectedType.indexOf(e) - if (index > -1) { - selectedType.splice(index, 1) - setSelectedType([...selectedType]) - } else { - setSelectedType([...selectedType, e]) - } - } - - function selectSeverity(e) { - let index = selectedSeverity.indexOf(e) - if (index > -1) { - selectedSeverity.splice(index, 1) - setSelectedSeverity([...selectedSeverity]) - } else { - setSelectedSeverity([...selectedSeverity, e]) - } - } - - function selectTags(e) { - let index = selectedTag.indexOf(e) - if (index > -1) { - selectedTag.splice(index, 1) - setSelectedTag([...selectedTag]) - } else { - setSelectedTag([...selectedTag, e]) - } - } - - return
    - setDetailVisible(false)}> -
    -

    代码分析结果

    -
    -
    - Bugs - { baseData.bugs } -
    -
    - 漏洞 - { formatNumber(baseData.vulnerabilities) } -
    -
    - 异味 - { formatNumber(baseData.code_smells) } -
    -
    - 重复率 - { baseData.duplicated_lines_density }% 代码{ formatNumber(baseData.lines) } -
    -
    - 覆盖率 - { baseData.coverage }% 代码{ formatNumber(baseData.lines_to_cover) } -
    -
    - 代码行数 - { baseData.lines && baseData.lines.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") } -
    -
    -
    - {issues &&

    发现{ baseInfo.total }个问题

    } -
    -
    -
    -
    - 问题类型 {} + return ( +
    + setDetailVisible(false)} + /> + { + false && +
    sonar代码质量分析工具
    +
    + +

    欢迎使用代码质量分析工具

    +
    代码质量分析工具通过静态代码分析帮助开发者识别和修复代码库潜在的缺陷、代码异味、复杂度过高的代码段以及潜在的安全漏洞。同时能够评估代码的结构和风格,提供关于代码覆盖率、重复率等关键指标的深入洞察。
    +
    +
    - { - listFilter.types && listFilter.types.map(e => ( -
    selectType(e.val)}> - { issueType[e.val] } - { e.count } -
    )) - } - -
    -
    -
    - 严重程度 -
    - { - listFilter.impactSeverities && listFilter.impactSeverities.map(e => ( -
    selectSeverity(e.val)}> - { maintainability[e.val] } - { e.count } -
    )) - } - -
    -
    -
    - 标签 -
    - { - listFilter.tags && listFilter.tags.map(e => ( -
    selectTags(e.val)}> - { e.val } - { e.count } -
    )) - } -
    -
    -
    - { - issues && Object.entries(issues).map(([key, item]) =>
    -
    { key.split(`:`)[1] }
    - { - item.map(e => ( -
    setSelectedIssue(e)}> -

    { e.message } detail(e)}>问题分析L{ e.line }

    -

    - { issueType[e.type] } - { maintainability[e.impacts[0].severity] } - { e.debt }工作 - - { - e.tags && e.tags.map((tag, index) => ( - { tag }{ index < e.tags.length - 1 ? ',' : '' } - )) - } - -

    + + } + { + true &&
    +
    +

    代码分析结果

    +
    + {renderTotalItem("Bugs", baseData.bugs, issueTypeIcon.BUG)} + {renderTotalItem("漏洞", formatNumber(baseData.vulnerabilities), issueTypeIcon.VULNERABILITY)} + {renderTotalItem("异味", formatNumber(baseData.code_smells), issueTypeIcon.CODE_SMELL)} + {renderDetailedItem("重复率", baseData.lines, baseData.duplicated_lines_density, "代码", repeat)} + {renderDetailedItem("覆盖率", baseData.lines_to_cover, baseData.coverage, "代码", cover)} + {renderTotalItem("代码行数", formatNumberWithCommas(baseData.lines), code_lines)} +
    - )) - } -
    - ) - } -
    + {issues &&
    发现{ baseData.maintainability_issues && JSON.parse(baseData.maintainability_issues).total }个问题
    } +
    +
    + { (selectedType.length > 0 || selectedSeverity.length > 0 || selectedTag .length > 0) && } + {renderFilterItems(listFilter.types, selectedType, setSelectedType, "问题类型")} + {renderFilterItems(listFilter.impactSeverities, selectedSeverity, setSelectedSeverity, "严重程度")} + {renderFilterItems(listFilter.tags, selectedTag, setSelectedTag, "标签")} +
    +
    + {issues && Object.entries(issues).map(([key, item]) => ( +
    +
    + {key.split(':')[1]} +
    + {item.map(e => ( +
    toDetail(e)} + > +

    + {e.message} + detail(e)}>问题分析 + L{e.line} +

    +

    + {issueType[e.type]} + {maintainability[e.impacts[0].severity]} + {e.debt}工作 + + {e.tags && e.tags.length > 0 && } + {e.tags && e.tags.map((tag, index) => ( + + {tag}{index < e.tags.length - 1 ? ',' : ''} + + ))} + +

    +
    + ))} +
    + ))} + setPageNum(e)} onShowSizeChange={(current, size) => setPageSize(size)} /> +
    +
    +
    + }
    -
    + ); } -export default issueList; \ No newline at end of file + +export default IssueList; diff --git a/src/forge/Server/reposyncer/component/storeList.jsx b/src/forge/Server/reposyncer/component/storeList.jsx index 0580435ef..86992e01b 100644 --- a/src/forge/Server/reposyncer/component/storeList.jsx +++ b/src/forge/Server/reposyncer/component/storeList.jsx @@ -2,7 +2,7 @@ import React, { Fragment, useEffect, useState} from "react"; import { Button, Modal, Icon, message, Select, Input, Form, Radio, Table, Divider, Badge, Spin, Checkbox } from "antd"; import { Link } from "react-router-dom"; import gitlinkLogo from '../../img/gitlink.jpg'; -import logo from '../../img/logo3.png'; +import logo from '../../img/logo3-1.png'; import '../index.scss'; import axios from "axios"; import AddTaskModal from './addTaskModal'; diff --git a/src/forge/Utils/utils.js b/src/forge/Utils/utils.js index 6aa8c57f7..085bfd5c6 100644 --- a/src/forge/Utils/utils.js +++ b/src/forge/Utils/utils.js @@ -9,4 +9,8 @@ export function formatNumber(num) { } else { return num.toString(); // 小于 1000 的数字原样返回 } +} +export function formatNumberWithCommas(num) { + if (num === undefined || num === null) return + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } \ No newline at end of file