merge:合并代码
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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> }
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
{/* 个人建站服务 */}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 5.5 KiB |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
After Width: | Height: | Size: 84 KiB |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
After Width: | Height: | Size: 377 KiB |
|
|
@ -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 |
|
|
@ -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 |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 197 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
|
@ -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 |
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||