sonar+个人设置页面
|
|
@ -32,7 +32,7 @@ export function initAxiosInterceptors(props) {
|
|||
// 判断网络是否连接
|
||||
initOnlineOfflineListener();
|
||||
|
||||
var proxy = "https://pre.gitlink.org.cn";
|
||||
var proxy = "http://172.20.32.202:4000";
|
||||
// var proxy = "https://www.gitlink.org.cn";
|
||||
|
||||
//响应前的设置
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Button, Checkbox, message, Radio, Select, Switch, Spin, Tooltip } from
|
|||
import axios from "axios";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getInstallBot, updateInstallBot } from "../../../../softbot/api";
|
||||
import logoBot from '../../img/logoBot.png';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import './index.scss';
|
||||
import '../exploit/index.scss';
|
||||
|
|
@ -158,7 +159,7 @@ function DispositionDetail(props){
|
|||
<div className="dispositionDetailBox">
|
||||
<div className="oneLine mb30">
|
||||
{botDetail && <div className="oneLine">
|
||||
<img src={getImageUrl(botDetail.logo)} alt="" className="botImg imgBox"/>
|
||||
<img src={(botDetail.logo && (botDetail.logo.indexOf("347246")>-1 || botDetail.logo.indexOf("412603")>-1)) ? logoBot :getImageUrl(botDetail.logo)} alt="" className="botImg imgBox"/>
|
||||
<div className="ml40 font-16">
|
||||
<div className="font-22">
|
||||
<a href={`/softbot/${id}`}>{botDetail && botDetail.market_name}</a>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Link } from "react-router-dom";
|
|||
import { getImageUrl } from 'educoder';
|
||||
import nullBot from '../../../../softbot/image/notBot.png';
|
||||
import { getAllInstallBots, deleteInstallBot } from "../../../../softbot/api";
|
||||
import logoBot from '../../img/logoBot.png';
|
||||
import './index.scss';
|
||||
import axios from "axios";
|
||||
|
||||
|
|
@ -85,7 +86,7 @@ function DispositionBot(props){
|
|||
{botList && (botList.length > 0 ? <div className="setBotListBox">
|
||||
{botList.map((item, index)=>{return <div className="disBotItem" key={index}>
|
||||
<div className="botNameLeft">
|
||||
<img src={getImageUrl(item.logo)} alt="" className="imgBox mr20"/>
|
||||
<img src={(item.logo && (item.logo.indexOf("347246")>-1 || item.logo.indexOf("412603")>-1)) ? logoBot : getImageUrl(item.logo)} alt="" className="imgBox mr20"/>
|
||||
<Link className="font-15 botName" to={`/settings/installbot/${item.bot_id}`}>{item.bot_name}</Link>
|
||||
<span className={`statusBox font-12 ml10 ${item.state === 0 ? '' : 'active'}`}>{item.state === 0 ? '挂起' : '启用'}</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { Link } from "react-router-dom";
|
|||
import { getImageUrl } from 'educoder';
|
||||
import { registerUpdateBot } from "../../../../../../softbot/api";
|
||||
import UploadImage from '../../../../../../forge/Team/Component/UploadImage.jsx';
|
||||
import logoBot from '../../../../img/logoBot.png';
|
||||
import {CopyToClipboard} from 'react-copy-to-clipboard';
|
||||
// 修改Bot
|
||||
function BasicInformation(props){
|
||||
|
|
@ -175,7 +176,7 @@ function BasicInformation(props){
|
|||
</Form>
|
||||
</div>}
|
||||
<div className="headPortraitBox">
|
||||
<UploadImage url={image && image.startsWith('/api/attachments/') ? getImageUrl(`${image}`) : image} getImage={getImage} getImageId = {getImageId}/>
|
||||
<UploadImage url={image ? ((image.indexOf('347246')>-1 ||image.indexOf('412603')>-1) ? logoBot : image) :logoBot} getImage={getImage} getImageId = {getImageId}/>
|
||||
<Button className="themeCorBorBut ml5" onClick={saveUpdateImage}>保存头像</Button>
|
||||
</div>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ function Putaway(props){
|
|||
const [ resourceFirst, setResourceFirst] = useState([]);
|
||||
const [ resourceSecond, setResourceSecond] = useState([]);
|
||||
const [ content , setContent ] = useState(undefined);
|
||||
const [ disabled , setDisabled ] = useState(undefined);
|
||||
// const [ disabled , setDisabled ] = useState(undefined);
|
||||
const [ detail, setDetail] = useState(undefined);
|
||||
const [ isEdit, setIsEdit] = useState(undefined);
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ function Putaway(props){
|
|||
})
|
||||
if(pathname && pathname.endsWith("/putaway/edit")){
|
||||
setIsEdit(true);
|
||||
setDisabled(true);
|
||||
// setDisabled(true);
|
||||
getMarketBotById({bot_id: id}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setDetail({
|
||||
|
|
@ -59,9 +59,9 @@ function Putaway(props){
|
|||
}, [pathname, botDetail])
|
||||
|
||||
// 监听同意协议
|
||||
function onChange(e){
|
||||
setDisabled(e.target.checked);
|
||||
}
|
||||
// function onChange(e){
|
||||
// setDisabled(e.target.checked);
|
||||
// }
|
||||
|
||||
function onContentChange(value){
|
||||
setContent(value);
|
||||
|
|
@ -102,7 +102,7 @@ function Putaway(props){
|
|||
user_id: current_user && current_user.user_id,
|
||||
bot_id: parseInt(id),
|
||||
category: "",
|
||||
is_receive_ag1: disabled ? 1 : 0,
|
||||
is_receive_ag1:1,
|
||||
is_receive_ag2: 0,
|
||||
logo: logo,
|
||||
market_time: "",
|
||||
|
|
@ -185,10 +185,10 @@ function Putaway(props){
|
|||
<Input.TextArea placeholder="请输入Webhook地址" maxLength={200} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item> */}
|
||||
{!isEdit && <Form.Item>
|
||||
<Checkbox onChange={onChange}>我已阅读并接受<a className="login-form-forgot" href="https://forum.trustie.net/forums/5029/detail" target="_blank">《GitLink服务协议条款》</a></Checkbox>
|
||||
</Form.Item>}
|
||||
<Form.Item><Button style={{width:"129px", height: '36px'}} type="primary" htmlType="submit" disabled={!disabled} className="putawayBut mt20">{isEdit ? '保存修改' : '申请上架'}</Button></Form.Item>
|
||||
{/* {!isEdit && <Form.Item>
|
||||
<Checkbox>我已阅读并接受<a className="login-form-forgot" href="https://forum.trustie.net/forums/5029/detail" target="_blank">《GitLink服务协议条款》</a></Checkbox>
|
||||
</Form.Item>} */}
|
||||
<Form.Item><Button style={{width:"129px", height: '36px'}} type="primary" htmlType="submit" className="putawayBut mt20">{isEdit ? '保存修改' : '申请上架'}</Button></Form.Item>
|
||||
</Form>}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Link } from "react-router-dom";
|
|||
import { getImageUrl } from 'educoder';
|
||||
import { getMyBot, getTransferToBot, receiveTransferBot, refuseTransferBot } from "../../../../softbot/api";
|
||||
import nullBot from '../../../../softbot/image/notBot1.png';
|
||||
import logoBot from '../../img/logoBot.png';
|
||||
import moment from "moment";
|
||||
|
||||
function ExploitBot(props){
|
||||
|
|
@ -96,7 +97,7 @@ function ExploitBot(props){
|
|||
{botList && (botList.length > 0 ? <div className="softBotListBox">
|
||||
{botList.map((item, index)=>{return <div className="softBotItem" key={index}>
|
||||
<div className="botOneLine botNameWarpBox">
|
||||
<img src={getImageUrl(item.logo)} alt="" className="imgBox mr20" object-fit="fill"/>
|
||||
<img src={(item.logo && (item.logo.indexOf("347246")>-1 || item.logo.indexOf("412603")>-1)) ? logoBot : getImageUrl(item.logo)} alt="" className="imgBox mr20" object-fit="fill"/>
|
||||
<Link className="font-15 botOneLine botNameBox" to={`/settings/mybot/configuration/${item.bot_id}`}>{item.bot_name}</Link>
|
||||
<span className={`font-12 ml10 statusBot ${item.is_public ? item.is_market ? 'market' : 'public' : 'private'}`}>{item.is_public ? item.is_market ? '上架' : '公开' : '私有'}</span>
|
||||
</div>
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 4.0 KiB |
|
|
@ -23,6 +23,17 @@ const Pages = Loadable({
|
|||
loader: () => import('./pages'),
|
||||
loading: Loading,
|
||||
})
|
||||
// 代码质量分析
|
||||
const Quality = Loadable({
|
||||
loader: () => import('./quality/index'),
|
||||
loading: Loading,
|
||||
})
|
||||
// 代码质量分析详情
|
||||
const QualityDetail = Loadable({
|
||||
loader: () => import('./quality/detail'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
function ServerIndex(props){
|
||||
const { projectDetail } = props;
|
||||
const { owner , projectsId } = props.match.params;
|
||||
|
|
@ -47,6 +58,16 @@ function ServerIndex(props){
|
|||
() => (<Data {...props}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/:owner/:projectsId/service/quality/detail"
|
||||
render={
|
||||
() => (<QualityDetail {...props}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/:owner/:projectsId/service/quality"
|
||||
render={
|
||||
() => (<Quality {...props}/>)
|
||||
}
|
||||
></Route>
|
||||
{/* 个人建站服务 */}
|
||||
{projectDetail && projectDetail.web_site && projectDetail.author.type === "User" && <Route path="/:owner/:projectsId/service/pages"
|
||||
render={
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function Main(props){
|
|||
<div>
|
||||
<AgreementModal visible={visible} onCancel={()=>setVisible(false)} onOk={onOk}/>
|
||||
<ul className="serverlist">
|
||||
<li>
|
||||
{/* <li>
|
||||
<span className="servername">
|
||||
<img src={require('./img/logo.png')} alt=""/>
|
||||
<a onClick={openDetail}>重睛鸟代码溯源</a>
|
||||
|
|
@ -54,7 +54,7 @@ function Main(props){
|
|||
<a onClick={openDetail} className="btnhover">查看详情</a>
|
||||
</span>
|
||||
</li>
|
||||
{/* <li>
|
||||
<li>
|
||||
<div className="servername" style={{marginBottom: '8px'}}>
|
||||
<img src={require('./img/logo3.png')} alt="" style={{width: '24px'}}/>
|
||||
<Link to={`/${owner}/${projectsId}/service/reposyncer`}>Reposyncer仓库同步</Link>
|
||||
|
|
@ -64,6 +64,16 @@ function Main(props){
|
|||
<Link to={`/${owner}/${projectsId}/service/reposyncer`} className="btnhover">查看详情</Link>
|
||||
</span>
|
||||
</li> */}
|
||||
<li>
|
||||
<div className="servername" style={{marginBottom: '8px'}}>
|
||||
<img src={require('./img/logo4.png')} alt="" style={{width: '24px'}}/>
|
||||
<Link to={`/${owner}/${projectsId}/service/quality`}>代码质量分析工具</Link>
|
||||
</div>
|
||||
<p className="task-hide-2 serverdesc">提供自动化的静态代码检查能力,能够识别代码中的缺陷、安全漏洞、代码异味,同时产出代码覆盖率和重复度的详细报告</p>
|
||||
<span className="serverbtn">
|
||||
<Link to={`/${owner}/${projectsId}/service/quality`} className="btnhover">查看详情</Link>
|
||||
</span>
|
||||
</li>
|
||||
{/* 是站点仓库则显示,否则隐藏 */}
|
||||
{/* {projectDetail && projectDetail.web_site && projectDetail.author.type === "User" && <li>
|
||||
<span className="servername">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import fetch from './fetch';
|
||||
|
||||
// 获取列表
|
||||
export function getList(params, owner, repo) {
|
||||
return fetch({
|
||||
url: `/api/v1/${owner}/${repo}/sonarqubes/issues_search.json?s=FILE_LINE`,
|
||||
// url: `/api/issues/search?components=kingchanx-fluid-cloudnative_fluid&s=FILE_LINE&issueStatuses=CONFIRMED%2COPEN&&additionalFields=_all&timeZone=Asia%2FShanghai`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 代码片段
|
||||
export function getCodeSnippets(key, owner, repo) {
|
||||
return fetch({
|
||||
url: `/api/v1/${owner}/${repo}/sonarqubes/sources_issue_snippet.json?issueKey=${ key }`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 问题分析
|
||||
export function getProblemDetail(key, owner, repo) {
|
||||
return fetch({
|
||||
url: `/api/v1/${owner}/${repo}/sonarqubes/rules_show.json?key=${ key }`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 数据汇总
|
||||
export function getProblemData(owner, repo , keys) {
|
||||
return fetch({
|
||||
url: `/api/v1/${owner}/${repo}/sonarqubes/measures_component.json?component=${ owner }-${ repo }&metricKeys=${ keys }`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化sonar
|
||||
export function initSonar(open, owner, repo) {
|
||||
return fetch({
|
||||
url: `/api/v1/${owner}/${repo}/sonarqubes/sonar_initialize.json?has_actions=${ open }`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// 开始分析
|
||||
export function startAlalyze( branch , owner, repo) {
|
||||
return fetch({
|
||||
url: `/api/v1/${owner}/${repo}/sonarqubes/insert_file.json?branch=${ branch }`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// // 获取action任务列表
|
||||
// export function getActionList(owner, repo ) {
|
||||
// return fetch({
|
||||
// url: `/api/v1/${owner}/${repo}/actions`,
|
||||
// method: 'get',
|
||||
// });
|
||||
// }
|
||||
|
||||
// 获取action任务列表
|
||||
export function getActionList(owner, repo ) {
|
||||
return fetch({
|
||||
url: `/api/v1/${owner}/${repo}/actions/runs?workflow=SonarScanner.yaml`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import bugs from '../img/quality/bugs.png'
|
||||
import vulnerabilities from '../img/quality/vulnerabilities.png'
|
||||
import code_smells from '../img/quality/code_smells.png'
|
||||
import low from '../img/quality/low.png'
|
||||
import medium from '../img/quality/medium.png'
|
||||
import high from '../img/quality/high.png'
|
||||
|
||||
export const issueType = {
|
||||
BUG: 'BUG',
|
||||
VULNERABILITY: '漏洞',
|
||||
CODE_SMELL: '异味'
|
||||
}
|
||||
|
||||
export const issueTypeKeys = {
|
||||
BUG: 'bugs',
|
||||
VULNERABILITY: 'vulnerabilities',
|
||||
CODE_SMELL: 'code_smells'
|
||||
}
|
||||
export const maintainability = {
|
||||
LOW: '低',
|
||||
MEDIUM: '中',
|
||||
HIGH: '高'
|
||||
}
|
||||
|
||||
export const issueTypeIcon = {
|
||||
BUG: bugs,
|
||||
VULNERABILITY: vulnerabilities,
|
||||
CODE_SMELL: code_smells
|
||||
}
|
||||
|
||||
export const maintainabilityIcon = {
|
||||
LOW: low,
|
||||
MEDIUM: medium,
|
||||
HIGH: high
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import javaFetch from '../javaFetch';
|
||||
|
||||
let settings = localStorage.chromesetting && JSON.parse(localStorage.chromesetting);
|
||||
// let actionUrl = (settings && settings.common && settings.common.softbot) ? settings.common.softbot :'http://172.20.32.202:4000' ;
|
||||
let actionUrl = 'http://172.20.32.202:4000'
|
||||
|
||||
const service = javaFetch(actionUrl);
|
||||
// export const httpUrl = actionUrl;
|
||||
// export const main_site_url = settings && settings.common.main_site_url
|
||||
export default service;
|
||||
|
After Width: | Height: | Size: 310 B |
|
After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 654 B |
|
After Width: | Height: | Size: 397 B |
|
After Width: | Height: | Size: 370 B |
|
After Width: | Height: | Size: 468 B |
|
After Width: | Height: | Size: 641 B |
|
After Width: | Height: | Size: 390 B |
|
After Width: | Height: | Size: 417 B |
|
After Width: | Height: | Size: 270 B |
|
After Width: | Height: | Size: 363 B |
|
After Width: | Height: | Size: 339 B |
|
After Width: | Height: | Size: 439 B |
|
After Width: | Height: | Size: 337 B |
|
|
@ -0,0 +1,41 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Drawer , Divider } from 'antd';
|
||||
import './index.scss'
|
||||
import close from '../../img/close.png'
|
||||
import moment from 'moment';
|
||||
|
||||
function ProblemDetail({visible, issue, issueDetail, onClose}){
|
||||
|
||||
onClose
|
||||
|
||||
return(
|
||||
issue && issueDetail && <Drawer
|
||||
placement="bottom"
|
||||
visible={visible}
|
||||
closable={false}
|
||||
onClose={onClose}
|
||||
height={460}
|
||||
maskStyle={{backgroundColor:'rgba(0,0,0,0.09)'}}
|
||||
>
|
||||
<div className="drawer-head">
|
||||
{ issue.message }
|
||||
<img src={close} alt="" onClick={onClose}/>
|
||||
</div>
|
||||
<div className="drawer-content">
|
||||
<p className="content-title">
|
||||
{ issue.message }
|
||||
<span>{ issue.rule }</span>
|
||||
</p>
|
||||
<p className="content-info">
|
||||
<span>{ issueDetail.type }</span>
|
||||
<span>{ issueDetail.severity }</span>
|
||||
<span>生效时间 { moment(issue.creationDate).format('YYYY年MM月DD日') }</span>
|
||||
<span>{ issueDetail.langName }</span>
|
||||
</p>
|
||||
<Divider dashed />
|
||||
<div className="content-detail" dangerouslySetInnerHTML={{ __html: issueDetail.htmlDesc }}></div>
|
||||
</div>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
export default ProblemDetail;
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
.drawer-head {
|
||||
height: 60px;
|
||||
background: #252D41;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #FFFFFF;
|
||||
padding: 20px 40px;
|
||||
img {
|
||||
width: 24px;
|
||||
margin-left: auto;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.drawer-content {
|
||||
height: 400px;
|
||||
padding: 20px 40px;
|
||||
overflow-y: auto;
|
||||
.content-title {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
color: #111010;
|
||||
span {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
.content-info {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #5D6790;
|
||||
margin-top: 15px;
|
||||
span:nth-of-type(2) {
|
||||
&::before {
|
||||
content: "|";
|
||||
color: rgba(187,189,197,0.61);
|
||||
margin-right: 13px;
|
||||
margin-left: 13px;
|
||||
}
|
||||
}
|
||||
span:nth-of-type(n + 3) {
|
||||
margin-left: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
import React, {useState, useEffect, useRef } from "react";
|
||||
import { getList, getCodeSnippets, getProblemDetail } from "../api";
|
||||
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';
|
||||
|
||||
function issueDetail(props){
|
||||
const { owner , projectsId } = props.match.params;
|
||||
|
||||
const [ snippetsDetail, setSnippetsDetail ] = useState(null);
|
||||
const [ issues, setIssues ] = useState({});
|
||||
const [ selectedIssue, setSelectedIssue ] = useState(null);
|
||||
const [ selectedFlow, setSelectedFlow ] = useState(null);
|
||||
const [ detailVisible, setDetailVisible ] = useState(true);
|
||||
const [ issueInfo, setIssueInfo ] = useState(null);
|
||||
const [reload, setReload] = useState();
|
||||
const [pageNum, setPageNum] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [total, setTotal] = useState()
|
||||
const { projectDetail, history } = props;
|
||||
const codeRef = useRef(null);
|
||||
|
||||
useEffect(()=>{
|
||||
// 更新网页标题
|
||||
if(projectDetail){
|
||||
if (history.location.state?.baseInfo) {
|
||||
handleIssues(history.location.state?.baseInfo)
|
||||
} else {
|
||||
getIssueList()
|
||||
}
|
||||
if (history.location.state?.selectedIssue) {
|
||||
setSelectedIssue(history.location.state?.selectedIssue)
|
||||
}
|
||||
}
|
||||
}, [projectDetail, pageNum])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedIssue) {
|
||||
getCodeSnippets(selectedIssue.key, owner, projectsId).then(res => {
|
||||
processIssues(res[selectedIssue.component].sources, issues[selectedIssue.component])
|
||||
setSnippetsDetail(res[selectedIssue.component]);
|
||||
})
|
||||
}
|
||||
},[selectedIssue])
|
||||
|
||||
useEffect(()=>{
|
||||
},[reload])
|
||||
|
||||
const getIssueList = (owner, name) => {
|
||||
let params = {
|
||||
p: pageNum,
|
||||
ps: pageSize
|
||||
};
|
||||
getList(params).then(res => {
|
||||
handleIssues(res)
|
||||
})
|
||||
};
|
||||
|
||||
const handleIssues = (data) => {
|
||||
let newIssues = data.issues
|
||||
let issuesHandled = JSON.parse(JSON.stringify(issues))
|
||||
setTotal(data.total)
|
||||
newIssues.forEach(e => {
|
||||
if (issuesHandled[e.component]) {
|
||||
issuesHandled[e.component].push(e)
|
||||
} else {
|
||||
issuesHandled[e.component] = [e]
|
||||
}
|
||||
});
|
||||
setIssues(issuesHandled)
|
||||
}
|
||||
|
||||
const addSpecialClassToRange = (code, textRange, className) => {
|
||||
let textOnly = code.replace(/<[^>]+>/g, ''); // Remove HTML tags to get text only
|
||||
let start = 0;
|
||||
let end = textOnly.length;
|
||||
|
||||
if (textRange.startLine === textRange.endLine) {
|
||||
start = textRange.startOffset;
|
||||
end = textRange.endOffset;
|
||||
}
|
||||
|
||||
let specialText = textOnly.slice(start, end);
|
||||
let highlightedText = `<span class="${className}">${specialText}</span>`;
|
||||
|
||||
let currentIndex = 0;
|
||||
let finalCode = '';
|
||||
let tag = false;
|
||||
let tagBuffer = '';
|
||||
|
||||
for (let i = 0; i < code.length; i++) {
|
||||
let char = code[i];
|
||||
|
||||
if (char === '<') {
|
||||
tag = true;
|
||||
tagBuffer += char;
|
||||
} else if (char === '>') {
|
||||
tagBuffer += char;
|
||||
tag = false;
|
||||
finalCode += tagBuffer;
|
||||
tagBuffer = '';
|
||||
} else if (tag) {
|
||||
tagBuffer += char;
|
||||
} else {
|
||||
if (currentIndex === start) {
|
||||
finalCode += highlightedText;
|
||||
}
|
||||
if (currentIndex >= start && currentIndex < end) {
|
||||
currentIndex++;
|
||||
continue;
|
||||
}
|
||||
finalCode += char;
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return finalCode;
|
||||
}
|
||||
|
||||
function processIssues(data, issues) {
|
||||
return data.map(item => {
|
||||
// 处理 issue 的 textRange
|
||||
issues.forEach(issue => {
|
||||
if (item.line === issue.line) {
|
||||
item.code = addSpecialClassToRange(item.code, issue.textRange, 'special');
|
||||
// 将 issue 添加到 data 项
|
||||
item.issue = issue
|
||||
}
|
||||
});
|
||||
// 处理选中 issue 的 flows 中的 textRange
|
||||
selectedIssue.flows.forEach(flow => {
|
||||
flow.locations.forEach(location => {
|
||||
if (item.line === location.textRange.startLine) {
|
||||
item.code = addSpecialClassToRange(item.code, location.textRange, 'special1');
|
||||
}
|
||||
});
|
||||
});
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
// 点击右侧列表重复选中
|
||||
function selectRepeat(e, line) {
|
||||
if (e.target.className === 'special1') {
|
||||
setSelectedFlow(line)
|
||||
}
|
||||
}
|
||||
|
||||
// 点击左侧列表跳转到对应位置
|
||||
function selectAndJumpRepeat(line) {
|
||||
setSelectedFlow(line)
|
||||
const element = document.getElementById(`line-${ line }`);
|
||||
if (element) {
|
||||
// 滚动到目标元素
|
||||
element.scrollIntoView({ behavior: 'smooth', block: "center" });
|
||||
}
|
||||
}
|
||||
|
||||
function detail() {
|
||||
getProblemDetail( selectedIssue.rule, owner, projectsId).then(res => {
|
||||
setIssueInfo(res?.rule)
|
||||
setDetailVisible(true)
|
||||
})
|
||||
}
|
||||
|
||||
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>
|
||||
{
|
||||
snippetsDetail && <div className="snippets" ref={ codeRef }>
|
||||
<div className="head"><img src={ folderImg } alt="" />{ snippetsDetail.component.path }</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>
|
||||
}
|
||||
export default issueDetail;
|
||||
|
|
@ -0,0 +1,461 @@
|
|||
.bug-details {
|
||||
display: flex;
|
||||
max-height: 80vh;
|
||||
.left-list {
|
||||
overflow-y: auto;
|
||||
width: 350px;
|
||||
flex-shrink: 0;
|
||||
.back-icon {
|
||||
width: 24px;
|
||||
}
|
||||
.ant-divider {
|
||||
padding-right: 10px;
|
||||
width: 330px;
|
||||
}
|
||||
.load-more {
|
||||
text-align: center;
|
||||
padding: 4px;
|
||||
margin-right: 10px;
|
||||
margin-right: 10px;
|
||||
background: #FAFCFF;
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
border: 1px solid rgba(42,97,255,0.23);
|
||||
font-size: 14px;
|
||||
color: #111010;
|
||||
cursor: pointer;
|
||||
}
|
||||
.doc-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
img {
|
||||
width: 18px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 240px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap; /* 防止文本换行 */
|
||||
overflow: hidden; /* 隐藏溢出的文本 */
|
||||
text-overflow: ellipsis; /* 显示省略号 */
|
||||
direction: rtl; /* 文字从右到左排列 */
|
||||
text-align: left; /* 文字从左边开始剪裁 */
|
||||
}
|
||||
margin-top: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.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;
|
||||
&:hover {
|
||||
border: 1px solid #466AFF;
|
||||
}
|
||||
.issue-typeline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
img {
|
||||
width: 14px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.issue-type {
|
||||
font-weight: 400;
|
||||
color: #5D6790;
|
||||
}
|
||||
.issue-duplication {
|
||||
margin-left: 5px;
|
||||
padding: 0 8px;
|
||||
background: #57575B;
|
||||
border-radius: 4px 4px 4px 4px;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.repeat-item {
|
||||
span {
|
||||
border-radius: 2px 2px 2px 2px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
span:nth-of-type(1) {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background: #C7797E;
|
||||
}
|
||||
span:nth-of-type(2) {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
padding: 0 7px;
|
||||
margin-left: 9px;
|
||||
height: 22px;
|
||||
background: #A8A8A8;
|
||||
}
|
||||
}
|
||||
.selected-repeat {
|
||||
span:nth-of-type(1) {
|
||||
background: #D4333F;
|
||||
}
|
||||
span:nth-of-type(2) {
|
||||
background: #475760;
|
||||
}
|
||||
}
|
||||
}
|
||||
.selected-issue {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #2A61FF;
|
||||
.issue-duplication {
|
||||
background: #D4333F;
|
||||
}
|
||||
}
|
||||
}
|
||||
.snippets {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
.head {
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
background: #FAFCFF;
|
||||
border: 1px solid #ECEEEF;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
color: #111010;
|
||||
padding-left: 20px;
|
||||
line-height: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
img {
|
||||
width: 18px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
border: 1px solid #ECEEEF;
|
||||
tbody {
|
||||
width: 100%;
|
||||
}
|
||||
tr:hover {
|
||||
background: rgb(239, 242, 249);
|
||||
}
|
||||
.td-index {
|
||||
width: 40px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.td-code {
|
||||
border-left: 1px solid rgb(221, 221, 221);
|
||||
padding-left: 20px;
|
||||
width: 100%;
|
||||
.code {
|
||||
position: relative;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
tab-size: 4;
|
||||
}
|
||||
.issue-info {
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgb(197, 205, 223);
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
word-break: break-word;
|
||||
span {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #466AFF;
|
||||
margin-left: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.selected-issue {
|
||||
border: 1px solid rgb(253, 162, 155);
|
||||
}
|
||||
.special {
|
||||
text-decoration: underline 2px wavy rgb(253, 162, 155);
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
.special1 {
|
||||
background-color: rgba(253, 162, 155, 0.15);
|
||||
cursor: pointer;
|
||||
}
|
||||
.selected-flow {
|
||||
.special1 {
|
||||
background-color: rgba(253, 162, 155, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
.td-ellipsis {
|
||||
text-align: center;
|
||||
border-top: 1px solid #ECEEEF;
|
||||
border-bottom: 1px solid #ECEEEF;
|
||||
}
|
||||
}
|
||||
.cd {
|
||||
font-family: "Ubuntu Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-style: italic;
|
||||
color: rgb(95, 96, 102);
|
||||
}
|
||||
.k {
|
||||
font-family: "Ubuntu Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-weight: 700;
|
||||
color: rgb(162, 34, 160);
|
||||
}
|
||||
.s {
|
||||
color: rgb(36, 117, 35);
|
||||
}
|
||||
}
|
||||
}
|
||||
.issue-total {
|
||||
.nodata {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
width: 400px;
|
||||
}
|
||||
p {
|
||||
margin-top: 20px;
|
||||
font-size: 16px;
|
||||
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;
|
||||
.show-more-list {
|
||||
max-height: 2300px;
|
||||
}
|
||||
.show-less-list {
|
||||
max-height: 1400px;
|
||||
}
|
||||
.left-content {
|
||||
width: 218px;
|
||||
flex-shrink: 0;
|
||||
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;
|
||||
cursor: pointer;
|
||||
}
|
||||
.filters {
|
||||
.filter-title {
|
||||
font-weight: bold;
|
||||
font-size: 17px;
|
||||
color: #000000;
|
||||
padding: 20px;
|
||||
line-height: 28px;
|
||||
.clear-button {
|
||||
float: right;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.filter-item {
|
||||
padding: 0 20px;
|
||||
margin: 2px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #466bff0c;
|
||||
}
|
||||
.item-type {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
img {
|
||||
width: 16px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.selected-item {
|
||||
background: #466bff17;
|
||||
}
|
||||
.ant-divider {
|
||||
margin: 20px;
|
||||
margin-bottom: 0px;
|
||||
width: auto;
|
||||
min-width: unset;
|
||||
}
|
||||
.show-more {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #5D6790;
|
||||
margin-left: 20px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right-content {
|
||||
margin-left: 17px;
|
||||
flex-grow: 1;
|
||||
padding: 20px;
|
||||
padding-top: 10px;
|
||||
.doc-title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
color: #111010;
|
||||
margin: 19px 0;
|
||||
}
|
||||
.issue {
|
||||
padding: 15px 20px;
|
||||
background: #FAFCFF;
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
border: 1px solid rgba(42,97,255,0.23);
|
||||
margin-bottom: 12px;
|
||||
.content-info {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #5D6790;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
> span:nth-child(2), > span:nth-child(3) {
|
||||
&::before {
|
||||
content: "|";
|
||||
color: rgba(187,189,197,0.61);
|
||||
margin-right: 13px;
|
||||
margin-left: 13px;
|
||||
}
|
||||
}
|
||||
span:nth-of-type(n + 4) {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
.issue-message {
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
color: #111010;
|
||||
word-break: break-word;
|
||||
span:nth-of-type(1) {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #466AFF;
|
||||
margin-left: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
span:nth-of-type(2) {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.total-info {
|
||||
padding: 20px;
|
||||
p {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
color: #333333;
|
||||
}
|
||||
.info-list {
|
||||
display: flex;
|
||||
.total-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 53px;
|
||||
font-weight: 400;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.headBox{
|
||||
height:60px;
|
||||
line-height: 60px;
|
||||
background-color:#fafcff;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
border-radius:3px 3px 0px 0px;
|
||||
color:#333333;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nullStoreBox{
|
||||
background-color:#fafcff;
|
||||
border-radius:4px 4px 0px 0px;
|
||||
text-align: center;
|
||||
color:#333333;
|
||||
padding-bottom: 65px;
|
||||
.loBox{width: 68px;}
|
||||
.introBox{
|
||||
color:#666666;
|
||||
width: 57%;
|
||||
margin: 15px auto;
|
||||
}
|
||||
.borBox{
|
||||
width: 45%;
|
||||
margin: 0 auto 20px;
|
||||
border-bottom: 1px solid rgba(90, 117, 193, 0.23);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
img {
|
||||
width: 16px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
import React, { useState, useEffect, Fragment } from "react";
|
||||
import { getList, getProblemDetail, getProblemData, initSonar, startAlalyze, getActionList } from "../api";
|
||||
import './detail.scss';
|
||||
import ProblemDetail from "./component/ProblemDetail";
|
||||
import { issueType, maintainability, issueTypeIcon, maintainabilityIcon } 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 nodata from '../../Images/nodata.png'
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
|
||||
const actionStatus = {
|
||||
success: 1,
|
||||
failure: 2,
|
||||
waiting: 5,
|
||||
running: 6,
|
||||
none: 7
|
||||
}
|
||||
|
||||
function IssueList(props) {
|
||||
const [issues, setIssues] = useState({});
|
||||
const [baseInfo, setBaseInfo] = useState({});
|
||||
const [baseData, setBaseData] = useState({});
|
||||
const [listFilter, setListFilter] = useState({});
|
||||
const [selectedIssue, setSelectedIssue] = useState(null);
|
||||
const [detailVisible, setDetailVisible] = useState(true);
|
||||
const [issueInfo, setIssueInfo] = useState({});
|
||||
const [selectedType, setSelectedType] = useState([]);
|
||||
const [selectedSeverity, setSelectedSeverity] = useState([]);
|
||||
const [selectedTag, setSelectedTag] = useState([]);
|
||||
const [pageNum, setPageNum] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [pageLoading, setPageLoading] = useState(true);
|
||||
const [analysisStatus, setAnalysisStatus] = useState(null);
|
||||
const [tagShowMore, setTagShowMore] = useState(false);
|
||||
const [tagShowMoreDelay, setTagShowMoreDelay] = useState(false);
|
||||
const { projectDetail, history } = props;
|
||||
const metricKeys = 'lines,coverage,lines_to_cover,duplicated_lines_density,maintainability_issues,vulnerabilities,bugs,code_smells';
|
||||
const facets = 'impactSeverities,tags,types';
|
||||
const { owner , projectsId } = props.match.params;
|
||||
|
||||
useEffect(() => {
|
||||
let newContainer = document.getElementsByClassName("newContainer")[0];
|
||||
// newContainer.style.backgroundColor = '#F7F9FCFF';
|
||||
return () => {
|
||||
newContainer.style.backgroundColor = '#FFFFFF';
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (projectDetail) {
|
||||
const { author, name } = projectDetail;
|
||||
document.title = `代码质量分析-${author.name}/${name}`;
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [projectDetail]);
|
||||
|
||||
const getData = () => {
|
||||
getProblemData(owner, projectsId, metricKeys).then(res => {
|
||||
if (res.status === 0) {
|
||||
const measures = res.component.measures;
|
||||
const result = {};
|
||||
measures.forEach(measure => {
|
||||
result[measure.metric] = measure.value;
|
||||
});
|
||||
setBaseData(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getIssueList = () => {
|
||||
let params = {
|
||||
facets: facets,
|
||||
types: selectedType.join(','),
|
||||
impactSeverities: selectedSeverity.join(','),
|
||||
tags: selectedTag.join(','),
|
||||
p: pageNum,
|
||||
ps: pageSize
|
||||
};
|
||||
setPageLoading(true)
|
||||
getList(params, owner, projectsId).then(res => {
|
||||
setPageLoading(false)
|
||||
const issues = 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) => {
|
||||
acc[facet.property] = facet.values;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
setListFilter(filter);
|
||||
setBaseInfo(res);
|
||||
setIssues(issues);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (pageNum === 1) {
|
||||
getIssueList()
|
||||
} else {
|
||||
setPageNum(1)
|
||||
}
|
||||
}, [selectedType, selectedSeverity, selectedTag]);
|
||||
|
||||
useEffect(() => {
|
||||
if (projectDetail && analysisStatus === actionStatus.success) {
|
||||
getIssueList()
|
||||
}
|
||||
}, [pageNum, pageSize]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!tagShowMore) {
|
||||
setTimeout(() => {
|
||||
setTagShowMoreDelay(tagShowMore)
|
||||
}, 450);
|
||||
} else {
|
||||
setTagShowMoreDelay(tagShowMore)
|
||||
}
|
||||
}, [tagShowMore])
|
||||
|
||||
const detail = (e) => {
|
||||
setSelectedIssue(e);
|
||||
getProblemDetail(e.rule, owner, projectsId).then(res => {
|
||||
setIssueInfo(res?.rule);
|
||||
setDetailVisible(true);
|
||||
});
|
||||
};
|
||||
|
||||
const toggleSelection = (state, setter, value) => {
|
||||
window.scrollTo(0,0);
|
||||
const index = state.indexOf(value);
|
||||
if (index > -1) {
|
||||
state.splice(index, 1);
|
||||
setter([...state]);
|
||||
} else {
|
||||
setter([...state, value]);
|
||||
}
|
||||
};
|
||||
|
||||
const clearAllSelection = () => {
|
||||
setSelectedType([])
|
||||
setSelectedSeverity([])
|
||||
setSelectedTag([])
|
||||
}
|
||||
|
||||
// 筛选列表
|
||||
const renderFilterItems = (items, state, setter, label, showMoreButton = false) => {
|
||||
const displayedItems = showMoreButton && items && !tagShowMoreDelay ? items.slice(0, 10) : items;
|
||||
|
||||
return (<div className={`filters`}>
|
||||
<div className="filter-title">
|
||||
<span>{label}</span>
|
||||
{ state.length > 0 && <button className="clear-button" onClick={() => setter([])}>清空</button> }
|
||||
</div>
|
||||
{displayedItems && displayedItems.map(e => (
|
||||
<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>{e.count}</span>
|
||||
</div>
|
||||
))}
|
||||
{ showMoreButton && items.length > 10 && <span className="show-more" onClick={() => { setTagShowMore(!tagShowMore) }}> { tagShowMore ? '收起' : '显示更多'} </span> }
|
||||
<Divider dashed />
|
||||
</div>)
|
||||
};
|
||||
|
||||
// 总览项
|
||||
const renderTotalItem = (label, value, icon) => (
|
||||
<div className="total-item">
|
||||
<span className="icon-text"> <img src={ icon} alt="" /> {label}</span>
|
||||
<span className="ml20">{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">
|
||||
{percent}%
|
||||
<span className="item-more">{moreValue}<span>{formatNumber(value)}</span>行</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const toDetail = (e) => {
|
||||
history.push({
|
||||
pathname: `/${owner}/${projectsId}/service/quality/detail`,
|
||||
state: {
|
||||
baseInfo,
|
||||
selectedIssue: e
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const openAnalysis = () => {
|
||||
setLoading(true)
|
||||
initSonar(true, owner, projectsId).then(res => {
|
||||
if (res.status === 0) {
|
||||
startAlalyze(projectDetail.default_branch, owner, projectsId).then(res1=> {
|
||||
if (res1.status === 0) {
|
||||
message.success('创建分析成功,请等待分析完成')
|
||||
setAnalysisStatus(actionStatus.waiting)
|
||||
}
|
||||
setLoading(false)
|
||||
})
|
||||
} else {
|
||||
setLoading(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 开始按钮状态
|
||||
const getStatusButton = () => {
|
||||
switch (analysisStatus) {
|
||||
case actionStatus.none:
|
||||
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>
|
||||
case actionStatus.failure:
|
||||
return <div>
|
||||
<p>分析失败,请重试</p>
|
||||
<Button type="primary" style={{width: '112px', height: '36px'}} onClick={openAnalysis} loading={loading}>开始分析</Button>
|
||||
</div>
|
||||
default:
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Spin spinning={pageLoading}>
|
||||
<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">
|
||||
<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>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
|
||||
export default IssueList;
|
||||
|
|
@ -2,7 +2,7 @@ import React, { Fragment, useEffect, useState} from "react";
|
|||
import { Button, Modal, Icon, message, Select, Input, Form, Radio, Table, Divider, Badge, Spin, Checkbox } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import gitlinkLogo from '../../img/gitlink.jpg';
|
||||
import logo from '../../img/logo3.png';
|
||||
import logo from '../../img/logo3-1.png';
|
||||
import '../index.scss';
|
||||
import axios from "axios";
|
||||
import AddTaskModal from './addTaskModal';
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default (props)=>{
|
|||
return(
|
||||
<Box>
|
||||
<Short>
|
||||
<Setnav header={<Title>团队设置</Title>} nav={array}></Setnav>
|
||||
<Setnav header={<Title>团队设置</Title>} nav={array} history={props.history}></Setnav>
|
||||
</Short>
|
||||
<Long>
|
||||
<Gap>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ function List(props){
|
|||
|
||||
const OIdentifier = props.match.params.OIdentifier;
|
||||
const { organizeDetail, enterpriseOpenInfo } = props;
|
||||
console.log("------------------",enterpriseOpenInfo,props);
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
|
|
@ -225,6 +224,7 @@ function List(props){
|
|||
showCompeleteDialog={props.showCompeleteDialog}
|
||||
completeProfile={props.completeProfile}
|
||||
history={props.history}
|
||||
enterpriseOpenInfo={enterpriseOpenInfo}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -49,10 +49,11 @@ const Img = styled.img`{
|
|||
height:45px;
|
||||
margin-right:12px;
|
||||
}`
|
||||
function RightBox({ OIdentifier , history , admin , showCompeleteDialog ,completeProfile }) {
|
||||
function RightBox({ OIdentifier , history , admin , showCompeleteDialog ,completeProfile ,enterpriseOpenInfo }) {
|
||||
const [ languageData, setLanguageData ] = useState(undefined);
|
||||
const [ memberData, setMemberData ] = useState(undefined);
|
||||
const [ groupData, setGroupData ] = useState(undefined);
|
||||
const { isOpen } = enterpriseOpenInfo;
|
||||
|
||||
useEffect(()=>{
|
||||
if(OIdentifier){
|
||||
|
|
@ -148,46 +149,49 @@ function RightBox({ OIdentifier , history , admin , showCompeleteDialog ,complet
|
|||
</Box>
|
||||
:""
|
||||
}
|
||||
<Box
|
||||
name="组织团队"
|
||||
count={groupData && groupData.total_count}
|
||||
icon={<i className="iconfont icon-zuzhituandui font-17 mr8"></i>}
|
||||
bottom={
|
||||
admin &&
|
||||
<CheckProfile
|
||||
showCompeleteDialog={showCompeleteDialog}
|
||||
completeProfile={completeProfile}
|
||||
sureFunc={()=>history.push(`/${OIdentifier}/teams/new`)}
|
||||
className={"ant-btn newBtn"}
|
||||
>新建团队<img src={Right} alt="" width="9" className="ml5" /></CheckProfile>
|
||||
}
|
||||
url={`/${OIdentifier}/teams`}
|
||||
>
|
||||
{
|
||||
groupData && groupData.teams && groupData.teams.length>0?
|
||||
<React.Fragment>
|
||||
{groupData.teams.map((item,key)=>{
|
||||
return(
|
||||
key < 5 &&<div className="teammembers" key={key}>
|
||||
<span className="memberIndex">{key+1}</span>
|
||||
<div>
|
||||
{
|
||||
(item.is_admin || item.is_member) ?
|
||||
<Link to={`/${OIdentifier}/teams/${item.id}`}><ColorListName>{item.nickname}</ColorListName></Link>
|
||||
:
|
||||
<ColorListName>{item.nickname}</ColorListName>
|
||||
}
|
||||
<Align>
|
||||
<Span><span style={{color:"rgba(31, 35, 41, 1)"}}>{item.num_users}</span>名成员</Span>
|
||||
<Span><span style={{color:"rgba(31, 35, 41, 1)"}}>{item.num_projects}</span>个仓库</Span>
|
||||
</Align>
|
||||
{
|
||||
!isOpen &&
|
||||
<Box
|
||||
name="组织团队"
|
||||
count={groupData && groupData.total_count}
|
||||
icon={<i className="iconfont icon-zuzhituandui font-17 mr8"></i>}
|
||||
bottom={
|
||||
admin &&
|
||||
<CheckProfile
|
||||
showCompeleteDialog={showCompeleteDialog}
|
||||
completeProfile={completeProfile}
|
||||
sureFunc={()=>history.push(`/${OIdentifier}/teams/new`)}
|
||||
className={"ant-btn newBtn"}
|
||||
>新建团队<img src={Right} alt="" width="9" className="ml5" /></CheckProfile>
|
||||
}
|
||||
url={`/${OIdentifier}/teams`}
|
||||
>
|
||||
{
|
||||
groupData && groupData.teams && groupData.teams.length>0?
|
||||
<React.Fragment>
|
||||
{groupData.teams.map((item,key)=>{
|
||||
return(
|
||||
key < 5 &&<div className="teammembers" key={key}>
|
||||
<span className="memberIndex">{key+1}</span>
|
||||
<div>
|
||||
{
|
||||
(item.is_admin || item.is_member) ?
|
||||
<Link to={`/${OIdentifier}/teams/${item.id}`}><ColorListName>{item.nickname}</ColorListName></Link>
|
||||
:
|
||||
<ColorListName>{item.nickname}</ColorListName>
|
||||
}
|
||||
<Align>
|
||||
<Span><span style={{color:"rgba(31, 35, 41, 1)"}}>{item.num_users}</span>名成员</Span>
|
||||
<Span><span style={{color:"rgba(31, 35, 41, 1)"}}>{item.num_projects}</span>个仓库</Span>
|
||||
</Align>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</React.Fragment>:<Nodata _html="暂无团队" small/>
|
||||
}
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</React.Fragment>:<Nodata _html="暂无团队" small/>
|
||||
}
|
||||
</Box>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export default (props) => {
|
|||
</div>
|
||||
{wikiDetail && <div className="wiki-content">
|
||||
<Spin spinning={loading}>
|
||||
<div className="spacebetween intermediatecenterysls pt20 pl20 pr20">
|
||||
<div className="spacebetween intermediatecenterysls pt20 pl20 pr20" style={{display:'flex'}}>
|
||||
<div style={{width: '80%'}}>
|
||||
<p className="font-16 task-hide">{wikiDetail.title}</p>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ function Index(props){
|
|||
setType('1');
|
||||
documentTitle = "密码管理";
|
||||
break;
|
||||
case '/settings/verification':
|
||||
setType('4');
|
||||
documentTitle = "实名认证";
|
||||
break;
|
||||
// case '/settings/verification':
|
||||
// setType('4');
|
||||
// documentTitle = "实名认证";
|
||||
// break;
|
||||
default:// '/settings/phone'
|
||||
documentTitle = "手机号管理";
|
||||
setType('3');
|
||||
|
|
@ -54,7 +54,7 @@ function Index(props){
|
|||
<Menu.Item key="3" className="font-16" onClick={()=>{props.history.push('/settings/phone')}}>手机号管理</Menu.Item>
|
||||
<Menu.Item key="0" className="font-16" onClick={()=>{props.history.push('/settings/emails')}}>邮箱管理</Menu.Item>
|
||||
<Menu.Item key="1" className="font-16" onClick={()=>{props.history.push('/settings/password')}}>密码管理</Menu.Item>
|
||||
<Menu.Item key="4" className="font-16" onClick={()=>{props.history.push('/settings/verification')}}>实名认证</Menu.Item>
|
||||
{/* <Menu.Item key="4" className="font-16" onClick={()=>{props.history.push('/settings/verification')}}>实名认证</Menu.Item> */}
|
||||
{/* <Menu.Item key="2" className="font-16" onClick={()=>{props.history.push('/settings/cancel')}}>账号注销</Menu.Item> */}
|
||||
</Menu>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -203,9 +203,9 @@ export default Form.create()(
|
|||
function updateEmailOrPsd(){
|
||||
if(type === '0'){
|
||||
const {code, email, password1} = getFieldsValue();
|
||||
if(code && email && password1){
|
||||
if(email && password1){
|
||||
current_user && Axios.patch(`/v1/${current_user.login}/update_email.json`,{
|
||||
code, email, password: password1
|
||||
code:"123123", email, password: password1
|
||||
}).then(res=>{
|
||||
if(res && !res.data.status){
|
||||
setPassMap({password: undefined, email: undefined, code: undefined});
|
||||
|
|
@ -242,7 +242,7 @@ export default Form.create()(
|
|||
<Fragment>
|
||||
{type === '0' ?
|
||||
<div>
|
||||
<div className='tipsBox mb15'><img src={img1} alt="" width={16} className='mr10'/>更改邮箱后,您在<span className='spanTil'>GitLink</span> 的登录账号、接收通知的邮件地址将同步变更。请谨慎更改!</div>
|
||||
<div className='tipsBox mb15'><img src={img1} alt="" width={16} className='mr10'/>更改邮箱后,您在<span className='spanTil'>系统</span> 的登录账号、接收通知的邮件地址将同步变更。请谨慎更改!</div>
|
||||
<div className='tipsBox mb20 emailBox'>现邮箱地址<span className='ml10 email spanTil'>{current_user && current_user.email}</span></div>
|
||||
<div className='updateEmail mb20'>更改邮箱</div>
|
||||
<Form layout={'inline'} className="formBase passMan personalUpdateInfoForm">
|
||||
|
|
@ -269,7 +269,7 @@ export default Form.create()(
|
|||
<Input placeholder="请输入新邮箱地址" style={{width:"400px"}} autoComplete={"off"}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div>
|
||||
{/* <div>
|
||||
<Form.Item label="邮箱验证码" className={`${passMap.code ? 'hasSuccess' : ''}`}>
|
||||
{getFieldDecorator("code",{
|
||||
rules:[
|
||||
|
|
@ -282,10 +282,10 @@ export default Form.create()(
|
|||
)}
|
||||
</Form.Item>
|
||||
{countDown ? <Button className='getCaptchaBut ml50' disabled>重发 ({countDown})</Button> : <Button className='getCaptchaBut ml50' onClick={getCaptcha} disabled={!emailValue}>获取验证码</Button>}
|
||||
</div>
|
||||
</div> */}
|
||||
<AlignCenter style={{marginTop:"20px"}}>
|
||||
<span className="ant-form-item-label mr10"></span>
|
||||
<Button className={`${passMap.password && passMap.email && passMap.code ? 'but25' : ''}`} type={"primary"} onClick={confirmUpdateEmail} disabled={!(passMap.password && passMap.email && passMap.code)}>确认更改</Button>
|
||||
<Button className={`${passMap.password && passMap.email ? 'but25' : ''}`} type={"primary"} onClick={confirmUpdateEmail} disabled={!(passMap.password && passMap.email)}>确认更改</Button>
|
||||
</AlignCenter>
|
||||
</Form>
|
||||
</div> :
|
||||
|
|
@ -360,7 +360,7 @@ export default Form.create()(
|
|||
<div className="desc">
|
||||
<AlignCenter className="descMain">
|
||||
<i className="iconfont icon-jinggao1 mr10 font-20 red"></i>确认更改{type === '0' ? '邮箱' : '密码'}?</AlignCenter>
|
||||
<p>{type === '0' ? '更改邮箱后,您在GitLink的登录账号、接收通知的邮件地址将同步变更。请再次确认。' : '更改密码后,您在GitLink的登录密码将同步变更。请再次确认。'}</p>
|
||||
<p>{type === '0' ? '更改邮箱后,您在星云的登录账号、接收通知的邮件地址将同步变更。请再次确认。' : '更改密码后,您在星云的登录密码将同步变更。请再次确认。'}</p>
|
||||
</div>
|
||||
</Modals>
|
||||
</Fragment>
|
||||
|
|
|
|||
|
|
@ -179,10 +179,10 @@ export default Form.create()(
|
|||
<div className="desc">
|
||||
<AlignCenter className="descMain">
|
||||
<i className="iconfont icon-jinggao1 mr10 font-20 red"></i>确认更改手机号?</AlignCenter>
|
||||
<p>更改手机号后,您在GitLink的登录账号将同步变更。请再次确认。</p>
|
||||
<p>更改手机号后,您在系统的登录账号将同步变更。请再次确认。</p>
|
||||
</div>
|
||||
</Modals>
|
||||
<div className='tipsBox mb15'><img src={img1} alt="" width={16} className='mr10'/>更改手机号后,您在<span className='spanTil'>GitLink</span> 的登录账号将同步变更。请谨慎更改!</div>
|
||||
<div className='tipsBox mb15'><img src={img1} alt="" width={16} className='mr10'/>更改手机号后,您在<span className='spanTil'>系统</span> 的登录账号将同步变更。请谨慎更改!</div>
|
||||
{current_user && current_user.phone && <div className='tipsBox mb20 emailBox'>现手机号码<span className='ml10 email spanTil'>{current_user.phone}</span></div> }
|
||||
<div className='updateEmail mb20'>更改手机号</div>
|
||||
<Form layout={'inline'} className="formBase passMan personalUpdateInfoForm">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { url, zoneUrl } from '../ssrUrl';
|
|||
let baseUrl = `${url}/api`
|
||||
let baseZoneUrl = `${zoneUrl}/api`
|
||||
|
||||
if (true ) {
|
||||
if (__CLIENT__) {
|
||||
let settings = localStorage.chromesetting && localStorage.chromesetting !== 'undefined' &&JSON.parse(localStorage.chromesetting);
|
||||
baseUrl = (settings && settings.common && settings.common.main_site_url) ? `${settings.common.main_site_url}/api` : '/api';
|
||||
baseZoneUrl = settings ? `${settings && settings.common.zone}/api` : `${zoneUrl}/api`;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import javaFetch from '../forge/javaFetch';
|
||||
|
||||
let settings = localStorage.chromesetting && JSON.parse(localStorage.chromesetting);
|
||||
let actionUrl = settings && settings.common.softbot;
|
||||
// let actionUrl = 'http://111.8.36.180:8069';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { Button, Checkbox, message, Modal, Radio, Select } from "antd";
|
||||
import {getBotDetail, installMarketBot, judgeIsIntallBot} from '../api';
|
||||
import logoBot from '../image/logoBot.png';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import './index.scss';
|
||||
import axios from "axios";
|
||||
|
|
@ -171,7 +172,7 @@ function SoftBotDEtail(props){
|
|||
return <div className="softBotDetailBox">
|
||||
{detail && <div className="botDetailHead">
|
||||
<div className="botDetailHeadCont">
|
||||
<div className="botImgBg"><img src={getImageUrl(detail.logo)} alt="" className="botImg"></img></div>
|
||||
<div className="botImgBg"><img src={(detail.logo && (detail.logo.indexOf("347246")>-1 || detail.logo.indexOf("412603")>-1)) ? logoBot : getImageUrl(detail.logo)} alt="" className="botImg"></img></div>
|
||||
<div className="centerBox">
|
||||
<p className="font-24">{detail.bot_name}</p>
|
||||
<div className="userBox font-16">{detail.first_func && <span className="typeBox font-15 mr15">{detail.first_func}</span>}{detail.second_func && <span className="typeBox font-15 mr15">{detail.second_func}</span>}<a href={`/${detail.developer_login}`}>{detail.developer_name}</a></div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
|||
import { Button, Input, Pagination, Spin } from "antd";
|
||||
import { getImageUrl } from 'educoder'
|
||||
import './index.scss';
|
||||
import { Link } from "react-router-dom";
|
||||
import logoBot from '../image/logoBot.png';
|
||||
import {getAllBotCategory, getContentsLikeNameAndFunc} from '../api.js';
|
||||
import Nodata from '../../forge/Nodata'
|
||||
|
||||
|
|
@ -68,8 +68,8 @@ function SoftBot(props){
|
|||
return <div className="softbotBox">
|
||||
<div className="softbotHead">
|
||||
<div className="softheadContent">
|
||||
<img src={getImageUrl(mygetHelmetapi.login_logo_url)} alt="" width={180}/>
|
||||
<div className="bigTit mt30">欢迎来到Bot市场</div>
|
||||
{/* <img src={getImageUrl(mygetHelmetapi.login_logo_url)} alt="" width={180}/> */}
|
||||
<div className="bigTit">欢迎来到Bot市场</div>
|
||||
<div className="bigTit">快来挑选一个心仪的Bot加入到您的项目吧!</div>
|
||||
<Button type="primary" style={{width: '152px', height:'40px'}} className="font-18 gotoRegister" onClick={registerBot}>立即注册<i className="iconfont icon-youjiantou font-15"></i></Button>
|
||||
</div>
|
||||
|
|
@ -85,7 +85,7 @@ function SoftBot(props){
|
|||
{!loading && botList && botList.length > 0 && botList.map((item, index)=>{
|
||||
return <div className="oneBotItem mt30" key={index} onClick={()=>{window.location.href=`/softbot/${item.bot_id}`}}>
|
||||
<div className="oneItemHead">
|
||||
<div className="oneBotImgBox"><img src={getImageUrl(item.logo)} alt="" className="oneBotImg"/></div>
|
||||
<div className="oneBotImgBox"><img src={(item.logo && (item.logo.indexOf("347246")>-1 || item.logo.indexOf("412603")>-1)) ? logoBot : getImageUrl(item.logo)} alt="" className="oneBotImg"/></div>
|
||||
<div className="oneBotLine">
|
||||
<p className="font-16 oneBotName oneBotLine">{item.market_name}</p>
|
||||
<p>{item.developer_name}</p>
|
||||
|
|
|
|||
|
|
@ -13,14 +13,13 @@
|
|||
height:460px;
|
||||
background-size: 100%;
|
||||
background-image: url('../image/banner.png');
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.bigTit{
|
||||
color: white;
|
||||
font-size: 38px;
|
||||
text-align: center;
|
||||
}
|
||||
.softheadContent{
|
||||
padding: 50px 0 100px;
|
||||
}
|
||||
.gotoRegister{
|
||||
display: block;
|
||||
margin: 40px auto 0;
|
||||
|
|
|
|||