merge:合并代码

This commit is contained in:
lilan 2025-06-05 15:56:32 +08:00
commit 63136b1620
40 changed files with 945 additions and 314 deletions

View File

@ -124,6 +124,7 @@
"react-router-dom": "^4.2.2",
"react-slick": "^0.28.1",
"react-split-pane": "^0.1.91",
"react-svg": "^16.3.0",
"react-url-query": "^1.5.0",
"react-zmage": "^0.8.5-beta.31",
"redux": "^4.0.5",

View File

@ -39,7 +39,7 @@ export function initAxiosInterceptors(props) {
return config
}
requestProxy(config);
let url = `/api${config.url}`;
let url = config.url.startsWith('/api') ? config.url : `/api${config.url}`;
// if (`${config[0]}` !== `true`) {
// if (window.location.port === "3007") {

View File

@ -242,7 +242,11 @@ function CoderDepot(props){
let ele = document.getElementById("ptxt");
if(ele){
let h = ele.offsetHeight;
if( h <= 36 ) setHideBtn(false);
if( h <= 36 ) {
setHideBtn(false);
} else {
setHideBtn(true)
}
}
}
},[projectDetail,lastCommit])
@ -589,7 +593,7 @@ function CoderDepot(props){
{
lastCommit.message &&
<div className={hideBtn && hide ? "ellipsistxt hidetxt" :"ellipsistxt"}>
<pre id="ptxt"><Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(lastCommit.sha)}`}><RenderHtml value={lastCommit.message}/></Link></pre>
<pre id="ptxt"><Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(lastCommit.sha)}`}><div className="markdown-body"><p>{lastCommit.message}</p></div></Link></pre>
</div>
}
{ hideBtn && __CLIENT__ && <div className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></div> }

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { Route, Switch } from 'react-router-dom';
import "./index.scss";
import Loadable from 'react-loadable';
@ -46,6 +46,7 @@ const MeasurementDetail = Loadable({
function ServerIndex(props){
const { projectDetail } = props;
const { owner , projectsId } = props.match.params;
const [ qualityClass, setQualityClass ] = useState(false)
useEffect(()=>{
// tab 3397
@ -54,8 +55,12 @@ function ServerIndex(props){
}
}, [projectDetail])
const changeClass = (isServer = false ) => {
setQualityClass(isServer);
}
return(
<div className="panels">
<div className={`panels ${ qualityClass && 'panels-quality' }`}>
<Switch {...props}>
<Route path="/:owner/:projectsId/service/measurement/:type"
render={
@ -79,12 +84,12 @@ function ServerIndex(props){
></Route>
<Route path="/:owner/:projectsId/service/quality/detail"
render={
() => (<QualityDetail {...props}/>)
() => (<QualityDetail {...props} changeClass={ changeClass } />)
}
></Route>
<Route path="/:owner/:projectsId/service/quality"
render={
() => (<Quality {...props}/>)
() => (<Quality {...props} changeClass={ changeClass }/>)
}
></Route>
{/* 个人建站服务 */}

View File

@ -70,7 +70,7 @@ function Main(props){
</li> */}
<li>
<div className="servername" style={{marginBottom: '8px'}}>
<img src={require('./img/logo4.png')} alt="" style={{width: '24px'}}/>
<img src={require('./img/logoq.png')} alt=""/>
<Link to={`/${owner}/${projectsId}/service/quality`}>代码质量分析工具</Link>
</div>
<p className="task-hide-2 serverdesc">提供自动化的静态代码检查能力能够识别代码中的缺陷安全漏洞代码异味同时产出代码覆盖率和重复度的详细报告</p>

View File

@ -67,3 +67,19 @@ export function getActionList(owner, repo ) {
});
}
// 获取classes、files地址
export function getAnalizeUrl(owner, repo ) {
return fetch({
url: `/api/v1/${owner}/${repo}/sonarqubes/doxygen_url`,
method: 'get',
});
}
// 获取方法调用分析
export function getFuncAnalize(owner, repo ) {
return fetch({
url: `/api/v1/${owner}/${repo}/sonarqubes/analyze_doxygen`,
method: 'get',
});
}

View File

@ -1,9 +1,16 @@
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'
import bugs from '../img/quality/bugs.svg'
import vulnerabilities from '../img/quality/vulnerabilities.svg'
import code_smells from '../img/quality/code_smells.svg'
import blocked from '../img/quality/blocked.svg'
import low from '../img/quality/low.svg'
import medium from '../img/quality/medium.svg'
import high from '../img/quality/high.svg'
import label1 from '../img/quality/label1.svg'
import label2 from '../img/quality/label2.svg'
import label3 from '../img/quality/label3.svg'
import label4 from '../img/quality/label4.svg'
import label5 from '../img/quality/label5.svg'
export const issueType = {
BUG: 'BUG',
@ -17,9 +24,10 @@ export const issueTypeKeys = {
CODE_SMELL: 'code_smells'
}
export const maintainability = {
LOW: '低',
MEDIUM: '中',
HIGH: '高'
LOW: '次要',
MEDIUM: '主要',
HIGH: '严重',
BLOCKED: '阻塞'
}
export const issueTypeIcon = {
@ -31,5 +39,14 @@ export const issueTypeIcon = {
export const maintainabilityIcon = {
LOW: low,
MEDIUM: medium,
HIGH: high
HIGH: high,
BLOCKED: blocked
}
export const labelIcon = {
0: label1,
1: label2,
2: label3,
3: label4,
4: label5
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="阻断" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<path id="减去_16" data-name="减去 16" d="M10421.105,11189.506a5.214,5.214,0,1,1,4.424-1.475A6.045,6.045,0,0,1,10421.105,11189.506Zm0-3.364a1.182,1.182,0,1,0,1.133,1.183A1.191,1.191,0,0,0,10421.105,11186.142Zm0-7.259a1.075,1.075,0,0,0-1.184.907l.447,4.681c0,.458.057.9.736.9s.736-.446.736-.9l.443-4.681A1.072,1.072,0,0,0,10421.105,11178.883Z" transform="translate(-10157.9 -10850.401)" fill="#d4333f"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 708 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g id="bugs" transform="translate(-253.245 -323.244)">
<rect id="矩形_639" data-name="矩形 639" width="16" height="16" transform="translate(253.245 323.244)" fill="#a94444" opacity="0"/>
<path id="bugs-2" data-name="bugs" d="M12.667,7.236a.53.53,0,0,1-.507.523H10.387a4.75,4.75,0,0,1-.53,2.335L11.5,11.788a.491.491,0,0,1,.15.364.5.5,0,0,1-.863.364L9.207,10.933l-.119.1-.333.23a4.75,4.75,0,0,1-.515.293,3.539,3.539,0,0,1-.649.238,2.89,2.89,0,0,1-.792.1V4.655H5.827v7.22a3.048,3.048,0,0,1-.792-.111,3.373,3.373,0,0,1-.7-.261l-.523-.317a3.57,3.57,0,0,1-.34-.261l-.119-.119L1.9,12.5a.5.5,0,0,1-.38.166.507.507,0,0,1-.34-.127.538.538,0,0,1-.166-.364A.523.523,0,0,1,1.14,11.8L2.723,9.967A4.869,4.869,0,0,1,2.28,7.758H.507A.491.491,0,0,1,.151,7.6.491.491,0,0,1,0,7.236a.5.5,0,0,1,.15-.364.483.483,0,0,1,.356-.15H2.28V4.346l-1.37-1.4a.475.475,0,0,1-.119-.356.491.491,0,0,1,.15-.364.483.483,0,0,1,.713,0L2.993,3.618H9.674l1.409-1.393a.507.507,0,0,1,.792.364.475.475,0,0,1-.15.356l-1.338,1.4V6.721H12.16a.483.483,0,0,1,.356.15.5.5,0,0,1,.15.364Zm-3.8-4.647H3.8a2.541,2.541,0,0,1,.736-1.8A2.446,2.446,0,0,1,6.333,0,2.415,2.415,0,0,1,8.123.792a2.518,2.518,0,0,1,.744,1.837Z" transform="translate(255.008 325.008)" fill="#0c296e"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="class" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<g id="代码" transform="translate(225.621 200.859)">
<path id="路径_17140" data-name="路径 17140" d="M32,129.276H42.313a.851.851,0,0,1,.851.851v5.954a.851.851,0,0,1-.851.851H32.851a.851.851,0,0,1-.851-.851ZM32.851,128h2.99a.852.852,0,0,1,.706.376l.606.9H32v-.426A.851.851,0,0,1,32.851,128Z" transform="translate(0 0)" fill="currentColor"/>
<path id="路径_17141" data-name="路径 17141" d="M260.641,436.451a.68.68,0,0,1,.057.9l-.057.065-1.032,1.021a.425.425,0,0,1-.643-.551l.045-.052.91-.9-.91-.9a.426.426,0,0,1-.048-.548l.045-.053a.426.426,0,0,1,.549-.048l.053.045,1.032,1.02Z" transform="translate(-224.247 -303.733)" fill="#fff"/>
<path id="路径_17142" data-name="路径 17142" d="M512,641.6m.425,0h2.585a.376.376,0,0,1,.425.425h0a.376.376,0,0,1-.425.425h-2.585a.376.376,0,0,1-.425-.425h0A.376.376,0,0,1,512.425,641.6Z" transform="translate(-474.418 -507.627)" fill="#fff"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g id="代码行数" transform="translate(-253.245 -323.244)">
<rect id="矩形_639" data-name="矩形 639" width="16" height="16" transform="translate(253.245 323.244)" fill="#a94444" opacity="0"/>
<path id="代码" d="M91.7,96H82.81a1.232,1.232,0,0,0-1.21,1.23v.912H92.886V97.23A1.19,1.19,0,0,0,91.7,96ZM81.62,105.758a1.219,1.219,0,0,0,1.21,1.23H91.7a1.232,1.232,0,0,0,1.21-1.23V99.114H81.62Zm7.835-3.709a.383.383,0,0,1,0-.555.361.361,0,0,1,.536,0l1.388,1.448a.383.383,0,0,1,0,.555l-1.388,1.448a.361.361,0,0,1-.536,0,.383.383,0,0,1,0-.555l1.131-1.17Zm-2.975,2.9.972-3.729a.377.377,0,0,1,.476-.278.431.431,0,0,1,.278.5l-.972,3.729a.377.377,0,0,1-.476.278A.414.414,0,0,1,86.479,104.945Zm-3.015-2.023,1.388-1.448a.361.361,0,0,1,.536,0,.383.383,0,0,1,0,.555l-1.131,1.17,1.131,1.17a.383.383,0,0,1,0,.555.361.361,0,0,1-.536,0l-1.388-1.448A.383.383,0,0,1,83.464,102.922Z" transform="translate(173.992 229.75)" fill="#0c296e"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g id="异味" transform="translate(-253.245 -323.244)">
<rect id="矩形_639" data-name="矩形 639" width="16" height="16" transform="translate(253.245 323.244)" fill="#a94444" opacity="0"/>
<path id="异味-2" data-name="异味" d="M25.936,13.169a6.275,6.275,0,1,0-6.275-6.275,6.275,6.275,0,0,0,6.275,6.275ZM26.6,5.192l1.309-2.259a.259.259,0,0,1,.224-.127.269.269,0,0,1,.135,0,4.715,4.715,0,0,1,2.339,4.062.25.25,0,0,1-.261.261H27.728a.25.25,0,0,1-.259-.261,1.6,1.6,0,0,0-.788-1.354.259.259,0,0,1-.072-.322H26.6Zm.1,3.083a.206.206,0,0,1,.133-.036.262.262,0,0,1,.225.126l1.309,2.259a.161.161,0,0,1,0,.116.269.269,0,0,1-.136.242,4.751,4.751,0,0,1-4.687,0,.27.27,0,0,1-.09-.357L24.8,8.365a.259.259,0,0,1,.242-.126.206.206,0,0,1,.126,0,1.605,1.605,0,0,0,1.542.036H26.7ZM23.543,2.851a.269.269,0,0,1,.367.089L25.22,5.2a.259.259,0,0,1-.09.358,1.6,1.6,0,0,0-.781,1.353.25.25,0,0,1-.259.261H21.455a.287.287,0,0,1-.287-.261,4.715,4.715,0,0,1,2.376-4.06Z" transform="translate(235.309 324.35)" fill="#0c296e"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g id="覆盖率" transform="translate(-253.245 -323.244)">
<rect id="矩形_639" data-name="矩形 639" width="16" height="16" transform="translate(253.245 323.244)" fill="#a94444" opacity="0"/>
<path id="覆盖率-01" d="M96.593,88.528a2.577,2.577,0,0,0,1.641-2.421,2.544,2.544,0,0,0-2.507-2.579,2.64,2.64,0,0,0-2.508,2.579,2.578,2.578,0,0,0,1.64,2.421l-2.017,2.906A6.125,6.125,0,0,1,89.8,86.107a5.929,5.929,0,1,1,11.853,0,6.126,6.126,0,0,1-3.042,5.327Zm-.866.733L98.006,92.2H93.447Zm0-1.747a1.331,1.331,0,0,1-1.185-.416,2.021,2.021,0,0,1,0-1.695,1.361,1.361,0,0,1,1.185-.7,1.408,1.408,0,0,1,0,2.814Z" transform="translate(165.519 245.137)" fill="#0c296e"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="files" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<g id="组_14720" data-name="组 14720" transform="translate(2.877 2.825)">
<path id="open" d="M72.617,32.018c-.333-.026-.4-.044-.1-.341a4.823,4.823,0,0,1,.473-.44c.008-.02-.207-.545-1.084-.928a3.545,3.545,0,0,0-2.5.169c-.163.139-.656.218-.208-.237a3.1,3.1,0,0,1,3-.827,2.934,2.934,0,0,1,1.626,1.025c-.005.007-.007.011,0,.011a.013.013,0,0,0,0-.011c.023-.032.185-.151.463-.407.238-.242.307-.182.335.112.034.554.015,2.136.015,2.136S73.161,32.1,72.617,32.018Z" transform="translate(190.469 295.858)" fill="currentColor"/>
<path id="open-2" data-name="open" d="M65.328,31.494h4.437s.723.023.737.658-.737.644-.737.644H67.379a.746.746,0,0,0-.791.507c-.088.23-.837,2.322-1.129,3.239-.082.259.114.329.275.333s.319,0,.458,0a.419.419,0,0,0,.432-.333c.187-.567.794-2.078.873-2.277.2-.514.47-.462.765-.462h5.6c.7,0,.957.376.755.957l-.848,4.168a1.791,1.791,0,0,1-1.564,1.261H65.352A1.369,1.369,0,0,1,64,38.872V32.783A1.345,1.345,0,0,1,65.328,31.494Z" transform="translate(191.114 295.577)" fill="currentColor"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="files" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<path id="方法_1_" data-name="方法 (1)" d="M54.474,59.259a.393.393,0,1,1,0,.787H52.1a.393.393,0,1,1,0-.786h2.375Zm-1.188-10.1A4.451,4.451,0,0,1,55.4,57.542v.407a.525.525,0,0,1-.528.525H51.7a.525.525,0,0,1-.528-.525v-.406a4.451,4.451,0,0,1,2.111-8.388Zm0,1.179a3.32,3.32,0,0,0-3.293,2.684.4.4,0,0,0,.777.153l0-.015a2.53,2.53,0,0,1,2.514-2.035.393.393,0,1,0,0-.787Zm6.29,1.75a.417.417,0,0,1,.419.41v.1a.413.413,0,0,1-.418.409H58.85a.415.415,0,0,1-.419-.408v-.1a.414.414,0,0,1,.419-.411h.727Zm-11.891,0a.417.417,0,0,1,.419.41v.1a.413.413,0,0,1-.419.409H46.96a.415.415,0,0,1-.419-.408v-.1a.414.414,0,0,1,.418-.411Zm1.3-3.7.587.418a.407.407,0,0,1,.094.573l-.058.078a.423.423,0,0,1-.583.089l-.588-.419a.407.407,0,0,1-.093-.573l.058-.077A.423.423,0,0,1,48.989,48.385Zm8.847-.074a.42.42,0,0,1,.273.164l.057.077a.407.407,0,0,1-.093.573l-.587.418a.423.423,0,0,1-.583-.089l-.058-.078a.407.407,0,0,1,.093-.572l.588-.42A.423.423,0,0,1,57.837,48.311Zm-4.5-1.719a.413.413,0,0,1,.417.411v.712a.416.416,0,0,1-.417.411h-.1a.42.42,0,0,1-.295-.12.4.4,0,0,1-.121-.291V47a.414.414,0,0,1,.416-.411Z" transform="translate(209.935 279.884)" fill="#403e63" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="严重" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<g id="严重告警" transform="translate(154.929 224.929)">
<path id="路径_15477" data-name="路径 15477" d="M102.4,108.274a5.874,5.874,0,1,0,5.874-5.874A5.874,5.874,0,0,0,102.4,108.274Z" fill="#df0000"/>
<path id="路径_15478" data-name="路径 15478" d="M302.408,235.566l-3.635,4.6a.206.206,0,0,0,0,.264.22.22,0,0,0,.117,0H302l-.734,2.937a.184.184,0,0,0,.323.162l3.363-4.3a.176.176,0,0,0,.176-.147.18.18,0,0,0-.323-.147H301.7l1.028-3.18a.184.184,0,0,0,0-.2.191.191,0,0,0-.184-.1.169.169,0,0,0-.154.125Z" transform="translate(-193.51 -131.154)" fill="#fff"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="标签1" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<path id="标签" d="M198.069,167.334v9.679a.622.622,0,0,1-.841.582l-3.918-1.47a.623.623,0,0,0-.437,0l-3.918,1.47a.622.622,0,0,1-.841-.582v-9.679ZM195.58,169.2H190.6v1.245h4.977Z" transform="translate(70.111 160.719)" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 542 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="标签2" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<path id="标签栏_精选" d="M84.875,138.667l.065,0a.474.474,0,0,1,.351.24l2.3,4.165.029.063a.474.474,0,0,1-.111.5l-6.01,5.959-.053.045a.474.474,0,0,1-.615-.045l-6.01-5.959-.046-.053a.474.474,0,0,1-.036-.513l2.3-4.164.035-.054a.474.474,0,0,1,.381-.192h7.428Zm-4.592,4.719a.633.633,0,0,0-.835.947l1.265,1.265.059.053a.633.633,0,0,0,.835-.053l1.265-1.265.053-.059a.633.633,0,0,0-.053-.835l-.059-.053a.633.633,0,0,0-.835.053l-.818.817-.818-.817Z" transform="translate(182.042 188.536)" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 809 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="标签3" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<path id="面性标签图标" d="M80.092,169.712a.509.509,0,1,1-.9.466,4.382,4.382,0,0,0-2.938-2.594l.414,1.841a1.474,1.474,0,0,1-.268,1.219l-3.777,4.919a1.462,1.462,0,0,1-2.053.266l-3.7-2.862a1.472,1.472,0,0,1-.266-2.059l3.726-4.853a1.593,1.593,0,0,1,1.241-.623l3-.04a1.465,1.465,0,0,1,1.447,1.147C77.277,166.665,78.886,167.356,80.092,169.712Zm-5.223-1.562a1.063,1.063,0,0,0,.222-.6,2.965,2.965,0,0,0-.7.178.51.51,0,0,1-.4-.939,3.7,3.7,0,0,1,.637-.18,1.075,1.075,0,1,0,.239,1.543Zm-3.821,2.332-.79-.595a.424.424,0,1,0-.51.677l.79.595-.595.79a.424.424,0,0,0,.677.51l.595-.79.79.595a.425.425,0,0,0,.511-.679l-.79-.595.595-.79a.424.424,0,1,0-.677-.51Zm0,0" transform="translate(189.907 162.809)" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1018 B

View File

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="标签4" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<g id="标记" transform="translate(127.784 237.169)">
<path id="路径_17132" data-name="路径 17132" d="M135.186,90.2a3.858,3.858,0,0,0-3.127.519A.989.989,0,0,0,131,90a1.023,1.023,0,0,0-.86.9,1.074,1.074,0,0,0,.412.995v9.556a.607.607,0,0,0,.552.628.586.586,0,0,0,.586-.6v-3.27c3.217-2.12,5.173-.638,6.541-.3a3.418,3.418,0,0,0,2.429-.368.316.316,0,0,0,.156-.278V91.33a.3.3,0,0,0-.435-.28C138.767,91.9,137.159,90.836,135.186,90.2Zm2.646,3.388a6.533,6.533,0,0,1-1.884-.792,2.509,2.509,0,0,0-2.589-.016.54.54,0,0,1-.767-.187.611.611,0,0,1,.17-.812,3.574,3.574,0,0,1,3.73-.017,5.457,5.457,0,0,0,1.572.667.582.582,0,0,1,.438.577A.567.567,0,0,1,137.831,93.585Z" transform="translate(-0.05 0)" fill="currentColor"/>
<path id="路径_17133" data-name="路径 17133" d="M140.326,91.1c-1.609.847-3.218-.214-5.191-.853a3.858,3.858,0,0,0-3.127.519.989.989,0,0,0-1.062-.72,1.023,1.023,0,0,0-.86.9,1.073,1.073,0,0,0,.412.995v7.8a8.409,8.409,0,0,0,1.138.134V98.256a5.68,5.68,0,0,1,6-.472,8.433,8.433,0,0,0,2.872-6.348c0-.123,0-.245-.009-.366A.281.281,0,0,0,140.326,91.1Zm-2.545,2.534a6.533,6.533,0,0,1-1.884-.792,2.509,2.509,0,0,0-2.589-.016.54.54,0,0,1-.767-.187.611.611,0,0,1,.17-.812,3.574,3.574,0,0,1,3.73-.017,5.457,5.457,0,0,0,1.572.667.582.582,0,0,1,.438.577A.567.567,0,0,1,137.781,93.635Z" transform="translate(0 -0.05)" fill="currentColor"/>
<path id="路径_17134" data-name="路径 17134" d="M132.007,90.716a.989.989,0,0,0-1.062-.72,1.023,1.023,0,0,0-.86.9,1.073,1.073,0,0,0,.412.995V96.8a8.445,8.445,0,0,0,6.19-3.6,8.237,8.237,0,0,1-.79-.415,2.51,2.51,0,0,0-2.589-.015.54.54,0,0,1-.767-.187.611.611,0,0,1,.17-.812,3.574,3.574,0,0,1,3.73-.017,6.867,6.867,0,0,0,.852.432,8.412,8.412,0,0,0,.444-1.05c-.811-.244-1.666-.643-2.6-.945A3.86,3.86,0,0,0,132.007,90.716Z" transform="translate(0 0)" fill="currentColor"/>
<path id="路径_17135" data-name="路径 17135" d="M132.458,92.323a.6.6,0,0,1,.254-.545,3.366,3.366,0,0,1,.859-.384,8.453,8.453,0,0,0,1.08-1.31,4.111,4.111,0,0,0-2.644.631.989.989,0,0,0-1.062-.72,1.023,1.023,0,0,0-.86.9,1.073,1.073,0,0,0,.412.995v1.449A8.435,8.435,0,0,0,132.458,92.323Z" transform="translate(0 0)" fill="currentColor"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="标签5" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<g id="标记01-F" transform="translate(171.737 273.297)">
<path id="路径_17136" data-name="路径 17136" d="M309.312,56.213A2.923,2.923,0,1,1,313,59.035a.194.194,0,0,0-.144.185v4.223a.616.616,0,1,1-1.231,0V59.22a.194.194,0,0,0-.145-.185A2.924,2.924,0,0,1,309.312,56.213Z" transform="translate(-220.769)" fill="currentColor"/>
<path id="路径_17137" data-name="路径 17137" d="M87.436,629.8a.616.616,0,1,0-.556-1.1,2.414,2.414,0,0,0-1.568,1.974A1.851,1.851,0,0,0,85.935,632a4.372,4.372,0,0,0,1.382.848,12.474,12.474,0,0,0,8.3,0A4.364,4.364,0,0,0,97,632a1.851,1.851,0,0,0,.623-1.325,2.414,2.414,0,0,0-1.569-1.974.616.616,0,1,0-.555,1.1c.729.369.893.709.893.876a.647.647,0,0,1-.247.44,3.188,3.188,0,0,1-.992.594,11.276,11.276,0,0,1-7.366,0,3.188,3.188,0,0,1-.992-.594.647.647,0,0,1-.247-.44c0-.166.163-.508.893-.877Z" transform="translate(0 -567.04)" fill="currentColor"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="次要" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<path id="次要的" d="M6.444,0A6.444,6.444,0,1,1,0,6.444,6.444,6.444,0,0,1,6.444,0Zm0,2.216a.629.629,0,0,0-.629.629V8.581l-.886-.886a.629.629,0,0,0-.883-.007l-.007.007a.629.629,0,0,0-.007.883l.007.007L6,10.543a.634.634,0,0,0,.894,0L8.849,8.585a.629.629,0,0,0-.89-.89l-.885.885V2.845a.629.629,0,0,0-.629-.629Z" transform="translate(256.759 326.759)" fill="#8ec043"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 669 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<g id="主要" transform="translate(-254.203 -324.203)">
<rect id="矩形_639" data-name="矩形 639" width="18" height="18" transform="translate(254.203 324.203)" fill="#a94444" opacity="0"/>
<path id="主要-2" data-name="主要" d="M6.759,0A6.759,6.759,0,1,1,0,6.759,6.759,6.759,0,0,1,6.759,0Zm0,3.379L2.535,7.6,3.742,8.811,6.758,5.794,9.776,8.81,10.983,7.6Z" transform="translate(256.444 326.444)" fill="#f68f18"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g id="重复率" transform="translate(-253.245 -323.244)">
<rect id="矩形_639" data-name="矩形 639" width="16" height="16" transform="translate(253.245 323.244)" fill="#a94444" opacity="0"/>
<path id="功能重复率" d="M36.511,3.319l-.382.879q-.338.858.784.67l3.175-.753q.72-.189.4-.817L38.755.242q-.529-.5-.889.1l-.382.878q-5.864-2.365-7.262,3.81C30.2,5.015,31.747,1.477,36.511,3.319ZM34.542,8.008l.36-.879q.317-.858-.8-.648l-3.155.837q-.72.209-.381.837l1.82,3.015q.551.5.89-.126l.36-.879q5.927,2.219,7.155-3.977c0-.021-1.461,3.559-6.245,1.82Z" transform="translate(226.009 325.368)" fill="#0c296e"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 717 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18.876" height="7.81" viewBox="0 0 18.876 7.81">
<g id="长箭头-copy" transform="translate(-14.44 -309.495)">
<path id="路径_17130" data-name="路径 17130" d="M14.44,490.254H26.624v1.128H14.44Z" transform="translate(0 -177.383)" fill="#5d6790"/>
<path id="路径_17131" data-name="路径 17131" d="M673.811,309.5l6.561,3.853-6.561,3.957Z" transform="translate(-647.057)" fill="#5d6790"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<g id="漏洞" transform="translate(-695 -488)">
<rect id="矩形_639" data-name="矩形 639" width="16" height="16" transform="translate(695 488)" fill="#a94444" opacity="0"/>
<path id="漏洞-全量漏洞" d="M82.073,4.548H75.548V2.818a1.944,1.944,0,0,1,2.076-1.681,2.125,2.125,0,0,1,1.928,1.088h1.335A3.2,3.2,0,0,0,77.625,0a3.094,3.094,0,0,0-3.312,2.966V4.548h-.989a.771.771,0,0,0-.791.741v5.784a.77.77,0,0,0,.791.741h8.6a.771.771,0,0,0,.791-.741V5.339a.793.793,0,0,0-.643-.791Zm-5.487,6.476L77.279,8.5H75.944l1.335-2.521h1.335l-.692,1.681h1.335Z" transform="translate(625.375 490.093)" fill="#0c296e"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 715 B

View File

@ -2,6 +2,15 @@
width: 1200px;
margin:20px auto;
}
.panels-quality {
background:#f3f5f9;
background-image: url("./img/quality/page-bg.png");
background-repeat: no-repeat;
background-size: 100%;
box-sizing: content-box;
width: 100%;
margin: 0;
}
.btnhover{
background-color: #466aff;
color: #fff!important;

View File

@ -0,0 +1,65 @@
import React, { useEffect, useState } from 'react';
import { Table, Spin } from 'antd';
import './index.scss'
import { getFuncAnalize } from '../../api';
function CallTable(props){
const [ loading, setLoading ] = useState(false);
const [ listData, setListData ] = useState([])
const { owner , projectsId } = props.match.params;
useEffect(() => {
getlist()
},[])
const getlist = () => {
setLoading(true)
getFuncAnalize(owner, projectsId).then(res => {
if (res.status === 0) {
setListData(res.data)
}
setLoading(false)
})
}
const openCode = (text) => {
window.open(`${props.match.url}/tree/${ props.projectDetail.default_branch }/${text.split(':')[0]}`)
}
const columns = [
{
title: "方法名",
dataIndex: "function",
},
{
title: "方法位置",
dataIndex: "definition",
render: (text, item) => (
text !== 'unknown' ? <span onClick={() => openCode(text)} className="color-blue pointer">
{text}
</span> : text
),
},
{
title: "调用方法数",
dataIndex: "call_count",
},
{
title: "被调用次数",
dataIndex: "called_count",
},
];
return <Spin spinning={loading}>
<div className="call-table">
<Table
columns={columns}
dataSource={listData}
>
</Table>
</div>
</Spin>
}
export default CallTable;

View File

@ -41,4 +41,15 @@
margin-left: 70px;
}
}
}
.call-table {
background: #f9fafd;
background-image: url("../../img/quality/right-list-bg.png");
background-repeat: no-repeat;
background-size: 100%;
border: 1px solid;
border-color: #eff0f1;
border-radius: 7px;
padding: 20px;
margin-bottom: 20px;
}

View File

@ -4,8 +4,8 @@ import './detail.scss';
import ProblemDetail from "./component/ProblemDetail";
import { issueType, issueTypeIcon } from "../constants/quality"
import folderImg from '../img/quality/folder.png'
import backImg from '../img/quality/back.png'
import { Divider } from 'antd';
import { Button } from 'antd';
import arrow from '../img/quality/triangular-arrow.png'
function issueDetail(props){
const { owner , projectsId } = props.match.params;
@ -63,7 +63,7 @@ function issueDetail(props){
let newIssues = data.issues
let issuesHandled = JSON.parse(JSON.stringify(issues))
setTotal(data.total)
newIssues.forEach(e => {
newIssues && newIssues.forEach(e => {
if (issuesHandled[e.component]) {
issuesHandled[e.component].push(e)
} else {
@ -166,64 +166,80 @@ function issueDetail(props){
})
}
return <div className="bug-details">
<ProblemDetail visible={detailVisible} issue={selectedIssue} issueDetail={issueInfo} onClose={() => setDetailVisible(false)}></ProblemDetail>
<div className="left-list">
<img className="back-icon pointer" src={ backImg } alt="" onClick={() => history.goBack()}/>
<Divider dashed />
{
issues && Object.entries(issues).map(([key, item]) => <div key={ key }>
<div className="doc-title" title={ key.split(`:`)[1] }><img src={ folderImg } alt="" /><span>{ key.split(`:`)[1] }</span></div>
{
item.map(e => (
<div key={ e.key } className={`issue ${ e.key === selectedIssue?.key ? 'selected-issue' : '' }`} onClick={() => setSelectedIssue(e)}>
<p>{ e.message }</p>
<p className="issue-typeline"><img src={issueTypeIcon[e.type]} alt="" /><span className="issue-type">{ issueType[e.type] }</span>{ e.flows.length > 0 && <span className="issue-duplication">+{e.flows.length} </span> }</p>
{
e.key === selectedIssue?.key && e.flows && e.flows.length > 0 && e.flows.map((flow, index) => {
return <div className={`repeat-item ${ flow.locations[0].textRange.startLine === selectedFlow ? 'selected-repeat' : '' }`} key={index} onClick={ e => selectAndJumpRepeat(flow.locations[0].textRange.startLine) }>
<span>{ index }</span>
<span>{ flow.locations[0].msg }</span>
</div>
})
}
</div>
))
}
</div>
)
}
{ pageNum * pageSize < total && <div className="load-more" onClick={() => setPageNum(pageNum + 1)}>查看更多</div> }
</div>
return <div className="page-quality">
{
snippetsDetail && <div className="snippets" ref={ codeRef }>
<div className="head"><img src={ folderImg } alt="" /><span>{ snippetsDetail.component.path }</span></div>
<div className="content">
<tbody>
{snippetsDetail.sources.map((item, index) => (
<><tr key={index} id={ `line-${ item.line }` }>
<td className="td-index">{item.line}</td>
<td className="td-code">
<div>
<pre dangerouslySetInnerHTML={{ __html: item.code }} className={`code ${ selectedFlow === item.line ? 'selected-flow' : '' }`} onClick={e => selectRepeat(e, item.line)}/>
{issues[selectedIssue.component] && issues[selectedIssue.component].map(e => {
if (e.line === item.line) {
return <div className={`issue-info ${selectedIssue.key === item.issue?.key ? 'selected-issue' : ''}`} onClick={() => setSelectedIssue(e)}>
{e.message}
<span onClick={() => detail()}>问题分析</span>
</div>;
}
})}
</div>
</td>
</tr>
{ snippetsDetail.sources[index + 1] && (snippetsDetail.sources[index + 1].line - item.line > 1) && <tr><td colSpan={2} className="td-ellipsis">......</td></tr>}
</>
))}
</tbody>
</div>
<div className="quality-title">
<span>代码质量静态扫描</span>
<span>护航研发效能</span>
</div>
}
<div className="container bug-details">
<div className="details-wrapper">
<ProblemDetail visible={detailVisible} issue={selectedIssue} issueDetail={issueInfo} onClose={() => setDetailVisible(false)}></ProblemDetail>
<div className="details-header">
<p>代码分析结果,全方位精准定位问题</p>
<p>深度剖析代码细节精准定位潜在问题助力团队提升开发效率打造高质量软件为项目稳定运行保驾护航开启高效开发新时代</p>
<img className="triangular-arrow" src={ arrow } alt="" />
<Button type="primary" className="back-btn" onClick={() => history.goBack()} >返回</Button>
</div>
<div className="details-body">
<div className="left-list">
{
issues && Object.entries(issues).map(([key, item]) => <div key={ key }>
<div className="doc-title" title={ key.split(`:`)[1] }><img src={ folderImg } alt="" /><span>{ key.split(`:`)[1] }</span></div>
{
item.map(e => (
<div key={ e.key } className={`issue ${ e.key === selectedIssue?.key ? 'selected-issue' : '' }`} onClick={() => setSelectedIssue(e)}>
<p>{ e.message }</p>
<p className="issue-typeline"><img src={issueTypeIcon[e.type]} alt="" /><span className="issue-type">{ issueType[e.type] }</span>{ e.flows.length > 0 && <span className="issue-duplication">+{e.flows.length} </span> }</p>
{
e.key === selectedIssue?.key && e.flows && e.flows.length > 0 && e.flows.map((flow, index) => {
return <div className={`repeat-item ${ flow.locations[0].textRange.startLine === selectedFlow ? 'selected-repeat' : '' }`} key={index} onClick={ e => selectAndJumpRepeat(flow.locations[0].textRange.startLine) }>
<span>{ index }</span>
<span>{ flow.locations[0].msg }</span>
</div>
})
}
</div>
))
}
</div>
)
}
{ pageNum * pageSize < total && <div className="load-more" onClick={() => setPageNum(pageNum + 1)}>查看更多</div> }
</div>
{
snippetsDetail && <div className="snippets container" ref={ codeRef }>
<div className="head"><img src={ folderImg } alt="" /><span>{ snippetsDetail.component.path }</span></div>
<div className="content">
<tbody>
{snippetsDetail.sources.map((item, index) => (
<><tr key={index} id={ `line-${ item.line }` }>
<td className="td-index">{item.line}</td>
<td className="td-code">
<div>
<pre dangerouslySetInnerHTML={{ __html: item.code }} className={`code ${ selectedFlow === item.line ? 'selected-flow' : '' }`} onClick={e => selectRepeat(e, item.line)}/>
{issues[selectedIssue.component] && issues[selectedIssue.component].map(e => {
if (e.line === item.line) {
return <div className={`issue-info ${selectedIssue.key === item.issue?.key ? 'selected-issue' : ''}`} onClick={() => setSelectedIssue(e)}>
{e.message}
<span onClick={() => detail()}>问题分析</span>
</div>;
}
})}
</div>
</td>
</tr>
{ snippetsDetail.sources[index + 1] && (snippetsDetail.sources[index + 1].line - item.line > 1) && <tr><td colSpan={2} className="td-ellipsis">......</td></tr>}
</>
))}
</tbody>
</div>
</div>
}
</div>
</div>
</div>
</div>
}
export default issueDetail;

View File

@ -1,13 +1,70 @@
.page-quality {
width: 1200px;
margin: 0 auto;
padding: 20px 0;
.container {
border-radius: 10px;
box-shadow: 0px 0px 6px rgba(7, 70, 165, 0.08);
background: #FFFFFF;
}
}
.bug-details {
display: flex;
max-height: 80vh;
max-height: 90vh;
width: 1200px;
background: #ffffff;
padding: 25px 30px;
.details-wrapper {
background: #f6f8ff;
background-image: url("../img/quality/detail-bg.png");
background-repeat: no-repeat;
background-size: 100%;
border: 1px solid;
border-color: #eff0f1;
border-radius: 7px;
width: 100%;
padding: 30px 25px;
.details-header {
position: relative;
p {
color: #0c296e;
font-size: 14px;
max-width: 850px;
line-height:normal;
}
p:first-child {
font-weight: 500;
font-size: 16px;
padding-bottom: 10px;
}
.triangular-arrow {
position: absolute;
left: 260px;
top: -5px;
width: 36px;
height: 44px;
}
.back-btn {
width: 79px;
height: 32px;
background: #466aff;
border-radius: 6px;
position: absolute;
right: 0;
top: 0;
}
}
.details-body {
display: flex;
margin-top: 30px;
max-height: calc(90vh - 175px );
}
}
.left-list {
overflow-y: auto;
width: 350px;
flex-shrink: 0;
.back-icon {
width: 24px;
}
max-height: 100%;
.ant-divider {
padding-right: 10px;
width: 330px;
@ -46,18 +103,20 @@
}
.issue {
padding: 15px 20px;
background: #FAFCFF;
border-radius: 3px 3px 3px 3px;
border: 1px solid rgba(42,97,255,0.23);
margin-bottom: 15px;
margin-right: 10px;
word-break: break-word;
font-family: PingFang SC, PingFang SC;
font-size: 14px;
color: #111010;
cursor: pointer;
background: #ffffff;
border: 1px solid;
border-color: #e5ecf8;
border-radius: 6px;
&:hover {
border: 1px solid #466AFF;
background-image: url('../img/quality/issue-bg.png');
background-repeat: no-repeat;
background-size: 100% 100%;
}
.issue-typeline {
display: flex;
@ -74,10 +133,12 @@
.issue-duplication {
margin-left: 5px;
padding: 0 8px;
background: #57575B;
border-radius: 4px 4px 4px 4px;
font-weight: 400;
color: #FFFFFF;
color: #042182;
font-size: 14px;
height: 20px;
background:#e6f0ff;
border-radius: 4px;
line-height: 20px;
}
.repeat-item {
span {
@ -106,15 +167,20 @@
background: #D4333F;
}
span:nth-of-type(2) {
background: #475760;
background: #274faf;
color: #FFFFFF;
}
}
}
.selected-issue {
background: #FFFFFF;
border: 1px solid #2A61FF;
background-image: url('../img/quality/issue-bg.png');
background-repeat: no-repeat;
background-size: 100% 100%;
border: 2px solid #2A61FF;
.issue-duplication {
background: #D4333F;
color: #FFFFFF;
}
}
}
@ -133,6 +199,7 @@
line-height: 42px;
display: flex;
align-items: center;
border-radius:6px 6px 0px 0px;
img {
width: 18px;
margin-right: 10px;
@ -147,6 +214,8 @@
.content {
display: flex;
border: 1px solid #ECEEEF;
max-height: calc(90vh - 217px);
overflow-y: auto;
tbody {
width: 100%;
}
@ -221,7 +290,24 @@
}
}
}
.quality-title {
font-family: 'alibaba';
font-weight: 500;
color: #191919;
font-size: 36px;
text-align: center;
margin-bottom: 38px;
line-height: 1;
span:nth-child(1) {
background-image: linear-gradient(92.79deg,#2569f2 5.43%,#40bcfd 96.17%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-right: 18px;
}
}
.issue-total {
font-family: 'alibabaReg';
.nodata {
height: 100%;
display: flex;
@ -237,20 +323,12 @@
color: #788498;
}
}
.container {
background: #FFFFFF;
box-shadow: 0px 0px 6px 1px rgba(7,70,165,0.08);
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;
background: #ffffff;
border-radius: 0px 10px 10px 0px;
margin-top: 30px;
padding: 27px 23px;
.show-more-list {
max-height: 2300px;
}
@ -263,23 +341,37 @@
transition: max-height 0.4s;
overflow: hidden;
.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;
height: 23px;
cursor: pointer;
background: #ffffff;
border: 1px solid;
border-color: #d4333f;
border-radius: 3px;
color: #d4333f;
font-size: 13px;
line-height: 23px;
}
.count {
display: flex;
align-items: center;
justify-content: space-between;
.count-title {
font-family: 'alibaba';
color: #191919;
font-size: 16px;
margin-right: 33px;
}
}
.filters {
.filter-title {
font-weight: bold;
font-size: 17px;
color: #000000;
padding: 20px;
color: #9a99b9;
font-size: 13px;
padding: 17px 0 17px 7px;
line-height: 28px;
display: flex;
align-items: center;
justify-content: space-between;
.clear-button {
float: right;
margin: 0;
@ -293,20 +385,32 @@
align-items: center;
height: 50px;
cursor: pointer;
color:#403e63;
font-size: 15px;
&:hover {
background: #466bff0c;
background:#f6f8ff;
color:#466aff;
border-radius:5px;
}
.item-type {
display: flex;
align-items: center;
img {
width: 16px;
.type-icon {
margin-right: 5px;
transform: scale(1.125);
div {
height: 18px;
display: flex;
align-items: center;
}
}
}
}
.selected-item {
background: #466bff17;
background:#f6f8ff;
color:#466aff;
border-radius:5px;
filter: drop-shadow(#466aff 1000px 0)
}
.ant-divider {
margin: 20px;
@ -326,20 +430,48 @@
.right-content {
margin-left: 17px;
flex-grow: 1;
padding: 20px;
padding-top: 10px;
.content-warap {
background: #f9fafd;
border: 1px solid;
border-color: #eff0f1;
border-radius: 7px;
padding: 0 20px 20px 20px;
margin-bottom: 20px;
}
.has-issues {
background-image: url("../img/quality/right-list-bg.png");
background-repeat: no-repeat;
background-size: 100%;
}
.doc-title {
font-weight: bold;
font-size: 16px;
color: #111010;
margin: 19px 0;
margin: 30px 0 15px 0;
width: 509px;
color: #191919;
img {
width: 18px;
margin-right: 10px;
}
}
.issue {
padding: 15px 20px;
background: #FAFCFF;
border-radius: 3px 3px 3px 3px;
border: 1px solid rgba(42,97,255,0.23);
margin-bottom: 12px;
background: #ffffff;
border: 1px solid;
border-color: rgba(123, 134, 180, 0.11);
border-radius: 6px;
box-shadow: 0px 0px 8px rgba(70, 106, 255, 0.05);
border: 2px solid;
border-color: transparent;
cursor: pointer;
&:hover {
border: 2px solid;
border-color: #466aff;
.issue-message {
color: #466aff;
}
}
.content-info {
font-weight: 400;
font-size: 14px;
@ -356,74 +488,136 @@
}
span:nth-of-type(n + 4) {
margin-left: auto;
img {
color: #5D6790;
}
}
.icon-text {
display: flex;
align-items: center;
img{
width: 16px;
margin-right: 5px;
}
.svg-icon {
margin-right: 5px;
div {
display: flex;
align-items: center;
}
}
}
}
.issue-message {
font-weight: bold;
font-size: 15px;
color: #111010;
color: #000000;
word-break: break-word;
display: flex;
span:nth-of-type(1) {
font-weight: 400;
font-size: 14px;
color: #466AFF;
margin-left: 16px;
margin-left: auto;
cursor: pointer;
}
span:nth-of-type(2) {
float: right;
font-family: 'alibaba';
color: #000000;
&::before {
content: "|";
color: rgba(187,189,197,0.61);
margin-right: 13px;
margin-left: 13px;
}
}
}
}
.ant-pagination {
float: right;
}
}
}
.total-info {
padding: 20px;
.sub-container {
background-color: #f6f8ff;
background-image: url("../img/quality/total-bg.png");
background-repeat: no-repeat;
background-size: 100% 100%;
padding: 21px 25px;
position: relative;
}
p {
font-weight: bold;
font-size: 18px;
color: #333333;
color: #0c296e;
font-size: 14px;
max-width: 565px;
line-height:normal;
}
p:first-child {
font-family: 'alibaba';
font-weight: 500;
font-size: 16px;
padding-bottom: 10px;
}
.triangular-arrow {
position: absolute;
left: 580px;
top: 40px;
width: 48px;
height: 58px;
}
.info-list {
display: flex;
flex-wrap: wrap;
width: 800px;
.total-item {
width: 250px;
height: 50px;
background: #ffffff;
border-radius: 5px;
box-shadow: 0px 0px 6px rgba(33, 73, 212, 0.06);
display: flex;
flex-direction: column;
min-height: 53px;
font-weight: 400;
align-items: center;
justify-content: space-between;
font-size: 15px;
color: #525151;
margin-top: 18px;
> span:nth-child(2) {
font-weight: bold;
font-size: 18px;
color: #111010;
}
.item-more {
margin-left: 5px;
font-weight: 400;
font-size: 12px;
color: #525151;
span {
color: #466AFF;
color: #0c296e;
margin-top: 15px;
margin-right: 15px;
padding: 12px 15px;
.item-text {
display: flex;
align-items: center;
img {
margin-right: 4px;
}
}
}
.total-item:not(:last-child) {
margin-right: 40px;
}
.total-item:nth-child(n + 2) {
margin-left: 64px;
position: relative;
&::after {
position: absolute;
left: -40px;
content: '';
width: 0px;
height: 100%;
border-left: 1px solid rgba(187,189,197,0.61);
.item-value {
font-family: YouSheBiaoTiHei;
color: #182952;
font-size: 20px;
}
.item-more {
font-weight: 400;
font-size: 12px;
margin-right: auto;
}
}
// .total-item:not(:last-child) {
// margin-right: 40px;
// }
// .total-item:nth-child(n + 2) {
// margin-left: 64px;
// position: relative;
// &::after {
// position: absolute;
// left: -40px;
// content: '';
// width: 0px;
// height: 100%;
// border-left: 1px solid rgba(187,189,197,0.61);
// }
// }
}
}
}
@ -456,12 +650,8 @@
border-bottom: 1px solid rgba(90, 117, 193, 0.23);
}
}
.icon-text {
display: flex;
align-items: center;
img {
width: 16px;
margin-right: 5px;
}
.iframe {
border: 1px solid;
border-color: #eff0f1;
border-radius: 7px;
}

View File

@ -1,18 +1,23 @@
import React, { useState, useEffect, Fragment } from "react";
import { getList, getProblemDetail, getProblemData, initSonar, startAlalyze, getActionList } from "../api";
import React, { useState, useEffect, Fragment, useRef } from "react";
import { getList, getProblemDetail, getProblemData, initSonar, startAlalyze, getActionList, getAnalizeUrl } from "../api";
import './detail.scss';
import ProblemDetail from "./component/ProblemDetail";
import { issueType, maintainability, issueTypeIcon, maintainabilityIcon } from "../constants/quality";
import { issueType, maintainability, issueTypeIcon, maintainabilityIcon, labelIcon } from "../constants/quality";
import { Divider, Pagination, Button, message, Spin } 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 code_lines from '../img/quality/code_lines.svg';
import repeat from '../img/quality/repeat.svg';
import cover from '../img/quality/cover.svg';
import nodata from '../../Images/nodata.png'
import { Link } from "react-router-dom";
import arrow from '../img/quality/triangular-arrow.png'
import files from '../img/quality/files.svg'
import classes from '../img/quality/class.svg'
import funcs from '../img/quality/funcs.svg'
import rightArrow from '../img/quality/right-arrow.svg'
import { ReactSVG } from 'react-svg'
import folderImg from '../img/quality/folder.png'
import CallTable from "./component/CallTable";
const actionStatus = {
success: 1,
@ -22,6 +27,18 @@ const actionStatus = {
none: 7
}
const codeAnalyze = {
classes: 1,
files: 2,
func: 3
}
const analizeIcon = {
[codeAnalyze.classes]: classes,
[codeAnalyze.files]: files,
[codeAnalyze.func]: funcs
}
function IssueList(props) {
const [issues, setIssues] = useState({});
const [baseInfo, setBaseInfo] = useState({});
@ -40,18 +57,14 @@ function IssueList(props) {
const [analysisStatus, setAnalysisStatus] = useState(null);
const [tagShowMore, setTagShowMore] = useState(false);
const [tagShowMoreDelay, setTagShowMoreDelay] = useState(false);
const { projectDetail, history } = props;
const [codeSelected, setCodeSelected] = useState(false);
const [url, setUrl] = useState(null);
const { projectDetail, history, changeClass } = 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;
useEffect(() => {
let newContainer = document.getElementsByClassName("newContainer")[0];
// newContainer.style.backgroundColor = '#F7F9FCFF';
return () => {
newContainer.style.backgroundColor = '#FFFFFF';
};
}, []);
const iframeRef = useRef(null);
const timeRef = useRef();
useEffect(() => {
if (projectDetail) {
@ -62,25 +75,80 @@ function IssueList(props) {
return
}
setPageLoading(true)
getActionList(owner, projectsId).then(res => {
setPageLoading(false)
if (res.status !== 0) {
if (res.runs.length === 0) {
setAnalysisStatus(actionStatus.none)
} else {
setAnalysisStatus(res.runs[0].status)
if (res.runs[0].status) {
getData()
getIssueList()
}
}
} else {
message.error(res.message)
}
})
getAction()
}
}, [projectDetail]);
useEffect(() => {
return () => {
clearTimer()
}
},[])
useEffect(() => {
const iframe = iframeRef.current;
if (!iframe || !url) return; // iframe url
const handleLoad = () => {
try {
const iframeDocument = iframe.contentWindow?.document;
if (iframeDocument) {
const contentHeight = Math.max(
iframeDocument.body.scrollHeight, iframeDocument.body.offsetHeight,
iframeDocument.documentElement.clientHeight, iframeDocument.documentElement.scrollHeight, iframeDocument.documentElement.offsetHeight
);
iframe.style.height = contentHeight + 'px';
}
} catch (e) {
iframe.style.height = '770px';
//
console.error("调整同源 iframe 大小出错 (或者它实际是跨域的):", e);
}
};
iframe.addEventListener('load', handleLoad);
// : iframe ()
return () => {
iframe.removeEventListener('load', handleLoad);
};
}, [url]); // URL
const clearTimer = () => {
if (timeRef.current) {
clearTimeout(timeRef.current);
timeRef.current = undefined; //
}
}
const getAction = () => {
getActionList(owner, projectsId).then(res => {
setPageLoading(false)
if (res.status !== 0) {
if (res.runs.length === 0) {
setAnalysisStatus(actionStatus.none)
} else {
if (res.runs[0].status === actionStatus.success) {
changeClass(true)
}
if ([actionStatus.waiting, actionStatus.running].includes(res.runs[0].status)) {
if (timeRef.current) clearTimer();
timeRef.current = setTimeout(() => {
getAction()
}, 3000);
}
setAnalysisStatus(res.runs[0].status)
if (res.runs[0].status) {
getData()
getIssueList()
}
}
} else {
message.error(res.message)
}
})
}
const getData = () => {
getProblemData(owner, projectsId, metricKeys).then(res => {
if (res.status === 0) {
@ -95,31 +163,49 @@ function IssueList(props) {
}
const getIssueList = () => {
const isBLOCKED = selectedSeverity.join(',') === 'BLOCKED' //BLOCKED
let params = {
facets: facets,
types: selectedType.join(','),
impactSeverities: selectedSeverity.join(','),
impactSeverities: selectedSeverity.join(',').replace('BLOCKED', ''),
tags: selectedTag.join(','),
p: pageNum,
ps: pageSize
};
setPageLoading(true)
if (!timeRef.current) {
setPageLoading(true)
}
getList(params, owner, projectsId).then(res => {
setPageLoading(false)
const issues = res.issues.reduce((acc, issue) => {
const issues = res.issues && !isBLOCKED ? res.issues.reduce((acc, issue) => {
if (acc[issue.component]) {
acc[issue.component].push(issue);
} else {
acc[issue.component] = [issue];
}
return acc;
}, {});
}, {}) : [];
const filter = res.facets.reduce((acc, facet) => {
const filter = res.facets ? res.facets.reduce((acc, facet) => {
if (facet.property === 'impactSeverities') {
facet.values.unshift({
count: 0,
val: "BLOCKED"
})
}
if (isBLOCKED && facet.property !== 'impactSeverities') {
if (facet.property === 'tags') facet.values = []
facet.values.map(e => {
e.count = 0
})
}
acc[facet.property] = facet.values;
return acc;
}, {});
}, {}) : [];
if (isBLOCKED) res.total = 0
setListFilter(filter);
setBaseInfo(res);
setIssues(issues);
@ -159,6 +245,8 @@ function IssueList(props) {
};
const toggleSelection = (state, setter, value) => {
setCodeSelected(undefined)
setUrl(undefined)
window.scrollTo(0,0);
const index = state.indexOf(value);
if (index > -1) {
@ -184,17 +272,23 @@ function IssueList(props) {
<span>{label}</span>
{ state.length > 0 && <button className="clear-button" onClick={() => setter([])}>清空</button> }
</div>
{displayedItems && displayedItems.map(e => (
{displayedItems && displayedItems.map((e, index) => (
<div
className={`filter-item${state.includes(e.val) ? ' selected-item' : ''}`}
key={e.val}
onClick={() => toggleSelection(state, setter, e.val)}
>
<span className="item-type"> <img src={ label === "问题类型" ? issueTypeIcon[e.val] : maintainabilityIcon[e.val] || labelIcon } alt="" /> {label === "问题类型" ? issueType[e.val] : maintainability[e.val] || e.val}</span>
<span className="item-type">
{
label === "缺陷预警提示" ? <img className="type-icon" src={ maintainabilityIcon[e.val] } alt="" /> :
<ReactSVG className="type-icon" src={ label === "代码缺陷及风格检查类型" ? issueTypeIcon[e.val] : labelIcon[index % 5] } />
}
{label === "代码缺陷及风格检查类型" ? issueType[e.val] : maintainability[e.val] || e.val}
</span>
<span>{e.count}</span>
</div>
))}
{ showMoreButton && items.length > 10 && <span className="show-more" onClick={() => { setTagShowMore(!tagShowMore) }}> { tagShowMore ? '收起' : '显示更多'} </span> }
{ showMoreButton && items && items.length > 10 && <span className="show-more" onClick={() => { setTagShowMore(!tagShowMore) }}> { tagShowMore ? '收起' : '显示更多'} </span> }
<Divider dashed />
</div>)
};
@ -202,22 +296,32 @@ function IssueList(props) {
//
const renderTotalItem = (label, value, icon) => (
<div className="total-item">
<span className="icon-text"> <img src={ icon} alt="" /> {label}</span>
<span className="ml20">{value}</span>
<span className="item-text"> <img src={ icon } alt="" /> {label}</span>
<span className="ml20 item-value">{value}</span>
</div>
);
//
const renderDetailedItem = (label, value, percent, moreValue, icon) => (
<div className="total-item">
<span className="icon-text"> <img src={ icon} alt="" /> {label}</span>
<span className="ml20">
<span className="item-text"> <img src={ icon} alt="" /> {label}</span>
<span className="item-more">({moreValue}<span>{formatNumber(value)}</span>)</span>
<span className="item-value">
{percent}%
<span className="item-more">{moreValue}<span>{formatNumber(value)}</span></span>
</span>
</div>
);
const renderAnalizeItem = (label, type) => {
return <div
className={`filter-item${codeSelected === type ? ' selected-item' : ''}`}
onClick={() => toIframe(type)}
>
<span className="item-type"> <ReactSVG className="type-icon" src={analizeIcon[type]} />{ label }</span>
<img className="type-icon" src={rightArrow} alt="" />
</div>
}
const toDetail = (e) => {
history.push({
pathname: `/${owner}/${projectsId}/service/quality/detail`,
@ -228,6 +332,19 @@ function IssueList(props) {
})
}
const toIframe = (type) => {
setCodeSelected(type)
clearAllSelection()
getAnalizeUrl(owner, projectsId).then(res => {
if (res.doxygen_url) {
if (type !== codeAnalyze.func) {
setUrl(`${ res.doxygen_url }/${ type === codeAnalyze.files ? 'files' : 'annotated' }.html`)
}
}
})
}
const openAnalysis = () => {
setLoading(true)
initSonar(true, owner, projectsId).then(res => {
@ -236,6 +353,10 @@ function IssueList(props) {
if (res1.status === 0) {
message.success('创建分析成功,请等待分析完成')
setAnalysisStatus(actionStatus.waiting)
if (timeRef.current) clearTimer();
timeRef.current = setTimeout(() => {
getAction()
}, 3000);
}
setLoading(false)
})
@ -252,7 +373,7 @@ function IssueList(props) {
return <Button type="primary" style={{width: '112px', height: '36px'}} onClick={openAnalysis} loading={loading}>开始体验</Button>
case actionStatus.waiting:
case actionStatus.running:
return <Button type="primary" style={{height: '36px'}} disabled>代码正在分析中请稍候</Button>
return <Button type="primary" style={{height: '36px'}} disabled loading>代码正在分析中请稍候</Button>
case actionStatus.failure:
return <div>
<p>分析失败请重试</p>
@ -264,95 +385,139 @@ function IssueList(props) {
}
return (
<Spin spinning={pageLoading}>
<div className="issue-total">
<ProblemDetail
visible={detailVisible}
issue={selectedIssue}
issueDetail={issueInfo}
onClose={() => setDetailVisible(false)}
/>
<Spin spinning={pageLoading} >
<div className="page-quality">
{
analysisStatus !== actionStatus.success && <Fragment>
<div className="headBox font-16 pl15">sonar代码质量分析工具</div>
<div className="nullStoreBox mt25">
<img src={logo} alt="" className="loBox mt50"/>
<p className="font-22 mt10">欢迎使用代码质量分析工具</p>
<div className="introBox font-15">代码质量分析工具通过静态代码分析帮助开发者识别和修复代码库潜在的缺陷代码异味复杂度过高的代码段以及潜在的安全漏洞同时能够评估代码的结构和风格提供关于代码覆盖率重复率等关键指标的深入洞察</div>
<div className="borBox"></div>
{
getStatusButton()
}
</div>
</Fragment>
}
{
analysisStatus === actionStatus.success && <div>
<div className="container total-info">
<p>代码分析结果</p>
<div className="info-list">
{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)}
</div>
</div>
{issues && <div className="count-title">发现{ baseData.maintainability_issues && JSON.parse(baseData.maintainability_issues).total }个问题</div>}
<div className="issue-list">
<div className={`container left-content ${tagShowMore ? 'show-more-list' : 'show-less-list'}`}>
{ (selectedType.length > 0 || selectedSeverity.length > 0 || selectedTag .length > 0) && <button className="clear-button" onClick={clearAllSelection}>清空所有条件</button>}
{renderFilterItems(listFilter.types, selectedType, setSelectedType, "问题类型")}
{renderFilterItems(listFilter.impactSeverities, selectedSeverity, setSelectedSeverity, "严重程度")}
{renderFilterItems(listFilter.tags, selectedTag, setSelectedTag, "标签", true)}
</div>
<div className="container right-content">
{issues && baseInfo.total > 0 && Object.entries(issues).map(([key, item]) => (
<div key={key}>
<div className="doc-title" title={key.split(':')[1]}>
{key.split(':')[1]}
</div>
{item.map(e => (
<div
key={e.key}
className={`issue`}
onClick={() => toDetail(e)}
>
<p className="issue-message">
{e.message}
<span onClick={(ele) => { ele.stopPropagation();detail(e)}}>问题分析</span>
<span>L{e.line}</span>
</p>
<p className="content-info">
<span className="icon-text"> <img src={ issueTypeIcon[e.type]} alt="" /> {issueType[e.type]}</span>
<span className="icon-text"> <img src={ maintainabilityIcon[e.impacts[0].severity] } alt="" /> {maintainability[e.impacts[0].severity]}</span>
<span>{e.debt}工作</span>
<span className="icon-text">
{e.tags && e.tags.length > 0 && <img src={ labelIcon } alt="" />}
{e.tags && e.tags.map((tag, index) => (
<span key={tag}>
{tag}{index < e.tags.length - 1 ? ',' : ''}
</span>
))}
</span>
</p>
</div>
))}
</div>
))}
{ baseInfo.total > 0 && <Pagination showSizeChanger showQuickJumper current={pageNum} total={baseInfo.total} onChange={e => setPageNum(e)} onShowSizeChange={(current, size) => setPageSize(size)} /> }
{!baseInfo.total && <div className="nodata">
<img src={nodata} alt="" />
<p>暂无问题</p>
</div>}
</div>
</div>
analysisStatus === actionStatus.success && <div className="quality-title">
<span>代码质量静态扫描</span>
<span>护航研发效能</span>
</div>
}
<div className="issue-total">
<ProblemDetail
visible={detailVisible}
issue={selectedIssue}
issueDetail={issueInfo}
onClose={() => setDetailVisible(false)}
/>
{
analysisStatus !== actionStatus.success && <Fragment>
<div className="headBox font-16 pl15">sonar代码质量分析工具</div>
<div className="nullStoreBox mt25">
<img src={logo} alt="" className="loBox mt50"/>
<p className="font-22 mt10">欢迎使用代码质量分析工具</p>
<div className="introBox font-15">代码质量分析工具通过静态代码分析帮助开发者识别和修复代码库潜在的缺陷代码异味复杂度过高的代码段以及潜在的安全漏洞同时能够评估代码的结构和风格提供关于代码覆盖率重复率等关键指标的深入洞察</div>
<div className="borBox"></div>
{
getStatusButton()
}
</div>
</Fragment>
}
{
analysisStatus === actionStatus.success && <div>
<div className="container total-info">
<div className="sub-container">
<p>软件质量统计结果,全方位精准定位问题</p>
<p>深度剖析代码细节精准定位潜在问题助力团队提升开发效率打造高质量软件为项目稳定运行保驾护航开启高效开发新时代</p>
<img className="triangular-arrow" src={ arrow } alt="" />
<div className="info-list">
{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)}
</div>
</div>
</div>
<div className="issue-list">
{/* 左边筛选框 */}
<div className={`left-content ${tagShowMore ? 'show-more-list' : 'show-less-list'}`}>
<div className="count">
{issues && <div className="count-title">{ baseData.maintainability_issues && JSON.parse(baseData.maintainability_issues).total }个问题</div>}
{ (selectedType.length > 0 || selectedSeverity.length > 0 || selectedTag .length > 0) && <button className="clear-button" onClick={clearAllSelection}>清空条件</button>}
</div>
{renderFilterItems(listFilter.types, selectedType, setSelectedType, "代码缺陷及风格检查类型")}
{renderFilterItems(listFilter.impactSeverities, selectedSeverity, setSelectedSeverity, "缺陷预警提示")}
{/* 代码执行分析功能固定写死 */}
<div className={`filters`}>
<div className="filter-title">
<span>代码执行分析功能</span>
</div>
{ renderAnalizeItem('classes类溯源', codeAnalyze.classes) }
{ renderAnalizeItem('files文件溯源', codeAnalyze.files) }
{ renderAnalizeItem('functions溯源', codeAnalyze.func) }
<Divider dashed />
</div>
{renderFilterItems(listFilter.tags, selectedTag, setSelectedTag, "标签", true)}
</div>
{/* 右边详情 */}
<div className="right-content">
{
!codeSelected && <div className={`content-warap ${ issues && baseInfo.total > 0 && 'has-issues' }`}>
{issues && baseInfo.total > 0 && Object.entries(issues).map(([key, item]) => (
<div key={key}>
<div className="doc-title" title={key.split(':')[1]}>
<img src={ folderImg } alt="" />
{key.split(':')[1]}
</div>
{item.map(e => (
<div
key={e.key}
className={`issue`}
onClick={() => toDetail(e)}
>
<p className="issue-message">
{e.message}
<span onClick={(ele) => { ele.stopPropagation();detail(e)}}>问题分析</span>
<span>L{e.line}</span>
</p>
<p className="content-info">
<span className="icon-text"> <img src={ issueTypeIcon[e.type]} alt="" /> {issueType[e.type]}</span>
<span className="icon-text"> <img src={ maintainabilityIcon[e.impacts[0].severity] } alt="" /> {maintainability[e.impacts[0].severity]}</span>
<span>{e.debt}工作</span>
<span className="icon-text">
{e.tags && e.tags.length > 0 && <ReactSVG className="svg-icon" src={labelIcon[0]} />}
{e.tags && e.tags.map((tag, index) => (
<span key={tag}>
{tag}{index < e.tags.length - 1 ? ',' : ''}
</span>
))}
</span>
</p>
</div>
))}
</div>
))}
{!baseInfo.total && <div className="nodata">
<img src={nodata} alt="" />
<p>暂无问题</p>
</div>}
</div>
}
{
codeSelected && (
codeSelected !== codeAnalyze.func ? <iframe
className="iframe"
ref={iframeRef}
src={url}
style={{ width: '100%', border: 'none' }} // JS
scrolling="no"
title="代码质量扫描结果"
></iframe> : <CallTable {...props}></CallTable>
)
}
{ !codeSelected && baseInfo.total > 0 && <Pagination showSizeChanger showQuickJumper current={pageNum} total={baseInfo.total} onChange={e => setPageNum(e)} onShowSizeChange={(current, size) => setPageSize(size)} /> }
</div>
</div>
</div>
}
</div>
</div>
</Spin>
);
}
export default IssueList;
export default IssueList;

View File

@ -1,14 +1,14 @@
export function formatNumber(num) {
if (num === undefined || num === null) return
if (num >= 1e9) {
return (num / 1e9).toFixed(1) + 'B'; // 十亿
} else if (num >= 1e6) {
return (num / 1e6).toFixed(1) + 'M'; // 百万
} else if (num >= 1e3) {
return (num / 1e3).toFixed(1) + 'K'; // 千
} else {
return num.toString(); // 小于 1000 的数字原样返回
}
if (num === undefined || num === null) return
if (num >= 1e9) {
return (num / 1e9).toFixed(1) + 'B'; // 十亿
} else if (num >= 1e6) {
return (num / 1e6).toFixed(1) + 'M'; // 百万
} else if (num >= 1e3) {
return (num / 1e3).toFixed(1) + 'K'; // 千
} else {
return num.toString(); // 小于 1000 的数字原样返回
}
}
export function formatNumberWithCommas(num) {
if (num === undefined || num === null) return