merge
This commit is contained in:
commit
0d17abc4b0
|
|
@ -7847,7 +7847,7 @@
|
|||
},
|
||||
"fsevents": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"resolved": "http://173.15.15.82:8081/repository/npm-all/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
@ -11299,7 +11299,7 @@
|
|||
"dependencies": {
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "http://173.15.15.82:8081/repository/npm-all/yallist/-/yallist-4.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
}
|
||||
|
|
@ -15441,7 +15441,7 @@
|
|||
},
|
||||
"react-copy-to-clipboard": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
|
||||
"integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==",
|
||||
"requires": {
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
|
|
@ -15450,7 +15450,7 @@
|
|||
"dependencies": {
|
||||
"prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
|
|
@ -20220,7 +20220,7 @@
|
|||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"resolved": "http://173.15.15.82:8081/repository/npm-all/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2340181 */
|
||||
src: url('iconfont.woff2?t=1675409079100') format('woff2'),
|
||||
url('iconfont.woff?t=1675409079100') format('woff'),
|
||||
url('iconfont.ttf?t=1675409079100') format('truetype');
|
||||
src: url('iconfont.woff2?t=1680498859493') format('woff2'),
|
||||
url('iconfont.woff?t=1680498859493') format('woff'),
|
||||
url('iconfont.ttf?t=1680498859493') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
|
|
@ -13,6 +13,38 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-a-zu37:before {
|
||||
content: "\e961";
|
||||
}
|
||||
|
||||
.icon-guanzhuicon:before {
|
||||
content: "\e95b";
|
||||
}
|
||||
|
||||
.icon-yidianzan_icon:before {
|
||||
content: "\e95c";
|
||||
}
|
||||
|
||||
.icon-morenfuke_icon1:before {
|
||||
content: "\e95d";
|
||||
}
|
||||
|
||||
.icon-yiguanzhuicon:before {
|
||||
content: "\e95e";
|
||||
}
|
||||
|
||||
.icon-yifuke_icon:before {
|
||||
content: "\e95f";
|
||||
}
|
||||
|
||||
.icon-xingzhuang:before {
|
||||
content: "\e960";
|
||||
}
|
||||
|
||||
.icon-kaiqi:before {
|
||||
content: "\e95a";
|
||||
}
|
||||
|
||||
.icon-a-31shijian:before {
|
||||
content: "\e944";
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -5,6 +5,62 @@
|
|||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "34875122",
|
||||
"name": "组 37",
|
||||
"font_class": "a-zu37",
|
||||
"unicode": "e961",
|
||||
"unicode_decimal": 59745
|
||||
},
|
||||
{
|
||||
"icon_id": "34819652",
|
||||
"name": "关注icon",
|
||||
"font_class": "guanzhuicon",
|
||||
"unicode": "e95b",
|
||||
"unicode_decimal": 59739
|
||||
},
|
||||
{
|
||||
"icon_id": "34819653",
|
||||
"name": "已点赞_icon",
|
||||
"font_class": "yidianzan_icon",
|
||||
"unicode": "e95c",
|
||||
"unicode_decimal": 59740
|
||||
},
|
||||
{
|
||||
"icon_id": "34819654",
|
||||
"name": "默认复刻_icon",
|
||||
"font_class": "morenfuke_icon1",
|
||||
"unicode": "e95d",
|
||||
"unicode_decimal": 59741
|
||||
},
|
||||
{
|
||||
"icon_id": "34819655",
|
||||
"name": "已关注icon",
|
||||
"font_class": "yiguanzhuicon",
|
||||
"unicode": "e95e",
|
||||
"unicode_decimal": 59742
|
||||
},
|
||||
{
|
||||
"icon_id": "34819656",
|
||||
"name": "已复刻_icon",
|
||||
"font_class": "yifuke_icon",
|
||||
"unicode": "e95f",
|
||||
"unicode_decimal": 59743
|
||||
},
|
||||
{
|
||||
"icon_id": "34820092",
|
||||
"name": "形状",
|
||||
"font_class": "xingzhuang",
|
||||
"unicode": "e960",
|
||||
"unicode_decimal": 59744
|
||||
},
|
||||
{
|
||||
"icon_id": "34197190",
|
||||
"name": "开启",
|
||||
"font_class": "kaiqi",
|
||||
"unicode": "e95a",
|
||||
"unicode_decimal": 59738
|
||||
},
|
||||
{
|
||||
"icon_id": "33949760",
|
||||
"name": "3.1 时间",
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -68,11 +68,11 @@
|
|||
window.onload=function(){
|
||||
$(".newContainer").delegate("a.anchors","click",function(){
|
||||
let h = $(this).offset().top - 180;
|
||||
console.log($(this).offset().top,document.body.scrollTop);
|
||||
$("html,body").animate({scrollTop:h},10);
|
||||
window.location.hash = $(this).attr("name");
|
||||
return false;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
|
||||
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!--
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ body {
|
|||
|
||||
/* 图片点击放大的场景,隐藏图片链接 */
|
||||
.editormd-image-click-expand .editormd-image-dialog {
|
||||
height: 234px !important;
|
||||
height: 244px !important;
|
||||
}
|
||||
|
||||
.editormd-image-click-expand .editormd-image-dialog .image-link {
|
||||
|
|
|
|||
40
src/App.js
40
src/App.js
|
|
@ -87,6 +87,7 @@ const Search = Loadable({
|
|||
loader: () => import('./modules/search/'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const WikiPreview = Loadable({
|
||||
loader: () => import('./forge/Wiki/Preview'),
|
||||
loading: Loading,
|
||||
|
|
@ -126,17 +127,31 @@ const Glcc = Loadable({
|
|||
loader : () => import("./glcc"),
|
||||
loading: Loading,
|
||||
})
|
||||
const ConfigurateBot = Loadable({
|
||||
loader: () => import("./forge/SecuritySetting/bot/exploit/configuration"),
|
||||
loading: Loading,
|
||||
});
|
||||
const SoftBot = Loadable({
|
||||
loader : () => import("./softbot"),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const WebIDE = Loadable({
|
||||
loader: () => import('./forge/Newfile/webIDE'),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
const Badge = Loadable({
|
||||
loader: () => import('./forge/Badge/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
// const CreateMerge = Loadable({
|
||||
// loader: () => import('./forge/Merge/NewMerge'),
|
||||
// loading: Loading,
|
||||
// })
|
||||
|
||||
// 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。
|
||||
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin"];
|
||||
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot"];
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
|
|
@ -319,6 +334,13 @@ class App extends Component {
|
|||
}
|
||||
} />
|
||||
|
||||
{/* svg */}
|
||||
<Route path="/:owner/:projectsId/badge/:type" render={
|
||||
(props) => {
|
||||
return (<Badge {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
} />
|
||||
|
||||
{/* webIDE */}
|
||||
<Route path="/:owner/:projectsId/webIDE/tree/:branchName"
|
||||
render={
|
||||
|
|
@ -342,6 +364,13 @@ class App extends Component {
|
|||
}
|
||||
}>
|
||||
</Route>
|
||||
{/* 上架bot配置 */}
|
||||
<Route
|
||||
path="/settings/mybot/configuration/:id"
|
||||
render={(p) => (
|
||||
<ConfigurateBot {...this.props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path={"/settings"}
|
||||
render={
|
||||
|
|
@ -350,7 +379,6 @@ class App extends Component {
|
|||
}
|
||||
}>
|
||||
</Route>
|
||||
|
||||
{/*403*/}
|
||||
<Route path="/403" component={Shixunauthority} />
|
||||
|
||||
|
|
@ -418,6 +446,14 @@ class App extends Component {
|
|||
render={(props) => <Glcc {...this.props} {...props} />}
|
||||
></Route>
|
||||
|
||||
{/* soft市场页 */}
|
||||
<Route
|
||||
path="/softbot"
|
||||
render={(props) => (
|
||||
<SoftBot {...this.props} {...props} />
|
||||
)}
|
||||
></Route>
|
||||
|
||||
{/* 组织 */}
|
||||
<Route path={"/organize"}
|
||||
render={
|
||||
|
|
|
|||
|
|
@ -82,6 +82,13 @@ export function initAxiosInterceptors(props) {
|
|||
}
|
||||
throw new axios.Cancel('Operation canceled by the user.');
|
||||
}
|
||||
if (response.data.status === 403 || response.data.status === "403" || response.data.status === 404) {
|
||||
if(!window.location.pathname.includes('/releases')){
|
||||
locationurl(`/${response.data.status}`);
|
||||
}else{
|
||||
console.log("releases 404了");
|
||||
}
|
||||
}
|
||||
|
||||
if (response.data.status === 403 || response.data.status === "403") {
|
||||
// locationurl('/403');
|
||||
|
|
|
|||
|
|
@ -68,13 +68,14 @@ function Index(props){
|
|||
return(
|
||||
<div className="aboutPanels">
|
||||
<div className="aboutContent">
|
||||
<AlignCenterBetween style={{padding:"14px 20px"}}>
|
||||
<span className="font-16"><i className="iconfont icon-xiangmujianjie mr5 font-16 color-blue"></i>项目概览</span>
|
||||
{ editOpration && !edit && <a onClick={editContent} className="color-blue">编辑</a> }
|
||||
</AlignCenterBetween>
|
||||
<div className="aboutTitle">
|
||||
<span className="font-16">项目概览</span>
|
||||
{ editOpration && !edit && <a onClick={editContent} className="color-blue">
|
||||
<i className="iconfont icon-a-bianji12 mr5 font-13 color-blue"></i>编辑</a> }
|
||||
</div>
|
||||
{
|
||||
edit ?
|
||||
<div className="padding20">
|
||||
<div className="padding20 boies">
|
||||
<MDEditor
|
||||
placeholder={"请输入描述信息"}
|
||||
height={500}
|
||||
|
|
@ -113,7 +114,7 @@ function Index(props){
|
|||
</div>
|
||||
</div>
|
||||
:
|
||||
<div className="padding20">
|
||||
<div className="padding20 boies">
|
||||
{content ?
|
||||
<RenderHtml className="break_word_comments imageLayerParent" value={content} url={props.history.location}/>
|
||||
:
|
||||
|
|
|
|||
|
|
@ -3,10 +3,25 @@
|
|||
margin:0px auto;
|
||||
.aboutContent{
|
||||
border-radius: 2px;
|
||||
border: 1px solid #EEEEEE;
|
||||
// border: 1px solid #EEEEEE;
|
||||
width:100%;
|
||||
background-color: #fff;
|
||||
margin-top:20px;
|
||||
margin-bottom: 30px;
|
||||
.aboutTitle{
|
||||
height: 50px;
|
||||
background-color: #fafcff;
|
||||
border: 1px solid;
|
||||
border-color: rgba(42, 97, 255, 0.23);
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding:0px 20px;
|
||||
}
|
||||
.boies{
|
||||
border: 1px solid #d0d0d0;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import IssueLine from './issueLine';
|
|||
import BranchLine from './branchLine';
|
||||
import SmoothLine from './smoothLine';
|
||||
import SmoothLineTwo from './smoothLineTwo';
|
||||
import Demo from './demo';
|
||||
import ProjectScale from './ProjectScale';
|
||||
import Pie from './pie';
|
||||
|
||||
|
|
@ -185,74 +186,76 @@ class Activity extends Component{
|
|||
<div className="contentBox mt20">
|
||||
<div className="normalBox">
|
||||
<div class="normalBox-title">概览</div>
|
||||
<div className="orderInfo">
|
||||
<div>
|
||||
<div className="percentLine prPercent">
|
||||
<p className="percent_purple" style={{width:first_per}}></p>
|
||||
<p className="percent_green resetStyle" style={{width:`${second_per}`}}></p>
|
||||
<div className="boxpart">
|
||||
<div className="orderInfo">
|
||||
<div>
|
||||
<div className="percentLine prPercent">
|
||||
<p className="percent_purple" style={{width:first_per}}></p>
|
||||
<p className="percent_green resetStyle" style={{width:`${second_per}`}}></p>
|
||||
</div>
|
||||
<span>{data && data.pr_all_count}合并请求</span>
|
||||
</div>
|
||||
<span>{data && data.pr_all_count}合并请求</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="percentLine">
|
||||
<p className="percent_red" style={{width:`${third_per}`}}></p>
|
||||
<p className="percent_green" style={{width:`${fourth_per}`}}></p>
|
||||
<div>
|
||||
<div className="percentLine">
|
||||
<p className="percent_red" style={{width:`${third_per}`}}></p>
|
||||
<p className="percent_green" style={{width:`${fourth_per}`}}></p>
|
||||
</div>
|
||||
<span>{data && data.issues_count}疑修</span>
|
||||
</div>
|
||||
<span>{data && data.issues_count}疑修</span>
|
||||
</div>
|
||||
<ul className="percentBox">
|
||||
<li>
|
||||
<span className="purple">{data && data.pr_count}</span>
|
||||
<span className={type==="PullRequest" && status==="delay" ?`change active`:"change"} onClick={()=>this.changeTrends("PullRequest","delay")}>已处理的合并请求</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="green">{data && data.new_pr_count}</span>
|
||||
<span className={type==="PullRequest"&& status==="not_delay" ?`change active`:"change"} onClick={()=>this.changeTrends("PullRequest","not_delay")}>未处理的合并请求</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="red">{data && data.close_issues_count}</span>
|
||||
<span className={type==="Issue"&& status==="delay" ?`change active`:"change"} onClick={()=>this.changeTrends("Issue","delay")}>已关闭的疑修</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="green">{data && data.open_issues_count}</span>
|
||||
<span className={type==="Issue"&& status==="not_delay" ?`change active`:"change"} onClick={()=>this.changeTrends("Issue","not_delay")}>未处理的疑修</span>
|
||||
</li>
|
||||
</ul>
|
||||
{
|
||||
codeStatus &&
|
||||
<div className="prMsg">
|
||||
<div><span className="fontbold">{codeStatus.author_count}位作者</span> 已经推送 <span className="fontbold">{codeStatus.commit_count}次提交</span> 到
|
||||
{codeStatus.commit_count_in_all_branches && codeStatus.commit_count_in_all_branches>0?<span> {projectDetail && projectDetail.default_branch}分支 和 <span>{codeStatus.commit_count_in_all_branches}次提交</span> 到所有分支</span>:""}。 在 {projectDetail && projectDetail.default_branch}分支上,
|
||||
{codeStatus.change_files && codeStatus.change_files >0 ? <span><span className="fontbold">{codeStatus.change_files} 个文件</span> 已经改变 而且</span>:""}
|
||||
{codeStatus.additions && codeStatus.additions >0 ?<span><span className="fontbold greencount">新增 {codeStatus.additions} 行代码</span></span>:""}
|
||||
{(codeStatus.additions && codeStatus.additions >0) &&(codeStatus.deletions && codeStatus.deletions >0) ?<span> 和</span>:""}
|
||||
{codeStatus.deletions && codeStatus.deletions >0 ?<span><span className="fontbold redcount"> 删除 {codeStatus.deletions} 行代码</span></span>:""}.</div>
|
||||
{/* {
|
||||
codeStatus.authors && codeStatus.authors.length > 0 &&
|
||||
<ul className="prAuthor">
|
||||
{
|
||||
codeStatus.authors.map((i,v)=>{
|
||||
return(
|
||||
i.author ?
|
||||
!i.author.id ?
|
||||
<span>
|
||||
<img src={getImageUrl(i.author && i.author.image_url)} width="38px" height="38px" alt=""/>
|
||||
<Tooltip title={i.author && i.author.name} placement="bottom"><span>{i.author && i.author.name}</span></Tooltip>
|
||||
</span>
|
||||
:
|
||||
<a href={i.author.login ? `/${i.author.login}` : `mailto:${i.author.email}`}>
|
||||
<img src={getImageUrl(i.author && i.author.image_url)} width="38px" height="38px" alt=""/>
|
||||
<Tooltip title={i.author && i.author.name} placement="bottom"><span>{i.author && i.author.name}</span></Tooltip>
|
||||
</a>
|
||||
:""
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
} */}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<ul className="percentBox">
|
||||
<li>
|
||||
<span className="purple">{data && data.pr_count}</span>
|
||||
<span className={type==="PullRequest" && status==="delay" ?`change active`:"change"} onClick={()=>this.changeTrends("PullRequest","delay")}>已处理的合并请求</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="green">{data && data.new_pr_count}</span>
|
||||
<span className={type==="PullRequest"&& status==="not_delay" ?`change active`:"change"} onClick={()=>this.changeTrends("PullRequest","not_delay")}>未处理的合并请求</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="red">{data && data.close_issues_count}</span>
|
||||
<span className={type==="Issue"&& status==="delay" ?`change active`:"change"} onClick={()=>this.changeTrends("Issue","delay")}>已关闭的疑修</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="green">{data && data.open_issues_count}</span>
|
||||
<span className={type==="Issue"&& status==="not_delay" ?`change active`:"change"} onClick={()=>this.changeTrends("Issue","not_delay")}>未处理的疑修</span>
|
||||
</li>
|
||||
</ul>
|
||||
{
|
||||
codeStatus &&
|
||||
<div className="prMsg">
|
||||
<div><span className="fontbold">{codeStatus.author_count}位作者</span> 已经推送 <span className="fontbold">{codeStatus.commit_count}次提交</span> 到
|
||||
{codeStatus.commit_count_in_all_branches && codeStatus.commit_count_in_all_branches>0?<span> {projectDetail && projectDetail.default_branch}分支 和 <span>{codeStatus.commit_count_in_all_branches}次提交</span> 到所有分支</span>:""}。 在 {projectDetail && projectDetail.default_branch}分支上,
|
||||
{codeStatus.change_files && codeStatus.change_files >0 ? <span><span className="fontbold">{codeStatus.change_files} 个文件</span> 已经改变 而且</span>:""}
|
||||
{codeStatus.additions && codeStatus.additions >0 ?<span><span className="fontbold greencount">新增 {codeStatus.additions} 行代码</span></span>:""}
|
||||
{(codeStatus.additions && codeStatus.additions >0) &&(codeStatus.deletions && codeStatus.deletions >0) ?<span> 和</span>:""}
|
||||
{codeStatus.deletions && codeStatus.deletions >0 ?<span><span className="fontbold redcount"> 删除 {codeStatus.deletions} 行代码</span></span>:""}.</div>
|
||||
{/* {
|
||||
codeStatus.authors && codeStatus.authors.length > 0 &&
|
||||
<ul className="prAuthor">
|
||||
{
|
||||
codeStatus.authors.map((i,v)=>{
|
||||
return(
|
||||
i.author ?
|
||||
!i.author.id ?
|
||||
<span>
|
||||
<img src={getImageUrl(i.author && i.author.image_url)} width="38px" height="38px" alt=""/>
|
||||
<Tooltip title={i.author && i.author.name} placement="bottom"><span>{i.author && i.author.name}</span></Tooltip>
|
||||
</span>
|
||||
:
|
||||
<a href={i.author.login ? `/${i.author.login}` : `mailto:${i.author.email}`}>
|
||||
<img src={getImageUrl(i.author && i.author.image_url)} width="38px" height="38px" alt=""/>
|
||||
<Tooltip title={i.author && i.author.name} placement="bottom"><span>{i.author && i.author.name}</span></Tooltip>
|
||||
</a>
|
||||
:""
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
} */}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
{
|
||||
ai_shang_v1_url &&
|
||||
|
|
@ -272,12 +275,13 @@ class Activity extends Component{
|
|||
<SmoothLineTwo url={ai_shang_v4_url}/>
|
||||
<p>基于信息熵围绕项目社群的社区分裂、缩减、合并和扩大演化行为度量</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div className="commentsBox">
|
||||
<div className="trendsTop">
|
||||
<span>疑修/合并请求记录</span>
|
||||
<span className="font-16">疑修/合并请求记录</span>
|
||||
<div className="branchDropdown f-wrap-alignCenter">
|
||||
<Dropdown overlay={this.menu()} trigger={['click']} placement="bottomLeft">
|
||||
<a className="ant-dropdown-link">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
.trendsTop{
|
||||
padding:15px;
|
||||
border-bottom:1px solid #ddd;
|
||||
background-color: #f4f4f4;
|
||||
background-color: #fafcff;
|
||||
border: 1px solid;
|
||||
border-color: rgba(42, 97, 255, 0.23);
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
padding: 10px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
|
@ -29,6 +31,10 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.boxpart{
|
||||
border: 1px solid #d0d0d0;
|
||||
border-top: none;
|
||||
}
|
||||
.orderInfo > div{
|
||||
width: 45%;
|
||||
}
|
||||
|
|
@ -95,7 +101,7 @@
|
|||
}
|
||||
.commentsBox{
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
/* border: 1px solid #ddd; */
|
||||
margin: 20px 0px 20px 0px;
|
||||
}
|
||||
.prMsg{
|
||||
|
|
@ -140,6 +146,10 @@
|
|||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.activity_list{
|
||||
border:1px solid #d0d0d0;
|
||||
border-top: none;
|
||||
}
|
||||
.activity_list .activity_item{
|
||||
border-bottom: 1px solid #f4f4f4;
|
||||
padding:15px;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,165 @@
|
|||
import React ,{ useEffect } from 'react';
|
||||
// import echarts from 'echarts/lib/echarts';
|
||||
import 'echarts/lib/chart/line';
|
||||
import * as echarts from 'echarts';
|
||||
import axios from 'axios';
|
||||
import { getdata } from './data'
|
||||
|
||||
function Demo({url}) {
|
||||
|
||||
useEffect(()=>{
|
||||
Init();
|
||||
},[])
|
||||
|
||||
function Init() {
|
||||
let data = getdata();
|
||||
console.log(data);
|
||||
var huan_val = document.getElementById("Demo");
|
||||
var myEcharts = echarts.init(huan_val);
|
||||
let date = [];
|
||||
let array = [0];
|
||||
let array1 = [0];
|
||||
let array2 = [0];
|
||||
let option = {
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
data:date,
|
||||
boundaryGap: false,
|
||||
axisLabel:{
|
||||
month:`{M}月`
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (params) {
|
||||
params = params[0];
|
||||
var date = new Date(params.name);
|
||||
return (
|
||||
date.getDate() +
|
||||
'/' +
|
||||
(date.getMonth() + 1) +
|
||||
'/' +
|
||||
date.getFullYear() +
|
||||
' : ' +
|
||||
params.value
|
||||
);
|
||||
},
|
||||
axisPointer: {
|
||||
animation: false
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '1%',
|
||||
top: '8%',
|
||||
},
|
||||
legend: {
|
||||
data: ['issue', 'fork' ,'commit']
|
||||
},
|
||||
color: ['red','#36bba6',"skyblue",'#a491d7','#e840f7',"#36bba6","#7b32b2"],
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true,
|
||||
boundaryGap: [0, '100%'],
|
||||
name: '',
|
||||
min:0,
|
||||
splitLine : {
|
||||
show : true,
|
||||
},
|
||||
nameLocation:'end',
|
||||
nameTextStyle:{
|
||||
fontSize:14,
|
||||
padding:[0,70,-80,0]
|
||||
}
|
||||
},series:[
|
||||
{
|
||||
data: array,
|
||||
type: 'line',
|
||||
name:"issue",
|
||||
smooth: true
|
||||
},
|
||||
{
|
||||
data: array1,
|
||||
type: 'line',
|
||||
name:"fork",
|
||||
smooth: true
|
||||
},
|
||||
{
|
||||
data: array2,
|
||||
type: 'line',
|
||||
name:"commit",
|
||||
smooth: true
|
||||
},
|
||||
]
|
||||
};
|
||||
myEcharts.setOption(option);
|
||||
|
||||
setTimeout(()=>{
|
||||
let count = data.length>15?15:data.length;
|
||||
for(var i = 0;i<count;i++){
|
||||
date.push(data[count][0]);
|
||||
array.push(data[count][1]);
|
||||
array1.push(data[count][2]);
|
||||
array2.push(data[count][3]);
|
||||
}
|
||||
let interval = setInterval(function () {
|
||||
count ++ ;
|
||||
if(count < data.length ){
|
||||
setValue(count)
|
||||
}else{
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 2000);
|
||||
},1000);
|
||||
|
||||
function setValue(count){
|
||||
for (var i = 0; i < 2; i++) {
|
||||
array.shift();
|
||||
array1.shift();
|
||||
array2.shift();
|
||||
date.shift();
|
||||
date.push(data[count][0]);
|
||||
array.push(data[count][1]);
|
||||
array1.push(data[count][2]);
|
||||
array2.push(data[count][3]);
|
||||
}
|
||||
myEcharts.setOption({
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data:date
|
||||
},
|
||||
legend: {
|
||||
data: ['issue', 'fork' ,'commit']
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: array,
|
||||
type: 'line',
|
||||
name:"issue",
|
||||
showSymbol: false,
|
||||
smooth: true
|
||||
},
|
||||
{
|
||||
data: array1,
|
||||
type: 'line',
|
||||
name:"fork",
|
||||
showSymbol: false,
|
||||
smooth: true
|
||||
},
|
||||
{
|
||||
data: array2,
|
||||
type: 'line',
|
||||
name:"commit",
|
||||
showSymbol: false,
|
||||
smooth: true
|
||||
},
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<div id="Demo" style={{height:"400px"}}></div>
|
||||
)
|
||||
}
|
||||
export default Demo;
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import axios from 'axios';
|
||||
import { setSeoMeta } from 'educoder';
|
||||
|
||||
function Badge(props) {
|
||||
const { match, history } = props;
|
||||
const { projectsId, owner, type } = match.params;
|
||||
|
||||
if (!["watch.svg", "star.svg", "fork.svg"].includes(type)) {
|
||||
history.push('/nopage');
|
||||
}
|
||||
|
||||
// 可以注释请求后,使用mock数据测试不同数值的场景
|
||||
// {
|
||||
// watchers_count: 10,
|
||||
// praises_count: 0,
|
||||
// forked_count: 1200,
|
||||
// }
|
||||
const [projectDetail, setProjectDetail] = useState();
|
||||
const [text, setText] = useState("");
|
||||
|
||||
// 查询项目详情,获取三种数值
|
||||
useEffect(() => {
|
||||
const url = `/${owner}/${projectsId}/detail.json`;
|
||||
axios.get(url).then((result) => {
|
||||
if (result && result.data) {
|
||||
if (result.data.status === 404) {
|
||||
this.props.history.push('/nopage');
|
||||
} else {
|
||||
setProjectDetail(result.data);
|
||||
// seo优化设置
|
||||
let keyWords = `${owner},${projectsId},${result.data.author.name},`;
|
||||
let title = `${owner}/${projectsId}${result.data.description ? ':' + result.data.description : ''}`;
|
||||
setSeoMeta(keyWords, title, result.data.description, `/${owner}/${projectsId}`, owner, projectsId);
|
||||
}
|
||||
}
|
||||
}).catch((error) => { })
|
||||
}, [projectsId, owner]);
|
||||
|
||||
useEffect(() => {
|
||||
if (projectDetail) {
|
||||
if (type === "watch.svg") {
|
||||
setText(`${coutTranslate(projectDetail.watchers_count)} 关注数`);
|
||||
} else if (type === "star.svg") {
|
||||
setText(`${coutTranslate(projectDetail.praises_count)} 点赞数`);
|
||||
} else if (type === "fork.svg") {
|
||||
setText(`${coutTranslate(projectDetail.forked_count)} Fork数`);
|
||||
}
|
||||
}
|
||||
}, [type, projectDetail]);
|
||||
|
||||
function coutTranslate(num) {
|
||||
if (num < 1000) {
|
||||
return num;
|
||||
} else if (num >= 1000 & num < 10000) {
|
||||
return (num / 1000).toFixed(1) + 'k';
|
||||
} else {
|
||||
return (num / 1000).toFixed(0) + 'k';
|
||||
}
|
||||
}
|
||||
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="148" height="44" viewBox="0 0 148 44">
|
||||
<defs>
|
||||
<filter id="矩形_234" x="0" y="0" width="148" height="44" filterUnits="userSpaceOnUse">
|
||||
<feOffset input="SourceAlpha" />
|
||||
<feGaussianBlur stdDeviation="3" result="blur" />
|
||||
<feFlood floodColor="#0c1a75" floodOpacity="0.161" />
|
||||
<feComposite operator="in" in2="blur" />
|
||||
<feComposite in="SourceGraphic" />
|
||||
</filter>
|
||||
<filter id="矩形_235" x="0" y="0" width="148" height="44" filterUnits="userSpaceOnUse">
|
||||
<feOffset input="SourceAlpha" />
|
||||
<feGaussianBlur stdDeviation="3" result="blur-2" />
|
||||
<feFlood floodColor="#445a64" floodOpacity="0.161" />
|
||||
<feComposite operator="in" in2="blur-2" />
|
||||
<feComposite in="SourceGraphic" />
|
||||
</filter>
|
||||
<linearGradient id="linear-gradient" x1="0.494" y1="-0.055" x2="1.494" y2="-0.055" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0" stopColor="#6dffff" />
|
||||
<stop offset="1" stopColor="#0080ff" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="组_1758" data-name="组 1758" transform="translate(-1468 -507)">
|
||||
<g transform="matrix(1, 0, 0, 1, 1468, 507)" filter="url(#矩形_234)">
|
||||
<path id="矩形_234-2" data-name="矩形 234" d="M0,0H126a4,4,0,0,1,4,4V22a4,4,0,0,1-4,4H0a0,0,0,0,1,0,0V0A0,0,0,0,1,0,0Z" transform="translate(9 9)" fill="#fff" />
|
||||
</g>
|
||||
<g transform="matrix(1, 0, 0, 1, 1468, 507)" filter="url(#矩形_235)">
|
||||
<path id="矩形_235-2" data-name="矩形 235" d="M4,0H42a0,0,0,0,1,0,0V26a0,0,0,0,1,0,0H4a4,4,0,0,1-4-4V4A4,4,0,0,1,4,0Z" transform="translate(9 9)" fill="#2b2b2b" />
|
||||
<text xmlns="http://www.w3.org/2000/svg" fill="#000"
|
||||
// svg文字的长度根据文字长短来决定,fork的额外减少1.5的系数;如果上千,额外减少0.5的系数
|
||||
textLength={80 - 10 * (7.5 - (text.indexOf("Fork") > -1 ? (text.length - 1.5) : text.length) + (text.includes('k ') && 0.5))}
|
||||
x={64 + 6 * (7.5 - (text.indexOf("Fork") > -1 ? (text.length - 1.5) : text.length) + (text.includes('k ') && 0.5))}
|
||||
y="30" lengthAdjust="spacing" transform="scale(.9)" weight="bold">
|
||||
{text}
|
||||
</text>
|
||||
</g>
|
||||
<g id="图层_2" data-name="图层 2" transform="translate(1484 520)">
|
||||
<g id="图层_1" data-name="图层 1">
|
||||
<path id="路径_3357" data-name="路径 3357" d="M20.6,5.477l-.75-1.023-.658.329v8.211l.546.288v-.911l.862,1.395.7.109V3.9l-.7.27Zm-15.462,8.4,1.559-.78V4.676L5.139,3.9Zm10.7,1.063,1.4.836V1.559l-1.4.836ZM13.827,0H11.878V17.764h2.984V16.008H13.827ZM7.51,3.683l.771.285V15.756l1.41-.729V4.578l.755.271V3.258L7.51,1.559ZM0,13.271l3.39-1.717V8.32L1.78,8.155V9.292h.645v1.1l-1.109.5V7.4L3.4,7.779V6.451L0,5.329ZM25.1,9.257l2.5-3.928-1.864.2L24.5,7.177V6.086l-1.37.329V12.32l1.37.286V11.475l1.329,1.684,2.138.107Z" fillRule="evenodd" fill="url(#linear-gradient)" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default Badge;
|
||||
|
|
@ -166,7 +166,7 @@ function Contributors({owner,projectsId,currentLogin}){
|
|||
return(
|
||||
total > 0 ?
|
||||
<div className="halfs">
|
||||
<Link to={`/${owner}/${projectsId}/contribute`} className="font-16 color-ooo hoverA">
|
||||
<Link to={`/${owner}/${projectsId}/contribute`} onClick={()=>{window.scrollTo(0,0)}} className="font-16 color-ooo hoverA">
|
||||
<span>贡献者</span>
|
||||
{ total > 0 && <span className="infoCount">{total}</span>}
|
||||
</Link>
|
||||
|
|
@ -174,11 +174,16 @@ function Contributors({owner,projectsId,currentLogin}){
|
|||
{
|
||||
list && list.length>0 && list.map((item,key)=>{
|
||||
return(
|
||||
item.login ?
|
||||
<Popover content={menu} visible={item.visible} overlayClassName="menuPanels" placement="top">
|
||||
<Link key={key} to={`/${item.login}`}>
|
||||
<a key={key} href={`/${item.login}`}>
|
||||
<img src={getImageUrl(`/${item.image_url}`)} alt="" onMouseOver={()=>setVisibleFunc(true,item.login,key)}/>
|
||||
</Link>
|
||||
</a>
|
||||
</Popover>
|
||||
:
|
||||
<a key={key} href={`mailto:${item.email}`}>
|
||||
<img src={getImageUrl(`/${item.image_url}`)} alt="" onMouseOver={()=>setVisibleFunc(true,item.login,key)}/>
|
||||
</a>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ function CopyTool({ beforeText, afterText, className , inputId , timeOut }) {
|
|||
title={title}
|
||||
onVisibleChange={() => { setTitle(beforeText) }}
|
||||
>
|
||||
<i className={`iconfont icon-fuzhiicon ${className}`} style={{ color: '#466aff' }} onClick={copyUrl}></i>
|
||||
<i className={`iconfont icon-fuzhiicon cursor ${className}`} style={{ color: '#466aff' }} onClick={copyUrl}></i>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
import React , { useState , useEffect } from 'react';
|
||||
import { Input } from 'antd';
|
||||
import './index.scss';
|
||||
|
||||
function FileName({
|
||||
onChangeFile,addonBefore , name , empty , onEmpty
|
||||
}){
|
||||
const [ defaultUrl , setDefaultUrl ] = useState(undefined);
|
||||
const [ newUrl , setNewUrl ] = useState([]);
|
||||
const [ value , setValue ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(name){
|
||||
let v ="";
|
||||
if(name.indexOf("/")>-1){
|
||||
let s = name.split("/");
|
||||
v = s[s.length-1];
|
||||
console.log(v);
|
||||
setValue(v);
|
||||
}else{
|
||||
v=name;
|
||||
setValue(name);
|
||||
}
|
||||
let a = newUrl && newUrl.length > 0 ? newUrl.join("/")+"/" +v : v ;
|
||||
onChangeFile(a);
|
||||
}else{
|
||||
setValue(undefined);
|
||||
}
|
||||
},[name])
|
||||
|
||||
useEffect(()=>{
|
||||
if(addonBefore){
|
||||
setDefaultUrl(addonBefore.split('/'));
|
||||
}
|
||||
},[addonBefore])
|
||||
|
||||
function keyUpFun(e){
|
||||
const key = e.keyCode;
|
||||
if(key === 8){
|
||||
//当input框无数据、新加过文件夹、点击删除键时
|
||||
if(value === undefined && (newUrl && newUrl.length>0)){
|
||||
const last = newUrl[newUrl.length-1];
|
||||
let n = newUrl;
|
||||
n.splice(n.length-1,1);
|
||||
setNewUrl(n);
|
||||
setValue(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changeValue(e){
|
||||
const value = e.target.value;
|
||||
console.log(value);
|
||||
if(value){
|
||||
if(value === "/"){
|
||||
setValue(undefined);
|
||||
}
|
||||
else if(value.indexOf("/")>-1){
|
||||
let a = value.split("/");
|
||||
let b = newUrl;
|
||||
let c = b.concat(a[0]);
|
||||
setNewUrl(c);
|
||||
let v = a[a.length-1];
|
||||
setValue(v);
|
||||
if(v){
|
||||
onChangeFile(c && c.length > 0?c.join("/")+"/"+v:v);
|
||||
onEmpty && onEmpty(false);
|
||||
}else{
|
||||
clearvalue();
|
||||
}
|
||||
}else{
|
||||
let a = newUrl && newUrl.length > 0 ? newUrl.join("/")+"/" +value : value ;
|
||||
onChangeFile(a);
|
||||
setValue(value);
|
||||
onEmpty && onEmpty(false);
|
||||
}
|
||||
}else{
|
||||
clearvalue();
|
||||
}
|
||||
}
|
||||
|
||||
function clearvalue(){
|
||||
if(newUrl && newUrl.length>0){
|
||||
setTimeout(()=>{setValue(undefined)},200);
|
||||
}else{
|
||||
setValue(undefined);
|
||||
}
|
||||
onEmpty && onEmpty(true);
|
||||
}
|
||||
|
||||
const renderList=(list)=>{
|
||||
return(
|
||||
<div>
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
i && <span>{i}<span className="ml2 mr3">/</span></span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return(
|
||||
<div className="fileName">
|
||||
{
|
||||
defaultUrl && defaultUrl.length>0 ? renderList(defaultUrl) : ""
|
||||
}
|
||||
{
|
||||
newUrl && newUrl.length>0 ? renderList(newUrl) : ""
|
||||
}
|
||||
<Input
|
||||
placeholder="命名文件名称或文件路径"
|
||||
value={value}
|
||||
onKeyUp={keyUpFun}
|
||||
onChange={changeValue}
|
||||
style={{width:"220px",borderColor:empty===true ? "red":"grey"}}
|
||||
/>
|
||||
{empty && <p className="color-red ml20">请输入文件名称</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default FileName;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
.fileName{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
@ -31,10 +31,10 @@ export default (({fork,parise})=>{
|
|||
return(
|
||||
<DivStyle>
|
||||
{
|
||||
fork || fork ===0 ? <SpanStylefork><i className="iconfont icon-fork font-15 mr3" style={{color:"#1B8FFF"}}></i>fork({fork})</SpanStylefork>:""
|
||||
fork || fork ===0 ? <SpanStylefork><i className="iconfont icon-morenfuke_icon1 font-15 mr3" style={{color:"#1B8FFF"}}></i>fork({fork})</SpanStylefork>:""
|
||||
}
|
||||
{
|
||||
parise || parise ===0?<SpanStyleparise><i className="iconfont icon-guanzhu font-14 mr3" style={{color:"#FFA802"}}></i>点赞({parise})</SpanStyleparise>:""
|
||||
parise || parise ===0?<SpanStyleparise><i className="iconfont icon-xingzhuang font-14 mr3" style={{color:"#FFA802"}}></i>点赞({parise})</SpanStyleparise>:""
|
||||
}
|
||||
</DivStyle>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Link } from 'react-router-dom';
|
|||
function Releases({ owner, projectsId, releaseVersions, distribution }) {
|
||||
return(
|
||||
<div>
|
||||
<Link to={`/${owner}/${projectsId}/releases`} className="font-16 color-ooo hoverA">
|
||||
<Link to={`/${owner}/${projectsId}/releases`} onClick={()=>{window.scrollTo(0,0)}} className="font-16 color-ooo hoverA">
|
||||
<span>发行版</span>
|
||||
{ releaseVersions && releaseVersions.total_count > 0 && <span className="infoCount">{releaseVersions.total_count}</span>}
|
||||
</Link>
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
|
|
@ -44,7 +44,6 @@ function AllMenus({owner,projectsId,chooseFunc,update,defaultNames,defaultIds,op
|
|||
|
||||
useEffect(()=>{
|
||||
if(defaultIds){
|
||||
console.log("defaule:",defaultIds);
|
||||
setIds(defaultIds);
|
||||
}
|
||||
},[defaultIds])
|
||||
|
|
@ -206,6 +205,7 @@ function AllMenus({owner,projectsId,chooseFunc,update,defaultNames,defaultIds,op
|
|||
chooseFunc={(id,name)=>{let copy = {...ids,status_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,status_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}}
|
||||
/>
|
||||
{ !update && <Menus
|
||||
className="minwidth"
|
||||
ids={ids && ids.sort_by}
|
||||
name={"排序"} size={"small"}
|
||||
lists={open_blockchain ? array : array1}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
import { Input, Button, message } from 'antd';
|
||||
import { getImageUrl, timeAgo } from 'educoder';
|
||||
import { Button, message } from 'antd';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import MDEditor from "../../../../modules/tpm/challengesnew/tpm-md-editor";
|
||||
import Upload from "../../../../forge/Upload/Index";
|
||||
import Attachments from "../../../../forge/Upload/attachment";
|
||||
import UploadImg from "../../../../forge/Images/upload.png";
|
||||
import './list.scss';
|
||||
import '../../../../forge/Order/order.scss';
|
||||
|
|
@ -20,6 +21,11 @@ function EditComment(props){
|
|||
const [atWhoLoginList, setAtWhoLoginList] = useState(undefined);
|
||||
const [fileList, setFileList] = useState(undefined);
|
||||
const [attachmentClean, setAttachmentClean] = useState(true);
|
||||
const [ save , setSave ] = useState(undefined);
|
||||
|
||||
useState(()=>{
|
||||
setSave(props.defaultFileList);
|
||||
},[props.defaultFileList])
|
||||
|
||||
// 评论点击函数
|
||||
function addJournals(){
|
||||
|
|
@ -65,6 +71,24 @@ function EditComment(props){
|
|||
})
|
||||
}
|
||||
};
|
||||
function deleteLoad(id){
|
||||
let arr = [];
|
||||
let list = save;
|
||||
if(list && list.length>0 ){
|
||||
arr = list.filter(i=>i.id !== id);
|
||||
}
|
||||
setFileList(arr.map(i=>{return i.id}));
|
||||
setSave(arr);
|
||||
}
|
||||
function UploadFunc(f){
|
||||
let list = [];
|
||||
let arr = [];
|
||||
if(save && save.length>0 ){
|
||||
arr = save.map(i=>{return i.id});
|
||||
}
|
||||
list = arr && arr.length>0 ? f.concat(arr) : f ;
|
||||
setFileList(list);
|
||||
};
|
||||
|
||||
return(
|
||||
<div className="grid-item-top pb10">
|
||||
|
|
@ -101,7 +125,7 @@ function EditComment(props){
|
|||
<Upload
|
||||
className="commentStyle"
|
||||
isComplete={attachmentClean}
|
||||
load={(fileList)=>{setFileList(fileList)}}
|
||||
load={UploadFunc}
|
||||
icon={
|
||||
<img
|
||||
src={UploadImg}
|
||||
|
|
@ -112,8 +136,16 @@ function EditComment(props){
|
|||
}
|
||||
size={100}
|
||||
showNotification={showNotification}
|
||||
defaultFileList={props.defaultFileList}
|
||||
// defaultFileList={props.defaultFileList}
|
||||
/>
|
||||
{props.defaultFileList && props.defaultFileList.length > 0 &&
|
||||
<Attachments
|
||||
attachments={props.defaultFileList}
|
||||
showNotification={props.showNotification}
|
||||
canDelete={true}
|
||||
deleteLoad={deleteLoad}
|
||||
></Attachments>
|
||||
}
|
||||
<p className="clearfix mt20">
|
||||
<Button
|
||||
type="primary"
|
||||
|
|
|
|||
|
|
@ -110,27 +110,17 @@ function IssueCommentList(props){
|
|||
return(
|
||||
<div className="commentListBox">
|
||||
{/* 全部 / 评论 / 操作日志 */}
|
||||
<div className='typeActionBox mt30 mb20'>
|
||||
<Radio.Group onChange={(e)=>{setSpin(true);setPage(1);setCategory(e.target.value)}} value={category}>
|
||||
<Radio value={'comment'} className='typeActionRadio font-14'>评论<span className='journalsCount font-13 ml5'>{journalsCount}</span></Radio>
|
||||
<Radio value={'operate'} className='typeActionRadio font-14'>操作日志</Radio>
|
||||
<Radio value={'all'} className='typeActionRadio font-14'>全部</Radio>
|
||||
</Radio.Group>
|
||||
<div className={`${!login && journalsCount > 0 && 'pb15'}`}>
|
||||
<div className='typeActionBox mt30 mb20'>
|
||||
<Radio.Group onChange={(e)=>{setSpin(true);setPage(1);setCategory(e.target.value);history.push(history.pathname)}} value={category}>
|
||||
<Radio value={'comment'} className='typeActionRadio font-14'>评论<span className='journalsCount font-13 ml5'>{journalsCount}</span></Radio>
|
||||
<Radio value={'operate'} className='typeActionRadio font-14'>操作日志</Radio>
|
||||
<Radio value={'all'} className='typeActionRadio font-14'>全部</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
</div>
|
||||
{/* 添加评论 */}
|
||||
{category !== 'operate' && <div className="pt5 pb30">
|
||||
{login ? showEdit === 1 ? <EditComment {...props} cancelMd={cancelMd}/> : <div className="addComments">
|
||||
<img src={getImageUrl(image_url)} alt="" />
|
||||
<div style={{flex:1}}>
|
||||
<CheckProfile {...props} sureFunc={()=>{setShowEdit(1)}}>
|
||||
<Input className='addCommentBox' placeholder="添加评论"/>
|
||||
</CheckProfile>
|
||||
</div>
|
||||
</div> : <div className='unLoginComment font-15 pl20'>
|
||||
{/* 未登录用户 */}
|
||||
<a className='mr5 loginBtn' onClick={()=>{showLoginDialog()}}>登录</a>并参与评论与回复
|
||||
</div>}
|
||||
</div>}
|
||||
{/* 评论快速入口-仅有评论且登录时展示 */}
|
||||
{login && category !== 'operate' && journalsCount > 0 && <div className={`pb30 ${journalsCount > 0 && 'pt15'}`}><div className='gotoComment'>点击<a className='ml5' href='#addComments'>添加评论</a></div></div>}
|
||||
{/* 评论/操作日志 展示列表 */}
|
||||
<Spin spinning={spin}>
|
||||
<div className={`issueCommentsBox ${category === 'comment' && !spin && 'justComment'}`}>
|
||||
|
|
@ -200,7 +190,7 @@ function IssueCommentList(props){
|
|||
</div>
|
||||
{login && <div>
|
||||
{/* 平台管理员/仓库管理员/发布评论者/回复评论者/issue创建者 */}
|
||||
{(admin || isManager || login === i.user.login || login === i.reply_user.login || user_id === author.id) && <Popconfirm
|
||||
{(admin || isManager || login === i.user.login || (i.reply_user && login === i.reply_user.login) || user_id === author.id) && <Popconfirm
|
||||
placement="bottom"
|
||||
title={"确定要删除当前回复吗?"}
|
||||
okText="是"
|
||||
|
|
@ -232,8 +222,20 @@ function IssueCommentList(props){
|
|||
{totalCount>limit && <div className='mt20 paginationIssueComment mb20'>
|
||||
<Pagination simple current={page} pageSize={limit} total={totalCount} onChange={(page)=>setPage(page)}/>
|
||||
</div>}
|
||||
{/* 无数据 新建第一条评论时暂无数据隐藏 用户未登录隐藏*/}
|
||||
{journals && !journals.length && showEdit !== 1 && login && <Nodata _html="暂无数据"/>}
|
||||
{/* 添加评论 */}
|
||||
{category !== 'operate' && <div className="pb30" style={{marginTop: journalsCount ? '-5px' : '30px'}}>
|
||||
{login ? showEdit === 1 ? <EditComment {...props} cancelMd={cancelMd}/> : <div className="addComments" id='addComments'>
|
||||
<img src={getImageUrl(image_url)} alt="" />
|
||||
<div style={{flex:1}}>
|
||||
<CheckProfile {...props} sureFunc={()=>{setShowEdit(1)}}>
|
||||
<Input className='addCommentBox' placeholder="添加评论"/>
|
||||
</CheckProfile>
|
||||
</div>
|
||||
</div> : <div className='unLoginComment font-15 pl20'>
|
||||
{/* 未登录用户 */}
|
||||
<a className='mr5 loginBtn' onClick={()=>{showLoginDialog()}}>登录</a>并参与评论与回复
|
||||
</div>}
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@
|
|||
position: relative;
|
||||
top: -6px;
|
||||
padding: 10px 15px 16px;
|
||||
width: 0;
|
||||
&::before{
|
||||
content: '';
|
||||
display: block;
|
||||
|
|
@ -192,4 +193,12 @@
|
|||
}
|
||||
.paginationIssueComment{
|
||||
text-align: center;
|
||||
}
|
||||
.gotoComment{
|
||||
background-color:rgba(241, 243, 252, 0.55);
|
||||
border-radius:4px;
|
||||
color: rgba(172, 176, 191, 1);
|
||||
padding: 8px 20px;
|
||||
margin-top: -10px;
|
||||
&>a{color: rgba(70, 106, 255, 1);}
|
||||
}
|
||||
|
|
@ -58,7 +58,12 @@ function Datas({checkbox ,item , projectsId,owner}){
|
|||
<span className="mr12">{item.created_at} 发布</span>
|
||||
<span className="mr20">{item.updated_at}更新</span>
|
||||
{item.blockchain_token_num && <span className="mr30"><img src={gold} alt="" width="13px" className="mr3"/>{item.blockchain_token_num}</span>}
|
||||
{item.milestone_name && <span style={{maxWidth:item.blockchain_token_num ? "261px":"340px"}} title={item.milestone_name} className="task-hide"><i className="iconfont icon-lichengbeiicon1 font-12 mr3"></i>{item.milestone_name}</span> }
|
||||
{item.milestone_name &&
|
||||
<Link to={`/${owner}/${projectsId}/milestones/${item.milestone_id}`} onClick={()=>{window.scrollTo(0,0)}} style={{maxWidth:item.blockchain_token_num ? "261px":"340px",color:"#898d9d"}} title={item.milestone_name} className="task-hide">
|
||||
<i className="iconfont icon-lichengbeiicon1 font-12 mr3"></i>
|
||||
{item.milestone_name}
|
||||
</Link>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,19 +31,17 @@ function Drop({overlay , children , placement, overlayClassName},ref){
|
|||
}
|
||||
}
|
||||
return(
|
||||
<li>
|
||||
<Dropdown
|
||||
placement={placement}
|
||||
visible={visible}
|
||||
overlay={<div ref={refFa}>{overlay}</div>}
|
||||
trigger={['click']}
|
||||
overlayClassName={overlayClassName}
|
||||
>
|
||||
<span className="dropspan" ref={refBox} onClick={()=>setVisible(visible ? false : true)}>
|
||||
{children}
|
||||
</span>
|
||||
</Dropdown>
|
||||
</li>
|
||||
<Dropdown
|
||||
placement={placement}
|
||||
visible={visible}
|
||||
overlay={<div ref={refFa}>{overlay}</div>}
|
||||
trigger={['click']}
|
||||
overlayClassName={overlayClassName}
|
||||
>
|
||||
<span className="dropspan" ref={refBox} onClick={()=>setVisible(visible ? false : true)}>
|
||||
{children}
|
||||
</span>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
export default forwardRef(Drop);
|
||||
|
|
@ -5,6 +5,7 @@ import Drop from './drop';
|
|||
const { Search } = Input;
|
||||
|
||||
/**
|
||||
* @param {className} <li>的样式名称
|
||||
* @param {name} 未选择时显示的内容
|
||||
* @param {lists} 下拉列表,从接口获取,需一一填充
|
||||
* @param {imgControl} 控制是否显示头像
|
||||
|
|
@ -15,13 +16,13 @@ const { Search } = Input;
|
|||
* @param {update} 编辑状态
|
||||
* @param {double} 是否可以多选
|
||||
*/
|
||||
function Menus({name, ids , lists , size , imgControl , searchFunc , chooseFunc , update , double ,names },ref){
|
||||
function Menus({className,name, ids , lists , size , imgControl , searchFunc , chooseFunc , update , double ,names },ref){
|
||||
const dropRef = useRef(null);
|
||||
const [ chooseValue , setChooseValue ] = useState([]);
|
||||
const [ showValue , setShowValue ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
setChooseValue(ids);
|
||||
ids ? setChooseValue(ids.split(",")) : setChooseValue([]);
|
||||
setShowValue(names);
|
||||
},[ids,names])
|
||||
|
||||
|
|
@ -81,10 +82,12 @@ function Menus({name, ids , lists , size , imgControl , searchFunc , chooseFunc
|
|||
</div>
|
||||
}
|
||||
return(
|
||||
<Drop ref={dropRef} overlay={menu(lists)} placement={"bottomRight"} >
|
||||
<span className="task-hide">{showValue || (update ? `更换${name}` : name) }</span>
|
||||
<Icon type="caret-down" className="ml5 color-grey-6" />
|
||||
</Drop>
|
||||
<li className={className || ""}>
|
||||
<Drop className={className} ref={dropRef} overlay={menu(lists)} placement={"bottomRight"} >
|
||||
<span className="task-hide">{showValue || (update ? `更换${name}` : name) }</span>
|
||||
<Icon type="caret-down" className="color-grey-6" />
|
||||
</Drop>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
export default Menus;
|
||||
|
|
@ -9,6 +9,7 @@ function NewPanel(props,ref){
|
|||
const [ description , setDescription ] = useState("");
|
||||
const [ fileList , setFileList] = useState(undefined);
|
||||
const [ attachments , setAttachments ] = useState([]);
|
||||
const [ save , setSave ] = useState([]);
|
||||
const [ receivers_login , setReceiversLogin ] = useState(undefined);
|
||||
|
||||
const { createFunc , title , desc , files , onCancel , owner , projectsId } = props;
|
||||
|
|
@ -21,6 +22,7 @@ function NewPanel(props,ref){
|
|||
},100)
|
||||
desc && setDescription(desc);
|
||||
files && setAttachments(files);
|
||||
files && setSave(files);
|
||||
},[title , desc , files])
|
||||
|
||||
function onContentChange(value){
|
||||
|
|
@ -29,8 +31,8 @@ function NewPanel(props,ref){
|
|||
function UploadFunc(f){
|
||||
let list = [];
|
||||
let arr = [];
|
||||
if(files && files.length>0 ){
|
||||
arr = files.map(i=>{return i.id});
|
||||
if(save && save.length>0 ){
|
||||
arr = save.map(i=>{return i.id});
|
||||
}
|
||||
list = arr && arr.length>0 ? f.concat(arr) : f ;
|
||||
setFileList(list);
|
||||
|
|
@ -38,11 +40,12 @@ function NewPanel(props,ref){
|
|||
|
||||
function deleteLoad(id){
|
||||
let arr = [];
|
||||
let list = files
|
||||
let list = save;
|
||||
if(list && list.length>0 ){
|
||||
arr = list.filter(i=>i.id !== id);
|
||||
}
|
||||
setFileList(arr.map(i=>{return i.id}));
|
||||
setSave(arr);
|
||||
}
|
||||
|
||||
function changeAtWhoLoginList(loginList){
|
||||
|
|
|
|||
|
|
@ -275,6 +275,7 @@ function Details(props){
|
|||
...params
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
window.scrollTo(0,0);
|
||||
props.showNotification("疑修更新成功!");
|
||||
// 刷新操作记录
|
||||
setCommentReload(Math.random());
|
||||
|
|
@ -303,6 +304,7 @@ function Details(props){
|
|||
due_date
|
||||
}).then(result=>{
|
||||
if(result && result.data && result.data.project_issues_index){
|
||||
window.scrollTo(0,0);
|
||||
props.showNotification("任务复制成功!");
|
||||
props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`);
|
||||
}
|
||||
|
|
@ -434,7 +436,7 @@ function Details(props){
|
|||
editFlag={editFlag}
|
||||
chooseFunc={(list)=>{setAssigner_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(l);}}
|
||||
double={5}
|
||||
removeFlag
|
||||
removeFlag={!editFlag}
|
||||
/>
|
||||
<DropMenu
|
||||
placeholder="状态"
|
||||
|
|
@ -461,7 +463,7 @@ function Details(props){
|
|||
chooseFunc={(list)=>{setTag_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,l);}}
|
||||
double={3}
|
||||
colorFlag="2"
|
||||
removeFlag
|
||||
removeFlag={!editFlag}
|
||||
editFlag={editFlag}
|
||||
onAdd={permission && permission !== "Reporter" ? ()=>{getSign();} :false}
|
||||
owner={owner}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ function List(props){
|
|||
useEffect(()=>{
|
||||
const datas = cookie.load('issuestates');
|
||||
let states = datas === "undefined" ? undefined : datas;
|
||||
console.log(states);
|
||||
if(states){
|
||||
setAboutMe(states.participant_category);
|
||||
setCategory(states.category);
|
||||
|
|
@ -82,12 +81,12 @@ function List(props){
|
|||
|
||||
useEffect(()=>{
|
||||
let v = allValue && allValue > 0;
|
||||
if((updateIds && !v && (updateIds.author_id || updateIds.issue_priorities_id || updateIds.issue_tag_ids || updateIds.milestone_id || updateIds.sort_by || updateIds.status_id ||updateIds.assigner_id)) || value || end_date || begin_date){
|
||||
if((updateIds && !v && (updateIds.author_id || updateIds.issue_priorities_id || updateIds.issue_tag_ids || updateIds.milestone_id || updateIds.sort_by || updateIds.status_id ||updateIds.assigner_id)) || value || end_date || begin_date || aboutMe !=="all"){
|
||||
setClearFlag(true);
|
||||
}else{
|
||||
setClearFlag(false);
|
||||
}
|
||||
},[updateIds,value,allValue,begin_date,end_date])
|
||||
},[updateIds,value,allValue,begin_date,end_date,aboutMe])
|
||||
// 自定义一个初始不更新的hook
|
||||
const useUpdateEffect = (fn, inputs) => {
|
||||
const didMountRef = useRef(false);
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ function New(props){
|
|||
blockchain_token_num:rewardAmount
|
||||
}).then(result=>{
|
||||
if(result && result.data && result.data.project_issues_index){
|
||||
window.scrollTo(0,0);
|
||||
props.showNotification("任务创建成功!");
|
||||
props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,22 +47,6 @@
|
|||
background-color: #f3f4f6;
|
||||
}
|
||||
}
|
||||
// 操作按钮-蓝色
|
||||
.operateButton{
|
||||
padding:0px 15px;
|
||||
height:32px;
|
||||
color: #fff!important;
|
||||
cursor: pointer;
|
||||
line-height: 30px;
|
||||
background-color:rgba(70, 106, 255, 1);
|
||||
border:1px solid rgba(70, 106, 255, 1);
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
&:hover{
|
||||
background-color:#708cff;
|
||||
border-color:#708cff;
|
||||
}
|
||||
}
|
||||
// flex-左右,上下居中
|
||||
.between{
|
||||
display: flex;
|
||||
|
|
@ -187,11 +171,15 @@
|
|||
}
|
||||
.dropboxul{
|
||||
padding-right: 20px;
|
||||
li.minwidth{
|
||||
min-width: 70px;
|
||||
}
|
||||
li{
|
||||
min-width: 106px;
|
||||
min-width:88px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
cursor: default;
|
||||
margin-left: 12px;
|
||||
.dropspan{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -804,7 +792,7 @@
|
|||
}
|
||||
}
|
||||
li{
|
||||
width: 100px;
|
||||
width: 98px;
|
||||
height:32px;
|
||||
line-height:30px;
|
||||
text-align: center;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ function CoderDepot(props){
|
|||
const [ desc , setDesc ] = useState(undefined);
|
||||
const [ website , setWebsite ] = useState(undefined);
|
||||
const [ lesson_url , setLessonUrl ] = useState(undefined);
|
||||
const [ topics, setTopics] = useState(undefined);
|
||||
const [ readme , setReadme ] = useState(undefined);
|
||||
const [ defaultBranch , setDefaultBranch ] = useState(undefined);
|
||||
const [ editReadme , setEditReadme ] = useState(false);
|
||||
|
|
@ -62,8 +63,9 @@ function CoderDepot(props){
|
|||
branchName = returnbar(branchName);
|
||||
const details = props.projectDetail;
|
||||
let pathname = props.history.location.pathname;
|
||||
const { platform } = props ;
|
||||
//distribution:判断此用户是否可以创建发行版
|
||||
const distribution = details && details.type != 2 && (details.permission === "Admin" || details.permission === "Owner" || details.permission === "Manager");
|
||||
const distribution = details && details.type !== 2 && ( details.permission && details.permission !== "Reporter");
|
||||
const { bannerList } = props;
|
||||
useEffect(()=>{
|
||||
if(bannerList && bannerList.length>0){
|
||||
|
|
@ -93,6 +95,7 @@ function CoderDepot(props){
|
|||
useEffect(()=>{
|
||||
if(details){
|
||||
setProjectDetail(details);
|
||||
setTopics(details.topics);
|
||||
setDesc(details.description);
|
||||
setWebsite(details.website);
|
||||
setLessonUrl(details.lesson_url);
|
||||
|
|
@ -139,17 +142,19 @@ function CoderDepot(props){
|
|||
},[projectsId,owner,pathname,defaultBranch])
|
||||
|
||||
useEffect(()=>{
|
||||
setReadOnly(props.history.location.search.indexOf('edit') !== -1);
|
||||
axios.get(`/${owner}/${projectsId}/releases.json`).then((result)=>{
|
||||
if(result && result.data){
|
||||
const release = {
|
||||
"list":result.data.releases,
|
||||
"total_count":result.data.releases && result.data.releases.length
|
||||
if(platform){
|
||||
setReadOnly(props.history.location.search.indexOf('edit') !== -1);
|
||||
axios.get(`/${owner}/${projectsId}/releases.json`).then((result)=>{
|
||||
if(result && result.data){
|
||||
const release = {
|
||||
"list":result.data.releases,
|
||||
"total_count":result.data.releases && result.data.releases.length
|
||||
}
|
||||
setReleaseVersions(release);
|
||||
}
|
||||
setReleaseVersions(release);
|
||||
}
|
||||
})
|
||||
},[])
|
||||
})
|
||||
}
|
||||
},[platform])
|
||||
|
||||
// 获取readme信息
|
||||
function getReadmeInfo(path, ref) {
|
||||
|
|
@ -185,7 +190,7 @@ function CoderDepot(props){
|
|||
setZip_url(result.data.zip_url);
|
||||
let c = result.data.last_commit
|
||||
setLastCommit(c && c.commit);
|
||||
setLastCommitAuthor(c && c.committer);
|
||||
setLastCommitAuthor(c && c.author);
|
||||
setMainFlag(true);
|
||||
setReadOnly(true);
|
||||
// setReadme(result.data.readme);
|
||||
|
|
@ -233,7 +238,7 @@ function CoderDepot(props){
|
|||
}
|
||||
let c = result.data.last_commit
|
||||
setLastCommit(c && c.commit);
|
||||
setLastCommitAuthor(c && c.committer);
|
||||
setLastCommitAuthor(c && c.author);
|
||||
setMainFlag(false);
|
||||
setReadOnly(true);
|
||||
setReadOnly(!editReadme);
|
||||
|
|
@ -295,6 +300,7 @@ function CoderDepot(props){
|
|||
let checkvalue = turnbar(enBranch);
|
||||
let enPath = turnbar(path);
|
||||
setType(type);
|
||||
window.scrollTo(0,0)
|
||||
props.history.push(`/${owner}/${projectsId}${`/tree/${checkvalue}`}${enPath?`/${enPath}`:""}`);
|
||||
}
|
||||
}
|
||||
|
|
@ -339,15 +345,17 @@ function CoderDepot(props){
|
|||
showNotification={props.showNotification}/>
|
||||
)
|
||||
// 确认修改简介、website、实践课程链接
|
||||
function okUpdate(d,w,l){
|
||||
function okUpdate(d,w,l, projectTopicNames){
|
||||
const url = `/${owner}/${projectsId}.json`;
|
||||
axios.put(url,{
|
||||
description:d,website:w || "",lesson_url:l||""
|
||||
description:d,website:w || "",lesson_url:l||"",
|
||||
project_topic_names: projectTopicNames
|
||||
}).then(result=>{
|
||||
if(result && result.data && result.data.id){
|
||||
setDesc(result.data.description);
|
||||
setWebsite(result.data.website);
|
||||
setLessonUrl(result.data.lesson_url);
|
||||
setTopics(result.data.topics);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -378,7 +386,7 @@ function CoderDepot(props){
|
|||
const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin));
|
||||
return(
|
||||
<WhiteBack>
|
||||
<UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
|
||||
<UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} topicsInfo={topics} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
|
||||
<Spin spinning={isSpin}>
|
||||
{
|
||||
(dirInfo || fileInfo) &&
|
||||
|
|
@ -582,6 +590,10 @@ function CoderDepot(props){
|
|||
}
|
||||
</FlexAJ>
|
||||
{desc && <p className="font-14 color-grey-3 mb15 task-hide-2" style={{lineHeight:"24px",WebkitLineClamp:"4",textAlign:"justify",wordBreak:"break-all"}} title={desc}>{desc}</p>}
|
||||
{/* 项目标签 */}
|
||||
{topics && <div className='projectHomeTopics'>
|
||||
{topics.map(item=><span className='proHomeTopic mr15 mb10 font-13 task-hide'>{item.name}</span>)}
|
||||
</div>}
|
||||
{
|
||||
website &&
|
||||
<div className="color-grey-6 df pinfos mb5">
|
||||
|
|
@ -622,7 +634,7 @@ function CoderDepot(props){
|
|||
}
|
||||
{/* 发布 */}
|
||||
{
|
||||
releaseVersions && props.platform &&
|
||||
releaseVersions &&
|
||||
<React.Fragment>
|
||||
<Divider />
|
||||
<Releases
|
||||
|
|
|
|||
|
|
@ -29,12 +29,22 @@ function CoderDepotCatalogue({item , goToSubRoot , owner , projectsId , platform
|
|||
<span><i className={`iconfont ${typeIco[`${item.type}`]} mr8`}></i>{item.name}</span>
|
||||
:
|
||||
(item.type === "submodule" ?
|
||||
<React.Fragment>
|
||||
<a href={`${item.submodule_git_url.slice(0,item.submodule_git_url.length-4)}`} target="_self">
|
||||
<i className={`iconfont ${typeIco[`${item.type}`]} mr8`}></i>{item.name}
|
||||
</a>
|
||||
<span className="ml2">@</span><a href={`${item.submodule_git_url.slice(0,item.submodule_git_url.length-4)}/commits/${truncateCommitId(item.sha)}`} title={item.sha}>{truncateCommitId(item.sha)}</a>
|
||||
</React.Fragment>
|
||||
(
|
||||
item.submodule_git_url ?
|
||||
<React.Fragment>
|
||||
<a href={`${item.submodule_git_url.slice(0,item.submodule_git_url.length-4)}`} onClick={()=>{window.scrollTo(0,0)}} target="_self">
|
||||
<i className={`iconfont ${typeIco[`${item.type}`]} mr8`}></i>{item.name}
|
||||
</a>
|
||||
<span className="ml2">@</span>
|
||||
<a href={`${item.submodule_git_url.slice(0,item.submodule_git_url.length-4)}/commits/${truncateCommitId(item.sha)}`} onClick={()=>{window.scrollTo(0,0)}} title={item.sha}>{truncateCommitId(item.sha)}</a>
|
||||
</React.Fragment>
|
||||
:
|
||||
<React.Fragment>
|
||||
<i className={`iconfont ${typeIco[`${item.type}`]} mr8`}></i>{item.name}
|
||||
<span className="ml2">@</span>
|
||||
<span title={item.sha}>{truncateCommitId(item.sha)}</span>
|
||||
</React.Fragment>
|
||||
)
|
||||
:
|
||||
<a onClick={()=>goToSubRoot(item.path,item.type,item.name)}>
|
||||
<i className={`iconfont ${typeIco[`${item.type}`]} mr8`}></i>{item.name}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
|||
</span>
|
||||
</Dropdown>
|
||||
|
||||
<span className="commonBox-title-read"><a href="#readme ">README.md</a></span>
|
||||
<span className="commonBox-title-read"><a href="#readme ">{readme.name}</a></span>
|
||||
|
||||
</AlignCenter>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import axios from "axios";
|
|||
import Meditor from "../Newfile/m_editor";
|
||||
import RenderHtml from "../../components/render-html";
|
||||
import ReadmeCatelogue from "./sub/ReadmeCatelogue";
|
||||
import { Base64 } from "js-base64";
|
||||
|
||||
const $ = window.$;
|
||||
function bytesToSize(bytes) {
|
||||
|
|
@ -158,6 +159,7 @@ class CoderRootFileDetail extends Component {
|
|||
axios.delete(url, {
|
||||
params: {
|
||||
filepath: detail.path,
|
||||
base64_filepath:Base64.encode(detail.path),
|
||||
branch:branchName,
|
||||
sha: detail.sha,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Spin, Tooltip } from 'antd';
|
||||
import { Link, Route, Switch } from 'react-router-dom';
|
||||
import { withRouter } from "react-router";
|
||||
import { Content, AlignTop } from '../Component/layout';
|
||||
import DetailBanner from './sub/DetailBanner';
|
||||
import cookie from 'react-cookies';
|
||||
|
|
@ -21,18 +22,6 @@ const Setting = Loadable({
|
|||
loader: () => import('../Settings/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
const TagList = Loadable({
|
||||
loader: () => import('../Order/Tags'),
|
||||
loading: Loading,
|
||||
})
|
||||
const OrderNew = Loadable({
|
||||
loader: () => import('../Order/New'),
|
||||
loading: Loading,
|
||||
})
|
||||
const OrderDetail = Loadable({
|
||||
loader: () => import('../Order/Detail'),
|
||||
loading: Loading,
|
||||
})
|
||||
const OrderIndex = Loadable({
|
||||
loader: () => import('../Issues/index'),
|
||||
loading: Loading,
|
||||
|
|
@ -55,18 +44,6 @@ const OrderupdateMilepost = Loadable({
|
|||
loader: () => import('../Order/UpdateMilepost'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const OrderupdateDetail = Loadable({
|
||||
// loader: () => import('../Order/UpdateDetail'),
|
||||
loader: () => import('../Order/order_form'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const OrdercopyDetail = Loadable({
|
||||
loader: () => import('../Order/CopyDetail'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//合并请求
|
||||
const MergeIndexDetail = Loadable({
|
||||
loader: () => import('../Merge/merge'),
|
||||
|
|
@ -155,10 +132,6 @@ const Review = Loadable({
|
|||
loader: () => import('../Newfile/codeReview'),
|
||||
loading: Loading,
|
||||
});
|
||||
// const WebIDE = Loadable({
|
||||
// loader: () => import('../Newfile/webIDE'),
|
||||
// loading: Loading,
|
||||
// });
|
||||
/**
|
||||
* permission:Manager:管理员,Reporter:报告人员(只有读取权限),Developer:开发人员(除不能设置仓库信息外)
|
||||
*/
|
||||
|
|
@ -222,6 +195,7 @@ class Detail extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
componentDidMount = () => {
|
||||
this.getProject();
|
||||
let history = this.props.location;
|
||||
|
|
@ -604,7 +578,7 @@ class Detail extends Component {
|
|||
}
|
||||
<span className="detail_tag_btn">
|
||||
<a className="detail_tag_btn_name" style={{ cursor: platform ? "pointer" : "default" }} onClick={() => this.focusFunc(watched)}>
|
||||
<i className={watched ? "iconfont icon-shixing color-orange font-16 mr3" : "iconfont icon-kongxing color-grey-9 font-16 mr3"}></i>
|
||||
<i className={watched ? "iconfont icon-yiguanzhuicon color-orange font-16 mr3" : "iconfont icon-guanzhuicon color-grey-9 font-16 mr3"}></i>
|
||||
<span>{watched ? '取消关注' : '关注'}</span>
|
||||
</a>
|
||||
{
|
||||
|
|
@ -620,7 +594,7 @@ class Detail extends Component {
|
|||
</span>
|
||||
<span className="detail_tag_btn">
|
||||
<a className="detail_tag_btn_name" style={{ cursor: platform ? "pointer" : "default" }} onClick={() => this.pariseFunc(praised)}>
|
||||
<i className={praised ? "iconfont icon-weibiaoti105 color-orange font-14 mr3" : "iconfont icon-guanzhu color-grey-9 font-14 mr3"}></i>
|
||||
<i className={praised ? "iconfont icon-yidianzan_icon color-orange font-15 mr3" : "iconfont icon-xingzhuang color-grey-9 font-14 mr3"}></i>
|
||||
<span>{praised ? '取消点赞' : '点赞'}</span>
|
||||
</a>
|
||||
{
|
||||
|
|
@ -636,7 +610,7 @@ class Detail extends Component {
|
|||
<span className="detail_tag_btn" loading={forkSpin}>
|
||||
<Tooltip title="复刻是fork的中文名,即复制代码仓库" placement="bottom">
|
||||
<a className="detail_tag_btn_name" style={{ cursor: platform ? "pointer" : "default" }} onClick={this.forkFunc}>
|
||||
<i className="iconfont icon-fork color-grey-9 mr3 font-16"></i><span>复刻(Fork)</span>
|
||||
<i className="iconfont icon-morenfuke_icon1 color-grey-9 mr3 font-16"></i><span>复刻(Fork)</span>
|
||||
</a>
|
||||
</Tooltip>
|
||||
{
|
||||
|
|
@ -719,13 +693,13 @@ class Detail extends Component {
|
|||
}
|
||||
></Route>
|
||||
{/* wiki编辑文件 */}
|
||||
<Route path="/:owner/:projectsId/wiki/edit/:wikiName"
|
||||
<Route path="/:owner/:projectsId/wiki/:wikiName/:key/edit"
|
||||
render={
|
||||
() => (<WikiEdit {...this.props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
{/* wiki具体某一个地址 */}
|
||||
<Route path="/:owner/:projectsId/wiki/:wikiName"
|
||||
<Route path="/:owner/:projectsId/wiki/:wikiName/:key"
|
||||
render={
|
||||
(props) => (<Wiki {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
|
|
@ -934,7 +908,7 @@ class Detail extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default ImageLayerOfCommentHOC({
|
||||
export default withRouter(ImageLayerOfCommentHOC({
|
||||
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
|
||||
parentSelector: ".newContainer",
|
||||
})(Detail);
|
||||
})(Detail));
|
||||
|
|
|
|||
|
|
@ -263,6 +263,9 @@
|
|||
color: #466AFF;
|
||||
}
|
||||
}
|
||||
li{
|
||||
line-height: 15px;
|
||||
}
|
||||
margin-top: 2px;
|
||||
// cursor: pointer;
|
||||
#ptxt{
|
||||
|
|
@ -526,4 +529,22 @@
|
|||
}
|
||||
.readmeFile p[align=center]{
|
||||
white-space: normal;
|
||||
}
|
||||
// 项目标签
|
||||
.projectHomeTopics{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
&.myProList .proHomeTopic{
|
||||
background-color:#f3f8ff;
|
||||
color:#4c5b76;
|
||||
}
|
||||
.proHomeTopic{
|
||||
background-color: #eff2ff;
|
||||
border-radius: 4px;
|
||||
max-width: 158px;
|
||||
padding: 0 10px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import { Link } from 'react-router-dom';
|
|||
import '../css/index.scss';
|
||||
import Nodata from '../Nodata';
|
||||
import './list.scss';
|
||||
import img_parise from '../Images/parise.png';
|
||||
import './Index.scss';
|
||||
|
||||
class IndexItem extends Component {
|
||||
TurnToDetail = (login, url) => {
|
||||
|
|
@ -35,7 +35,7 @@ class IndexItem extends Component {
|
|||
<div className="p-r-Infos">
|
||||
<div className="p-r-name">
|
||||
<AlignCenter>
|
||||
<Link to={`/${item.author.login}/${item.identifier}`} target="_blank" title={`${item.author.name}/${item.name}`} className="color-grey-3 font-18 task-hide " style={{maxWidth: 470 }}>
|
||||
<Link to={`/${item.author.login}/${item.identifier}`} title={`${item.author.name}/${item.name}`} className="color-grey-3 font-18 task-hide " style={{maxWidth: 470 }}>
|
||||
{item.author.name}/{item.name}
|
||||
</Link>
|
||||
{ !item.is_public && <span className="privateTag">私有</span> }
|
||||
|
|
@ -57,19 +57,22 @@ class IndexItem extends Component {
|
|||
{
|
||||
item.praises_count && item.praises_count>0 ?
|
||||
<span className="pariseTag">
|
||||
<img src={img_parise} alt="" className="pariseImg" />赞 {item.praises_count}
|
||||
<i className="iconfont icon-xingzhuang font-12 mr3" style={{color:"#ffb830"}} />赞 {item.praises_count}
|
||||
</span>:""
|
||||
}
|
||||
{
|
||||
item.forked_count && item.forked_count>0 ?
|
||||
<span>
|
||||
<i className="iconfont icon-fork mr3 font-16 color-blue"/>fork {item.forked_count}
|
||||
<i className="iconfont icon-morenfuke_icon1 mr3 font-14 color-blue"/>fork {item.forked_count}
|
||||
</span>:""
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>
|
||||
|
||||
{/* 项目标签 */}
|
||||
{item.topics && <div className='projectHomeTopics myProList'>
|
||||
{item.topics.map(topic=><span className='proHomeTopic mr15 font-13 task-hide'>{topic.name}</span>)}
|
||||
</div>}
|
||||
<div className="p-r-about">
|
||||
<span className="p-r-detail">
|
||||
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
|
||||
|
|
|
|||
|
|
@ -779,6 +779,8 @@ a.color-grey-ccc:hover{
|
|||
}
|
||||
.treecopy-cont{
|
||||
padding: 4px 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.btn-83{
|
||||
margin-left: 20px;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ function Index() {
|
|||
style={{width:"300px"}}
|
||||
allowClear
|
||||
/>
|
||||
<Link to={`/explore/all${search ? '?search='+search : ''}`} target="_blank">更多<i className="iconfont icon-triangle font-12"></i></Link>
|
||||
<Link to={`/explore/all${search ? '?search='+search : ''}`}>更多<i className="iconfont icon-triangle font-12"></i></Link>
|
||||
</div>
|
||||
<Spin spinning={isSpin}>
|
||||
<div style={{minHeight:"400px"}}>
|
||||
|
|
@ -110,20 +110,24 @@ function Index() {
|
|||
<img className="p-r-photo" alt="" src={i.author && i.author.image_url} ></img>
|
||||
</a>
|
||||
:
|
||||
<Link to={`/${i.author && i.author.login}`} target="_blank"><img src={getImageUrl(`/${i.author && i.author.image_url}`)} alt="" /></Link>
|
||||
<Link to={`/${i.author && i.author.login}`}><img src={getImageUrl(`/${i.author && i.author.image_url}`)} alt="" /></Link>
|
||||
}
|
||||
<div className="itemTitle">
|
||||
<div className="item-title-infos">
|
||||
<Link to={`/${i.author && i.author.login}/${i.identifier}`} target="_blank" className="infotitle task-hide">{i.author && i.author.name}/{i.name}</Link>
|
||||
{i.praises_count > 0 ? <span><i className="iconfont icon-dianzan11 mr3 font-16"></i>{i.praises_count}</span> :"" }
|
||||
{i.forked_count > 0 ? <span><i className="iconfont icon-fork2 mr3 font-13"></i>{i.forked_count}</span>:""}
|
||||
<Link to={`/${i.author && i.author.login}/${i.identifier}`} className="infotitle task-hide">{i.author && i.author.name}/{i.name}</Link>
|
||||
{i.praises_count > 0 ? <span><i className="iconfont icon-xingzhuang mr3 font-14"></i>{i.praises_count}</span> :"" }
|
||||
{i.forked_count > 0 ? <span><i className="iconfont icon-yifuke_icon mr3 font-14"></i>{i.forked_count}</span>:""}
|
||||
</div>
|
||||
<div className="item-desc task-hide-2">
|
||||
{i.description}
|
||||
</div>
|
||||
{/* 项目标签 */}
|
||||
{i.topics && <div className='proListTopics'>
|
||||
{i.topics.map(item=><span className='proListTopic mr15 font-13 task-hide'>{item.name}</span>)}
|
||||
</div>}
|
||||
<div className="item-data">
|
||||
{i.category && i.category.id ? <span className="category">{i.category.name}</span> :"" }
|
||||
{i.language && i.language.id ? <span className="language mr30">{i.language.name}</span> :""}
|
||||
{i.language && i.language.id ? <span className={`language mr30 ${i.category && i.category.id ? 'hasCate' : ''}`}>{i.language.name}</span> :""}
|
||||
<span style={{lineHeight:"15px",display:"flex"}}><i className="iconfont icon-shijian font-15 mr5"></i>更新于{i.time_ago}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -439,8 +439,10 @@
|
|||
}
|
||||
.language{
|
||||
position: relative;
|
||||
padding-left: 11px;
|
||||
&::before{
|
||||
&.hasCate{
|
||||
padding-left: 11px;
|
||||
}
|
||||
&.hasCate::before{
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 1px;
|
||||
|
|
@ -558,4 +560,18 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.proListTopics{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.proListTopic{
|
||||
background-color:#f3f8ff;
|
||||
border-radius: 4px;
|
||||
max-width: 158px;
|
||||
padding: 0 10px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
color: #4c5b76;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ function Contribute(props){
|
|||
<AlignCenter className='mb15'>
|
||||
<img alt="" style={{borderRadius:"50%",marginRight:"10px"}} src={getImageUrl(`/${item.image_url}`)} width="50px" height="50px"/>
|
||||
<div>
|
||||
<Link to={`/${item.login}`} className="font-16">{item.name}</Link>
|
||||
<a href={item.login ? `/${item.login}` : `mailto:${item.email}`} className="font-16">{item.name}</a>
|
||||
<p className="font-12 color-grey-9">提交{item.contributions}次{item.contribution_perc ? ` I 贡献占比${item.contribution_perc}` : ''}</p>
|
||||
</div>
|
||||
</AlignCenter>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
import React , { forwardRef, useEffect } from 'react';
|
||||
import {Form , Modal , Input } from 'antd';
|
||||
import {Form , Modal , Input, Select } from 'antd';
|
||||
import "./sub.scss";
|
||||
import axios from 'axios';
|
||||
import { useState } from 'react';
|
||||
const { TextArea } = Input;
|
||||
function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_url}){
|
||||
function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_url, topicsInfo}){
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
const [topics, setTopics] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取所有的项目标签
|
||||
axios.get(`/v1/project_topics`).then(res=>{
|
||||
if(res && res.data){
|
||||
setTopics(res.data.project_topics);
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
if(desc || website){
|
||||
|
|
@ -17,10 +29,22 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_u
|
|||
validateFields((err,values)=>{
|
||||
if(!err){
|
||||
onCancel();
|
||||
onOk(values.desc,values.website,values.lesson_url)
|
||||
onOk(values.desc,values.website,values.lesson_url, values.project_topic_names)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 校验项目标签
|
||||
function checkTopics(rule, value, callback){
|
||||
const boolLength = value.filter(item=>item.length > 20);
|
||||
if(value.length > 5){
|
||||
callback("最多创建5个标签");
|
||||
}else if(boolLength.length){
|
||||
callback("每个标签最多支持20个汉字");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
return(
|
||||
<Modal
|
||||
title={"修改信息"}
|
||||
|
|
@ -31,8 +55,9 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_u
|
|||
onOk={onSure}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
width="400px"
|
||||
width="520px"
|
||||
className={"descmodal"}
|
||||
maskClosable={false}
|
||||
>
|
||||
<Form>
|
||||
<Form.Item label="项目简介">
|
||||
|
|
@ -42,6 +67,16 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_u
|
|||
<TextArea placeholder="请输入项目简介" rows={4} maxLength={200}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="项目标签">
|
||||
{getFieldDecorator("project_topic_names",{
|
||||
rules:[{validator:checkTopics}],
|
||||
initialValue: topicsInfo && topicsInfo.map(item=>item.name)
|
||||
})(
|
||||
<Select mode='tags' placeholder="请输入标签名称" getPopupContainer={trigger => trigger.parentNode} maxLength={2}>
|
||||
{topics && topics.map(item=> <Select.Option key={item.name}>{item.name}</Select.Option>)}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="website">
|
||||
{getFieldDecorator("website",{
|
||||
rules:[]
|
||||
|
|
|
|||
|
|
@ -22,6 +22,16 @@
|
|||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.ant-select-selection--multiple{
|
||||
padding: 0 0 5px !important;
|
||||
}
|
||||
.ant-select-selection--multiple .ant-select-selection__choice{
|
||||
color: $primary-color;
|
||||
background-color:#eff2ff;
|
||||
.ant-select-selection__choice__remove{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menuslist{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ function Tags(props) {
|
|||
// 删除标签
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { projectsId, owner } = props.match.params;
|
||||
const { projectDetail} = props;
|
||||
const { projectDetail, current_user} = props;
|
||||
const permission = projectDetail && projectDetail.permission;
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail && document.title.indexOf('所有标签-') === -1){
|
||||
|
|
@ -60,18 +61,18 @@ function Tags(props) {
|
|||
render: (txt, item) => {
|
||||
return (
|
||||
<div className="tagBranch">
|
||||
<Link className="hover tagClass font-16" to={`/${owner}/${projectsId}/tree/${item.name}`}>{item.name}</Link>
|
||||
<Link className="hover tagClass font-16" to={`/${owner}/${projectsId}/tree/${item.name}`} onClick={()=>{window.scrollTo(0,0)}}>{item.name}</Link>
|
||||
<div className="tagModel">
|
||||
{
|
||||
item.tagger &&
|
||||
<Tooltip placement="top" title={item.tagger.name}>
|
||||
{
|
||||
item.tagger.id ?
|
||||
<Link className="mr3 tagModelImg" to={`/${item.tagger.login}`} >
|
||||
<Link className="mr3 tagModelImg" to={`/${item.tagger.login}`} onClick={()=>{window.scrollTo(0,0)}}>
|
||||
<img src={getImageUrl(`/${item.tagger && item.tagger.image_url}`)} alt="" />
|
||||
</Link>
|
||||
:
|
||||
<span className="mr3 tagModelImg" style={{ cursor: "default" }}>
|
||||
<span className="mr3 tagModelImg" style={{ cursor: "default" }} onClick={()=>{window.scrollTo(0,0)}}>
|
||||
<img src={getImageUrl(`/${item.tagger && item.tagger.image_url}`)} alt="" />
|
||||
</span>
|
||||
}
|
||||
|
|
@ -96,7 +97,7 @@ function Tags(props) {
|
|||
<span style={{lineHeight: '30px'}}>
|
||||
<img src={Tree} alt="sha" width={"16px"}/>
|
||||
<Tooltip placement="top" title={`最后提交日期:${item.created_at_unix ? moment(item.created_at_unix * 1000).format('YYYY-MM-DD') : ''}`}>
|
||||
<Link className="hover color-blue" to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.id}`)}`}>{truncateCommitId(item.id)}</Link>
|
||||
<Link className="hover color-blue" to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.id}`)}`} onClick={()=>{window.scrollTo(0,0)}}>{truncateCommitId(item.id)}</Link>
|
||||
</Tooltip>
|
||||
<input type="text" id={`value${truncateCommitId(`${item.id}`)}`} value={`${truncateCommitId(item.id)}`}/>
|
||||
</span>
|
||||
|
|
@ -130,8 +131,24 @@ function Tags(props) {
|
|||
<a href={`${item.zipball_url}`} download className="btn-83" style={{width: '76px'}}>
|
||||
<i className="iconfont icon-xiazai-icon font-16 mr5"></i>ZIP
|
||||
</a>
|
||||
{item.has_release ? <Tooltip title={`该标签已绑定发行版,不支持创建`}><a className="btn-83 disabledBut">创建发行版</a></Tooltip> : <Link to={`/${owner}/${projectsId}/releases/new?tag=${item.name}`} className="btn-83">创建发行版</Link>}
|
||||
{item.has_release ? <Tooltip title={`该标签已绑定发行版,不支持删除操作`}><Button style={{width: '76px'}} disabled className='ml8'>删除</Button></Tooltip> : <Button className='f6_bor_but ml8' style={{width: '76px'}} onClick={()=>{setVisible(item)}}>删除</Button>}
|
||||
{
|
||||
(current_user && current_user.login) && (permission && permission!=="Reporter") &&
|
||||
<React.Fragment>
|
||||
{item.has_release ?
|
||||
<Tooltip title={`该标签已绑定发行版,不支持创建`}>
|
||||
<a className="btn-83 disabledBut">创建发行版</a>
|
||||
</Tooltip>
|
||||
:
|
||||
<Link to={`/${owner}/${projectsId}/releases/new?tag=${item.name}`} className="btn-83">创建发行版</Link>
|
||||
}
|
||||
{item.has_release ?
|
||||
<Tooltip title={`该标签已绑定发行版,不支持删除操作`}>
|
||||
<Button style={{width: '76px'}} disabled className='ml8'>删除</Button>
|
||||
</Tooltip> :
|
||||
<Button className='f6_bor_but ml8' style={{width: '76px'}} onClick={()=>{setVisible(item)}}>删除</Button>
|
||||
}
|
||||
</React.Fragment>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ function Index(props) {
|
|||
|
||||
const { projectsId , owner } = props.match.params;
|
||||
const { isManager , isDeveloper , projectDetail , defaultBranch, getDetail } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
// getList();
|
||||
const url = `/v1/${owner}/${projectsId}/branches.json`;
|
||||
|
|
@ -76,7 +75,7 @@ function Index(props) {
|
|||
className: "branchNameColumn",
|
||||
render: (txt, item) => {
|
||||
return (
|
||||
<Link to={`/${owner}/${projectsId}/tree/${turnbar(txt)}`} className="task-hide">{txt}</Link>
|
||||
<Link to={`/${owner}/${projectsId}/tree/${turnbar(txt)}`} onClick={()=>{window.scrollTo(0,0)}} className="task-hide">{txt}</Link>
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
@ -241,6 +240,7 @@ function Index(props) {
|
|||
name: info.name
|
||||
}).then(res=>{
|
||||
if(res && res.data){
|
||||
getDetail();
|
||||
setReload(Math.random());
|
||||
!res.data.status && message.success('操作成功');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@
|
|||
padding:0px 15px;
|
||||
border-right: 1px solid rgba(153, 153, 153, 0.4);
|
||||
height: 100%;
|
||||
width: 135px;
|
||||
width: 140px;
|
||||
img{
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,16 +71,16 @@ function version(props) {
|
|||
<span className={`${item.draft === "稳定" ?"versionTag green":"versionTag orange"}`}>{item.draft}</span>
|
||||
<span className="color-grey-3 mt15 font-12">
|
||||
<i className="iconfont icon-biaoqianicon mr3 font-14"></i>
|
||||
<Link className="hover" to={`/${owner}/${projectsId}/tree/${item.tag_name}`} >{item.tag_name}</Link>
|
||||
<Link className="hover" to={`/${owner}/${projectsId}/tree/${item.tag_name}`} onClick={()=>{window.scrollTo(0,0)}}>{item.tag_name}</Link>
|
||||
</span>
|
||||
<span className="color-grey-3 font-12">
|
||||
<img src={Tree} width="16px" color="#333333" className="mr3"/>
|
||||
<Link className="hover" to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`}>{truncateCommitId(item.sha)}</Link>
|
||||
<img src={Tree} width="16px" color="#333333" className="mr3" alt=""/>
|
||||
<Link onClick={()=>{window.scrollTo(0,0)}} className="hover" to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`}>{truncateCommitId(item.sha)}</Link>
|
||||
</span>
|
||||
</span>
|
||||
<div className="versionInfo_right">
|
||||
<div className="versionName">
|
||||
<Link to={`/${owner}/${projectsId}/tree/${item.tag_name}`} className="task-hide color-blue hover font-18">{item.name}</Link>
|
||||
<Link onClick={()=>{window.scrollTo(0,0)}} to={`/${owner}/${projectsId}/tree/${item.tag_name}`} className="task-hide color-blue hover font-18">{item.name}</Link>
|
||||
<span>
|
||||
{data && data.user_admin_permission && type !== 2 && <Link to={{pathname:`/${owner}/${projectsId}/releases/${item.version_id}/update`,state:{"stable":item.draft==="稳定"}}} className="ml15"><i className="iconfont icon-a-bianji1 font-16 color-grey-6"></i></Link>}
|
||||
{data && data.user_admin_permission && type !== 2 &&
|
||||
|
|
@ -131,7 +131,7 @@ function version(props) {
|
|||
return (
|
||||
<React.Fragment>
|
||||
{
|
||||
data && data.user_admin_permission && type !== 2 &&
|
||||
data && data.user_permission && type !== 2 &&
|
||||
<div className="addReleaseBtn">
|
||||
<Button type={"primary"} onClick={addFunc} className="btnblue" style={{height:"36px"}}>发布新版本</Button>
|
||||
</div>
|
||||
|
|
@ -144,7 +144,7 @@ function version(props) {
|
|||
} else if (releases && releases.length === 0) {
|
||||
return (
|
||||
<Empty
|
||||
operation={data && data.user_admin_permission && type !== 2}
|
||||
operation={data && data.user_permission && type !== 2}
|
||||
addFunc={addFunc}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@
|
|||
margin-top: 5px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #eee;
|
||||
// width: 100%;
|
||||
width: 100%;
|
||||
a{
|
||||
display: block;
|
||||
color: #333;
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ function Files({ data,history,owner,projectsId , parentsSha }){
|
|||
<div onClick={()=>{setIsOpen(!isOpen)}}>
|
||||
<i className={`iconfont mr5 ${isOpen? "font-18 icon-sanjiaoxing-down":"font-16 icon-triangle"}`}></i>
|
||||
<span className="color-grey-6 update-file-count">
|
||||
共有<span className="color-grey-3"> {data && data.files_count} 个文件 </span>被更改,包括
|
||||
{ data && data.total_addition ? <span className="color-green"> {data && data.total_addition} 次插入</span>:"" }
|
||||
共有<span className="color-grey-3"> {data && data.files_count} 个文件 </span>被更改
|
||||
{ data && data.total_addition ? <span>,包括 <span className="color-green">{data && data.total_addition} 次插入</span></span>:"" }
|
||||
{ data && data.total_addition && data.total_deletion ? " 和 ":""}
|
||||
{ data && data.total_deletion ? <span className="color-red"> {data && data.total_deletion} 次删除</span>:""}
|
||||
</span>
|
||||
|
|
@ -90,8 +90,12 @@ function Files({ data,history,owner,projectsId , parentsSha }){
|
|||
<a id= {`value${key}`} className="anchorPoint"></a>
|
||||
<FlexAJ className="filesInfo">
|
||||
<AlignCenter>
|
||||
{!item.isBin ? <i className={!item.flag?"iconfont icon-sanjiaoxing-down color-grey-9 mt4":"iconfont icon-triangle font-15 color-grey-9"} onClick={()=>showDown(item.flag,key,item.isBin)}></i>:""}
|
||||
<span className="cursor-pointer" data-clipboard-text={item.name} onClick={()=>showDown(item.flag,key,item.isBin)}>{item.name}</span>
|
||||
{!item.isBin ? <i className={!item.flag?"iconfont icon-sanjiaoxing-down color-grey-9":"iconfont icon-triangle font-15 color-grey-9"} onClick={()=>showDown(item.flag,key,item.isBin)}></i>:""}
|
||||
<span className="cursor-pointer" data-clipboard-text={item.name} onClick={()=>showDown(item.flag,key,item.isBin)}>
|
||||
{ item.isRenamed && item.old_name}
|
||||
{ item.isRenamed && <i className="iconfont icon-youjiang font-12 color-grey-8 ml5 mr5"></i> }
|
||||
{item.name}
|
||||
</span>
|
||||
<Tooltip
|
||||
title={copyfileTipTitle}
|
||||
onVisibleChange={()=>setCopyfileTipTitle("复制文件路径")}
|
||||
|
|
|
|||
|
|
@ -100,4 +100,16 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.empBtn{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 60px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #F3F4F6;
|
||||
}
|
||||
.mergeBox{
|
||||
margin:0px auto;
|
||||
width: 1200px;
|
||||
}
|
||||
|
|
@ -56,6 +56,7 @@ class MergeItem extends Component {
|
|||
<i className={`iconfont icon-hebingqingqiu1 font-14 mr3 i_${status}`}></i>
|
||||
<Link
|
||||
to={`/${owner}/${projectsId}/pulls/${item.pull_request_number || item.pull_request_id}`}
|
||||
onClick={()=>{window.scrollTo(0,0)}}
|
||||
className="hide-1 font-15 color-grey-3 fwb lineh-30 mr10"
|
||||
style={{ maxWidth: "600px" }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { turnbar } from "educoder";
|
|||
import axios from "axios";
|
||||
import CheckProfile from '../Component/ProfileModal/Profile';
|
||||
|
||||
const Search = Input.Search;
|
||||
const { Search } = Input;
|
||||
/**
|
||||
* issue_chosen:下拉的筛选列表,
|
||||
* data:列表接口返回的所有数据,
|
||||
|
|
@ -49,6 +49,7 @@ class merge extends Component {
|
|||
assigned_to_ids: "审查人员",
|
||||
paix: "排序",
|
||||
priority_ids: "优先级",
|
||||
has_created_pull_requests:false,
|
||||
select_params: {
|
||||
status_type: "1", //开启中和关闭中,默认为开启中的
|
||||
assigned_to_id: undefined, // 指派人
|
||||
|
|
@ -113,6 +114,7 @@ class merge extends Component {
|
|||
issues: result.data.issues,
|
||||
search_count: result.data.search_count,
|
||||
isSpin: false,
|
||||
has_created_pull_requests:result.data.has_created_pull_requests
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
@ -247,7 +249,7 @@ class merge extends Component {
|
|||
data,
|
||||
isSpin,
|
||||
status_type,
|
||||
select_params,
|
||||
select_params,has_created_pull_requests
|
||||
} = this.state;
|
||||
const menu = (
|
||||
<Menu onClick={(e) => this.getMenu(e)}>
|
||||
|
|
@ -266,19 +268,16 @@ class merge extends Component {
|
|||
</Menu>
|
||||
);
|
||||
return (
|
||||
<div className="main mt20" style={{padding:"0px"}}>
|
||||
<div className="topWrapper" style={{borderBottom:"none",padding:"20px"}}>
|
||||
<div className="target-detail-search">
|
||||
<Search
|
||||
placeholder="输入关键字搜索合并请求"
|
||||
enterButton
|
||||
onSearch={this.searchFunc}
|
||||
style={{ width: 300 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt30 mergeBox">
|
||||
<div className="topWrapper">
|
||||
<Search
|
||||
placeholder="输入关键字搜索合并请求"
|
||||
style={{ width: 354 , height : 32 }}
|
||||
onSearch={this.searchFunc}
|
||||
/>
|
||||
{
|
||||
data && data.user_admin_or_developer &&
|
||||
<CheckProfile {...this.props} className="topWrapper_btn ml10" sureFunc={() => this.checkOperation()}>
|
||||
<CheckProfile {...this.props} className="operateButton ml10" sureFunc={() => this.checkOperation()}>
|
||||
+ 新建合并请求
|
||||
</CheckProfile>
|
||||
}
|
||||
|
|
@ -409,7 +408,7 @@ class merge extends Component {
|
|||
<div style={{minHeight:"470px"}}>
|
||||
<Spin spinning={isSpin}>
|
||||
{data && data.search_count && data.search_count > 0 ? (
|
||||
<div>
|
||||
<div className="mergeData">
|
||||
<MergeItem
|
||||
issues={issues}
|
||||
search_count={search_count}
|
||||
|
|
@ -437,7 +436,17 @@ class merge extends Component {
|
|||
) : (
|
||||
""
|
||||
)}
|
||||
{ data && data.issues && data.issues.length === 0 ? <NoneData _html="暂时还没有相关数据!" user_admin_or_developer={data.user_admin_or_developer} projectsId={projectsId} owner={owner} defaultBranch={this.props && turnbar(this.props.defaultBranch) }/> :""}
|
||||
{ data && data.issues && data.issues.length === 0 ?
|
||||
<div style={{border:"1px solid rgba(42, 97, 255, 0.23)",borderTop:" none",borderRadius: "0px 0px 2px 2px;"}}>
|
||||
<NoneData _html="暂时还没有相关数据!"
|
||||
user_admin_or_developer={data.user_admin_or_developer}
|
||||
has_created_pull_requests={has_created_pull_requests}
|
||||
projectsId={projectsId} owner={owner}
|
||||
defaultBranch={this.props && turnbar(this.props.defaultBranch) }/>
|
||||
</div>
|
||||
:""
|
||||
|
||||
}
|
||||
</Spin>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,30 @@
|
|||
import React , { Component } from 'react';
|
||||
import { Link } from "react-router-dom";
|
||||
import None from '../Images/emp.png'
|
||||
class Nodata extends Component{
|
||||
render(){
|
||||
const { _html, projectsId , owner , user_admin_or_developer , defaultBranch} = this.props;
|
||||
const { has_created_pull_requests , projectsId , owner , user_admin_or_developer , defaultBranch} = this.props;
|
||||
return(
|
||||
<div className="none_panels">
|
||||
<div>
|
||||
<div className="mb15">
|
||||
<i className="iconfont icon-hebingqingqiu font-80 ver-middle color-grey-b"></i>
|
||||
</div>
|
||||
<h3>欢迎使用合并请求!</h3>
|
||||
|
||||
<div className="color-grey-8">
|
||||
合并请求可以帮助您与他人协作编写代码。{ user_admin_or_developer && <span>在使用之前,请先创建一个 <Link className="color-blue" to={`/${owner}/${projectsId}/compare/${defaultBranch}...${defaultBranch}`}>合并请求</Link></span> }
|
||||
{/* <i className="iconfont icon-hebingqingqiu font-80 ver-middle color-grey-b"></i> */}
|
||||
<img src={None} alt=""/>
|
||||
</div>
|
||||
{
|
||||
!has_created_pull_requests &&
|
||||
<div>
|
||||
<p className="font-20">欢迎使用合并请求!</p>
|
||||
<div className="color-grey-8 mt10">
|
||||
合并请求可以帮助您与他人协作编写代码。请先创建一个合并请求
|
||||
</div>
|
||||
{ user_admin_or_developer &&
|
||||
<div className="empBtn">
|
||||
<span><Link className="operateButton" to={`/${owner}/${projectsId}/compare/${defaultBranch}...${defaultBranch}`}>去创建合并请求</Link></span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,134 +1,82 @@
|
|||
import React, { Component } from "react";
|
||||
import React, { useEffect , useState } from "react";
|
||||
import Meditor from "./m_editor";
|
||||
import "./index.css";
|
||||
import { Input } from "antd";
|
||||
import { returnbar } from 'educoder';
|
||||
import FileLanguage from '../Component/FileLanguage';
|
||||
import FP from '../Component/FileName/index';
|
||||
|
||||
class Index extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
editorValue: "",
|
||||
filename: "",
|
||||
language: undefined,
|
||||
checkName:false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(){
|
||||
this.updateDocumentTitle();
|
||||
}
|
||||
function Index(props){
|
||||
const [ filename , setFileName ] = useState("");
|
||||
const [ language , setLanguage ] = useState("");
|
||||
const [ checkName , setCheckName ] = useState(false);
|
||||
const [ empty , setEmpty ] = useState(false);
|
||||
|
||||
const {projectDetail} = props;
|
||||
const { pathname } = props && props.location;
|
||||
const urlroot = pathname.split("newfile/")[1];
|
||||
const file_path = urlroot ? `/${urlroot}/` :"/";
|
||||
|
||||
useEffect(()=>{
|
||||
updateDocumentTitle();
|
||||
},[projectDetail])
|
||||
|
||||
componentDidMount(){
|
||||
this.updateDocumentTitle();
|
||||
}
|
||||
|
||||
// 更新网页标题
|
||||
updateDocumentTitle(){
|
||||
const {projectDetail} = this.props;
|
||||
function updateDocumentTitle(){
|
||||
if(projectDetail && document.title.indexOf('新建文件-') === -1){
|
||||
const { author, name} = projectDetail;
|
||||
const { author, name } = projectDetail;
|
||||
document.title = `新建文件-${author.name}/${name}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 命名文件
|
||||
changeFileName = (e) => {
|
||||
this.setState({
|
||||
filename: e.target.value,
|
||||
});
|
||||
};
|
||||
// 取消,弹框询问
|
||||
CancelAddFile = () => {
|
||||
this.props.history.goBack();
|
||||
};
|
||||
|
||||
select_language = (e) => {
|
||||
this.setState({
|
||||
language: e
|
||||
})
|
||||
|
||||
function select_language(e){
|
||||
setLanguage(e);
|
||||
}
|
||||
|
||||
checkFileName=(e)=>{
|
||||
let value = (e.target.value);
|
||||
//\需要单独判断
|
||||
const str = '!!@/::"“”<《》>||??%';
|
||||
let reg = /\\/g;
|
||||
if(reg.test(value)){
|
||||
this.setState({
|
||||
checkName:true
|
||||
})
|
||||
}else{
|
||||
for(var i=0;i<str.length;i++){
|
||||
let s = str[i];
|
||||
if(value.indexOf(s) > -1){
|
||||
this.setState({
|
||||
checkName:true
|
||||
})
|
||||
return;
|
||||
}else{
|
||||
this.setState({
|
||||
checkName:false
|
||||
})
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeFile(value){
|
||||
setFileName(value);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { pathname } = this.props.location;
|
||||
const { filename, language , checkName } = this.state;
|
||||
const urlroot = pathname.split("newfile")[1];
|
||||
const file_path = `${urlroot}/${filename}`;
|
||||
const { projectDetail } = this.props;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="main">
|
||||
<p className="pb15 bor-bottom-greyE font-16 color-grey-3 mb20">
|
||||
新建文件
|
||||
</p>
|
||||
<div>
|
||||
<div className="grid-item mb30">
|
||||
<div className="grid-item">
|
||||
<div className={checkName?"setInputAddon red":"setInputAddon"}>
|
||||
<Input
|
||||
addonBefore={`/${ projectDetail && projectDetail.identifier }${urlroot}/`}
|
||||
value={filename}
|
||||
onChange={this.changeFileName}
|
||||
placeholder="命名文件..."
|
||||
onBlur={this.checkFileName}
|
||||
/>
|
||||
{checkName ? <p style={{color:"red",position:'absolute'}}>文件名不能包含下列任何字符:\{`!@/:"<>|?%`}</p>:"" }
|
||||
</div>
|
||||
<a onClick={this.CancelAddFile} className="color-blue">
|
||||
取消
|
||||
</a>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<FileLanguage language={language} select_language={this.select_language}></FileLanguage>
|
||||
</div>
|
||||
return(
|
||||
<React.Fragment>
|
||||
<div className="main">
|
||||
<p className="pb15 bor-bottom-greyE font-16 color-grey-3 mb20">
|
||||
新建文件
|
||||
</p>
|
||||
<div>
|
||||
<div className="grid-item mb30">
|
||||
<div className="grid-item">
|
||||
<FP
|
||||
addonBefore={`${ projectDetail && projectDetail.identifier }${returnbar(file_path)}`}
|
||||
onChangeFile={onChangeFile}
|
||||
empty={empty}
|
||||
onEmpty={(e)=>setEmpty(e)}
|
||||
/>
|
||||
</div>
|
||||
<div className="editorBorder">
|
||||
<Meditor
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
filepath={`${file_path}`}
|
||||
language = {language}
|
||||
content={undefined}
|
||||
readOnly={false}
|
||||
editor_type="new"
|
||||
descName={filename ? `Add ${filename}`:""}
|
||||
checkName={checkName}
|
||||
></Meditor>
|
||||
<div className="text-right">
|
||||
<FileLanguage language={language} select_language={select_language}></FileLanguage>
|
||||
</div>
|
||||
</div>
|
||||
<div className="editorBorder">
|
||||
<Meditor
|
||||
{...props}
|
||||
filepath={`${file_path}`}
|
||||
language = {language}
|
||||
content={undefined}
|
||||
readOnly={false}
|
||||
editor_type="new"
|
||||
descName={filename ? `Add ${filename}`:""}
|
||||
checkName={checkName}
|
||||
onEmpty={(e)=>setEmpty(e)}
|
||||
empty={empty}
|
||||
filename={filename}
|
||||
></Meditor>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
export default Index;
|
||||
export default Index;
|
||||
|
|
@ -5,6 +5,7 @@ import { Link } from "react-router-dom";
|
|||
import "./index.css";
|
||||
import { turnbar , returnbar } from 'educoder';
|
||||
import { Base64 } from 'js-base64';
|
||||
import FP from '../Component/FileName/index';
|
||||
|
||||
import axios from "axios";
|
||||
const TextArea = Input.TextArea;
|
||||
|
|
@ -16,25 +17,42 @@ class UserSubmitComponent extends Component {
|
|||
submitType: "0",
|
||||
filename: "",
|
||||
isSpin: false,
|
||||
empty:false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount=()=>{
|
||||
const { descName } = this.props;
|
||||
const { descName , empty , filename} = this.props;
|
||||
if(descName){
|
||||
this.props.form.setFieldsValue({
|
||||
desc:descName
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
empty,filename
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
componentDidUpdate=(preProps)=>{
|
||||
const { descName } = this.props;
|
||||
const { descName ,empty,filename} = this.props;
|
||||
const nowempty= this.state.empty;
|
||||
const nowfilename = this.state.filename;
|
||||
if(preProps && descName && preProps.descName !== descName ){
|
||||
this.props.form.setFieldsValue({
|
||||
desc:descName
|
||||
})
|
||||
}
|
||||
if(empty !== undefined && empty !== nowempty){
|
||||
this.setState({
|
||||
empty
|
||||
})
|
||||
}
|
||||
if(filename !== "" && filename!==nowfilename){
|
||||
this.setState({
|
||||
filename
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
changeSubmittype = (e) => {
|
||||
|
|
@ -43,25 +61,31 @@ class UserSubmitComponent extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
// 命名文件
|
||||
changeFileName = (e) => {
|
||||
onEmpty=(value)=>{
|
||||
this.setState({
|
||||
filename: e.target.value,
|
||||
});
|
||||
};
|
||||
empty:value
|
||||
})
|
||||
}
|
||||
|
||||
// 提交变更
|
||||
subMitFrom = () => {
|
||||
const { filepath, content, editor_type , checkName} = this.props;
|
||||
const { filepath, content, editor_type , checkName , onEmpty} = this.props;
|
||||
const { branch, projectsId , owner } = this.props.match.params;
|
||||
const { submitType, filename } = this.state;
|
||||
const { submitType, filename , empty } = this.state;
|
||||
if(!filename || empty){
|
||||
onEmpty ? onEmpty(true) : this.onEmpty(true);
|
||||
onEmpty && window.scrollTo(0,0);
|
||||
return;
|
||||
}
|
||||
|
||||
let path = filepath + filename;
|
||||
this.setState({ isSpin: true });
|
||||
let path = editor_type === "upload" ? filepath : filepath.substr(1);
|
||||
this.props.form.validateFieldsAndScroll((err, values) => {
|
||||
if (!err && !checkName) {
|
||||
const url = `/${owner}/${projectsId}/create_file.json`;
|
||||
axios.post(url, {
|
||||
filepath: filename ? filename : path,
|
||||
filepath: path.substr(1),
|
||||
base64_filepath:Base64.encode(path.substr(1)),
|
||||
branch: returnbar(branch),
|
||||
new_branch: submitType === "1" ? values.branchname : undefined,
|
||||
content:editor_type === "upload" ? content : Base64.encode(content),
|
||||
|
|
@ -75,6 +99,7 @@ class UserSubmitComponent extends Component {
|
|||
const { getDetail } = this.props;
|
||||
getDetail && getDetail();
|
||||
}
|
||||
window.scrollTo(0,0);
|
||||
let url = `/${owner}/${projectsId}${values.branchname ? `/tree/${turnbar(values.branchname)}`: (branch ? `/tree/${turnbar(branch)}` : "")}`;
|
||||
this.props.history.push(url);
|
||||
}
|
||||
|
|
@ -102,6 +127,7 @@ class UserSubmitComponent extends Component {
|
|||
axios
|
||||
.put(url, {
|
||||
filepath: detail.path,
|
||||
base64_filepath:Base64.encode(detail.path),
|
||||
branch: submitType === "1" ? undefined : returnbar(b),
|
||||
new_branch: submitType === "1" ? values.branchname : undefined,
|
||||
content: content,
|
||||
|
|
@ -112,6 +138,7 @@ class UserSubmitComponent extends Component {
|
|||
this.setState({ isSpin: false });
|
||||
if (result.data && result.data.status === 1) {
|
||||
let b = currentBranch || branch;
|
||||
window.scrollTo(0,0);
|
||||
let url = `/${owner}/${projectsId}${(values.branchname ? `/tree/${turnbar(values.branchname)}` : (b ? `/tree/${turnbar(b)}`:""))}`;
|
||||
this.props.history.push(url);
|
||||
this.props.showNotification("文件修改成功!");
|
||||
|
|
@ -151,13 +178,21 @@ class UserSubmitComponent extends Component {
|
|||
callback();
|
||||
}
|
||||
|
||||
onChangeFile=(value)=>{
|
||||
const { changeName } = this.props;
|
||||
this.setState({
|
||||
filename:value
|
||||
})
|
||||
changeName && changeName(value);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { submitType, filename, isSpin } = this.state;
|
||||
const { submitType, filename, isSpin , empty } = this.state;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
const { branch, projectsId , owner } = this.props.match.params;
|
||||
|
||||
const { current_user, filepath, projectDetail , currentBranch } = this.props;
|
||||
const { current_user, filepath, projectDetail , currentBranch , loadName } = this.props;
|
||||
const { editor_type } = this.props;
|
||||
let b = currentBranch || branch;
|
||||
|
||||
|
|
@ -180,24 +215,17 @@ class UserSubmitComponent extends Component {
|
|||
<div className="userScrew">
|
||||
<div className="screwPanel">
|
||||
<Form>
|
||||
<Form.Item
|
||||
style={{
|
||||
<div className="mb20" style={{
|
||||
display: editor_type === "upload" ? "block" : "none",
|
||||
}}
|
||||
>
|
||||
{getFieldDecorator("path", {
|
||||
rules: [],
|
||||
})(
|
||||
<div className="setInputAddon">
|
||||
<Input
|
||||
addonBefore={`/${ projectDetail && projectDetail.identifier }/`}
|
||||
value={filename || filepath}
|
||||
onChange={this.changeFileName}
|
||||
placeholder="文件路径..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Form.Item>
|
||||
}}>
|
||||
<FP
|
||||
addonBefore={`${ projectDetail && projectDetail.identifier }${returnbar(filepath)}`}
|
||||
onChangeFile={this.onChangeFile}
|
||||
empty={empty}
|
||||
name={loadName}
|
||||
onEmpty={(v)=>this.onEmpty(v)}
|
||||
/>
|
||||
</div>
|
||||
<Form.Item>
|
||||
{getFieldDecorator("desc", {
|
||||
rules: [
|
||||
|
|
@ -267,6 +295,7 @@ class UserSubmitComponent extends Component {
|
|||
type="primary grey"
|
||||
onClick={() => {
|
||||
this.props.history.push(`/${owner}/${projectsId}`);
|
||||
window.scrollTo(0,0);
|
||||
}}
|
||||
className="mr20"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class m_editor extends Component {
|
|||
|
||||
render() {
|
||||
const { editorValue, changeValue } = this.state;
|
||||
const { readOnly, editorType, currentBranch, descName, checkName, detail, match: { params },filepath,ideTheme } = this.props;
|
||||
const { readOnly, editorType,onEmpty,filename,empty, currentBranch, descName, checkName, detail, match: { params },filepath,ideTheme } = this.props;
|
||||
const editor_options = {
|
||||
lineNumbers: "on",
|
||||
lineWrapping: true, //强制换行
|
||||
|
|
@ -82,6 +82,9 @@ class m_editor extends Component {
|
|||
currentBranch={currentBranch}
|
||||
descName={descName}
|
||||
checkName={checkName}
|
||||
onEmpty={onEmpty}
|
||||
empty={empty}
|
||||
filename={filename}
|
||||
></UserSubmitComponent>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -40,12 +40,17 @@ class UploadFile extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
changeName=(value)=>{
|
||||
this.setState({
|
||||
filename:value
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { pathname } = this.props.location;
|
||||
const { filename, editorValue, attachment_clean } = this.state;
|
||||
const urlroot = pathname.split("uploadfile/")[1];
|
||||
const file_path = !urlroot ? filename : `${urlroot}/${filename}`;
|
||||
|
||||
const file_path = urlroot ? `/${urlroot}/` :"/";
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="main">
|
||||
|
|
@ -75,9 +80,11 @@ class UploadFile extends Component {
|
|||
{...this.props}
|
||||
{...this.state}
|
||||
filepath={file_path}
|
||||
loadName={filename}
|
||||
content={editorValue}
|
||||
editor_type={"upload"}
|
||||
descName={`ADD file via upload`}
|
||||
changeName={this.changeName}
|
||||
></UserSubmitComponent>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ function MilepostDetail(props){
|
|||
const[ aboutMe , setAboutMe ] = useState("aboutme");
|
||||
const[ keyword , setKeyword ] = useState(undefined);
|
||||
|
||||
const [ category , setCategory] = useState("all");
|
||||
const [ category , setCategory] = useState("opened");
|
||||
|
||||
const [ allValue , setAllValue ] = useState([]);
|
||||
const [ allIds , setAllIds ] = useState([]);
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ class UpdateMilepost extends Component {
|
|||
}).then(result => {
|
||||
if (result) {
|
||||
this.setState({ isSpin: false })
|
||||
window.scrollTo(0,0);
|
||||
this.props.history.push(`/${owner}/${projectsId}/milestones/${meilid}`);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
.screenWrap{
|
||||
background:rgba(250,250,250,1);
|
||||
border-top:1px solid rgba(221,221,221,1);
|
||||
border-bottom:1px solid rgba(221,221,221,1);
|
||||
background:#fafcff;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
padding-left: 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
}
|
||||
.mergeData{
|
||||
border:1px solid #d0d0d0;
|
||||
border-top: none;
|
||||
border-radius: 0px 0px 2px 2px;
|
||||
}
|
||||
.attachment-list-div:hover {
|
||||
background-color: #e6f7ff;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ class NewMilepost extends Component {
|
|||
}).then(result => {
|
||||
if (result) {
|
||||
this.setState({ isSpin: false })
|
||||
window.scrollTo(0,0);
|
||||
this.props.history.push(`/${owner}/${projectsId}/milestones`);
|
||||
const { getDetail } = this.props;
|
||||
getDetail && getDetail();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,22 @@ const PrivateLetter = Loadable({
|
|||
loader: () => import("./notice/privateLetter/Index"),
|
||||
loading: Loading,
|
||||
});
|
||||
const ExploitBot = Loadable({
|
||||
loader: () => import("./bot/exploit"),
|
||||
loading: Loading,
|
||||
});
|
||||
const CreateBot = Loadable({
|
||||
loader: () => import("./bot/exploit/new"),
|
||||
loading: Loading,
|
||||
});
|
||||
const DispositionDetail = Loadable({
|
||||
loader: () => import("./bot/disposition/detail"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Disposition = Loadable({
|
||||
loader: () => import("./bot/disposition"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
function Index(props){
|
||||
const { current_user,mygetHelmetapi , checkIfLogin } = props;
|
||||
|
|
@ -54,7 +70,7 @@ function Index(props){
|
|||
const notice_url = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice;
|
||||
|
||||
const [ avatarVisible , setAvatarVisible ] = useState(false);
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(checkIfLogin() === false){
|
||||
props.history.push('/login');
|
||||
|
|
@ -70,8 +86,8 @@ function Index(props){
|
|||
return(
|
||||
<div className="newMain clearfix whiteBack">
|
||||
{
|
||||
avatarVisible &&
|
||||
<Avatar
|
||||
avatarVisible &&
|
||||
<Avatar
|
||||
onCancel={onCancelAvatar}
|
||||
avatarImg={getImageUrl(`/${current_user && current_user.image_url}`)}
|
||||
login={current_user && current_user.login}
|
||||
|
|
@ -94,13 +110,18 @@ function Index(props){
|
|||
</ul>
|
||||
{notice_url && <ul className="securityUl ul-border-buttom">
|
||||
<li>消息通知</li>
|
||||
<li className={(pathname.indexOf("/settings/notice")>-1 && pathname.indexOf("/settings/notice/config") == -1) || pathname.indexOf("/settings/notice/privateLetter")>-1 ?"active":""}><Link to={"/settings/notice"}><i className="iconfont icon-wodetongzhi"></i><span className="text-shodow-bold">我的通知</span></Link></li>
|
||||
<li className={pathname.indexOf("/settings/notice/config")>-1 ?"active":""}><Link to={'/settings/notice/config'}><i className="iconfont icon-tongzhiguanli"></i><span className="text-shodow-bold">通知管理</span></Link></li>
|
||||
<li className={(pathname.indexOf("/settings/notice")>-1 && pathname.indexOf("/settings/notice/config") == -1) || pathname.indexOf("/settings/notice/privateLetter")>-1 ?"active":""}><Link to={"/settings/notice"}><i className="iconfont icon-wodetongzhi font-14"></i><span className="text-shodow-bold">我的通知</span></Link></li>
|
||||
<li className={pathname.indexOf("/settings/notice/config")>-1 ?"active":""}><Link to={'/settings/notice/config'}><i className="iconfont icon-tongzhiguanli font-14"></i><span className="text-shodow-bold">通知管理</span></Link></li>
|
||||
</ul>}
|
||||
<ul className="securityUl">
|
||||
<ul className="securityUl ul-border-buttom">
|
||||
<li>安全设置</li>
|
||||
<li className={pathname.indexOf("/settings/SSH")>-1 ?"active":""}><Link to={`/settings/SSH`}><i className="iconfont icon-xuanzhongssh_icon mr5 font-14"></i><span className="text-shodow-bold">SSH密钥</span></Link></li>
|
||||
</ul>
|
||||
<ul className="securityUl">
|
||||
<li>Bot开发</li>
|
||||
<li className={pathname.indexOf("/settings/mybot")>-1 ?"active":""}><Link to={`/settings/mybot`}><i className="iconfont icon-kaifabot mr5 font-18"></i><span className="text-shodow-bold">我的Bot</span></Link></li>
|
||||
<li className={pathname.indexOf("/settings/installbot")>-1 ?"active":""}><Link to={`/settings/installbot`}><i className="iconfont icon-BOTpeizhi mr5 font-17"></i><span className="text-shodow-bold">Bot安装</span></Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<LongWidth>
|
||||
<Gap>
|
||||
|
|
@ -165,6 +186,34 @@ function Index(props){
|
|||
<SSHIndex {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
{/* 创建一个新的bot */}
|
||||
<Route
|
||||
path="/settings/mybot/new"
|
||||
render={(p) => (
|
||||
<CreateBot {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
{/* 开发bot */}
|
||||
<Route
|
||||
path="/settings/mybot"
|
||||
render={(p) => (
|
||||
<ExploitBot {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
{/* bot配置页面 */}
|
||||
<Route
|
||||
path="/settings/installbot/:id"
|
||||
render={(p) => (
|
||||
<DispositionDetail {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
{/* bot配置 */}
|
||||
<Route
|
||||
path="/settings/installbot"
|
||||
render={(p) => (
|
||||
<Disposition {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
</Switch>
|
||||
</Gap>
|
||||
</LongWidth>
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@
|
|||
padding:30px 0px 10px;
|
||||
.shortW{
|
||||
width: 198px;
|
||||
border: 1px solid rgba(153, 153, 153, 0.22);
|
||||
border: 1px solid rgba(79, 108, 188, 0.21);
|
||||
border-radius: 4px;
|
||||
// min-height: 400px;
|
||||
margin-bottom: 30px;
|
||||
.userDetail{
|
||||
background: rgba(153, 153, 153, 0.05);
|
||||
background: rgba(124, 146, 230, 0.05);
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
padding:20px 25px;
|
||||
text-align: center;
|
||||
|
|
@ -32,52 +32,40 @@
|
|||
}
|
||||
}
|
||||
.securityUl{
|
||||
padding-left: 17px;
|
||||
color: #333;
|
||||
margin-bottom: 0px;
|
||||
padding-bottom: 12px;
|
||||
padding-top: 5px;
|
||||
li{
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
|
||||
padding-left: 17px;
|
||||
font-weight:500;
|
||||
color:#142a56;
|
||||
font-size:16px;
|
||||
a{
|
||||
color: #666;
|
||||
&:hover{
|
||||
color: #333;
|
||||
}
|
||||
color:#202d40;
|
||||
font-size: 14px;
|
||||
}
|
||||
&.active a{
|
||||
color: #333;
|
||||
.text-shodow-bold{
|
||||
font-weight: 400;
|
||||
text-shadow: 0.5px 0 #333;
|
||||
}
|
||||
&:hover{
|
||||
background-color:rgba(70, 106, 255, 0.05);
|
||||
}
|
||||
&.active::before{
|
||||
position: absolute;
|
||||
left: -18px;
|
||||
top:6px;
|
||||
height: 15px;
|
||||
width: 3px;
|
||||
border-radius: 2px;
|
||||
content: "";
|
||||
background-color: #466AFF;
|
||||
&:first-child:hover{
|
||||
background-color: white;
|
||||
}
|
||||
&.active{
|
||||
background-color:rgba(70, 106, 255, 0.05);
|
||||
a{color: $primary-color;}
|
||||
}
|
||||
|
||||
i{
|
||||
font-size: 14px !important;
|
||||
// font-size: 14px !important;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ul-border-buttom{
|
||||
border-bottom: 1px solid rgba(153, 153, 153, 0.22);
|
||||
border-bottom: 1px solid #eef3ff;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
|
@ -220,4 +208,96 @@
|
|||
display: block!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dangerBorBut, .dangerBorBut:focus{
|
||||
background-color:rgba(196, 0, 14, 0.09);
|
||||
border:1px solid #f60011;
|
||||
border-radius:5px;
|
||||
color:#f60011;
|
||||
&::after{display: none;}
|
||||
&:hover{
|
||||
background-color:rgba(196, 0, 14, 0.18);
|
||||
border:1px solid #ff727c;
|
||||
color:#f60011;
|
||||
}
|
||||
&:active{
|
||||
background-color:rgba(196, 0, 14, 0.22);
|
||||
border:1px solid #f60011;
|
||||
color:#f60011;
|
||||
}
|
||||
}
|
||||
.themeCorBorBut, .themeCorBorBut:focus{
|
||||
border:1px solid $primary-color;
|
||||
border-radius:5px;
|
||||
color: $primary-color;
|
||||
&::after{display: none;}
|
||||
&:hover{
|
||||
border:1px solid $primary-color-hover;
|
||||
}
|
||||
&:active{
|
||||
border:1px solid $primary-color-active;
|
||||
}
|
||||
}
|
||||
.grayBorBut, .grayBorBut:focus{
|
||||
background-color:#fbfbfb;
|
||||
border:1px solid #d5dbec;
|
||||
border-radius:5px;
|
||||
color:#333333;
|
||||
&::after{display: none;}
|
||||
&:hover{
|
||||
background-color:#edf0f6;
|
||||
border:1px solid #adb8d6;
|
||||
color:#333333;
|
||||
}
|
||||
&:active{
|
||||
background-color:#e7edfb;
|
||||
border:1px solid #adb8d6;
|
||||
color:#333333;
|
||||
}
|
||||
&.redFontBut, &.redFontBut:hover, &.redFontBut:active, &.redFontBut:focus{
|
||||
color:#f60011;
|
||||
}
|
||||
&.whiteBackBut, &.whiteBackBut:hover, &.whiteBackBut:active, &.whiteBackBut:focus{
|
||||
background-color:#ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.f8HeadModal{
|
||||
color:#333333;
|
||||
.ant-modal-content{border-radius: 0;}
|
||||
.ant-modal-header{
|
||||
height: 56px;
|
||||
padding: 10px 30px;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.ant-modal-title{
|
||||
text-align: left;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
.ant-modal-close-x{
|
||||
font-size: 25px;
|
||||
line-height: 45px;
|
||||
}
|
||||
.titleTip{margin-left: 45px;}
|
||||
.deleteTip{
|
||||
color: #666666;
|
||||
margin-top: 20px;
|
||||
margin-left: 92px;
|
||||
}
|
||||
.ant-modal-footer{
|
||||
border-top: none;
|
||||
padding: 40px 0 60px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.circleRed{
|
||||
display: inline-block;
|
||||
width:36px;
|
||||
height:36px;
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
background-color:#ca0002;
|
||||
color: white;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Button, Checkbox, message, Radio, Select, Switch, Spin, Tooltip } from "antd";
|
||||
import axios from "axios";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getInstallBot, updateInstallBot } from "../../../../softbot/api";
|
||||
import { getImageUrl } from 'educoder';
|
||||
import './index.scss';
|
||||
|
||||
function DispositionDetail(props){
|
||||
const {match:{params:{id}}, current_user, current_user:{login, user_id}} = props;
|
||||
const [checkValue, setCheckValue] = useState(0);
|
||||
const [selectValue, setSelectValue] = useState([]);
|
||||
const [botDetail, setBotDetail] = useState(undefined);
|
||||
const [options, setOptions] = useState(undefined);
|
||||
const [filterOption, setFilterOption] = useState([]);
|
||||
// bot状态
|
||||
const [botState, setBotState] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
// 获取bot详情
|
||||
useEffect(()=>{
|
||||
// 查询用户创建的仓库信息
|
||||
current_user && axios.get(`/users/${login}/projects.json`,{
|
||||
params:{
|
||||
limit: 1000,
|
||||
page: 1,
|
||||
category:"admin"
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
const {projects} = result.data;
|
||||
setOptions(projects);
|
||||
setFilterOption(projects);
|
||||
// getInstallBot需要根据用户具备管理权限的所有仓库去查询是否安装
|
||||
const repoIds = new Set();
|
||||
projects.map(item=>{
|
||||
const {id} = item;
|
||||
id && repoIds.add(id);
|
||||
})
|
||||
getInsBotDetail(repoIds);
|
||||
}
|
||||
}).catch(error=>{
|
||||
console.log(error);
|
||||
})
|
||||
}, [])
|
||||
|
||||
// 监听已选项selectValue的变化 改变下拉选择框的选择项
|
||||
useEffect(()=>{
|
||||
const ids = selectValue.map(item=>{return item.id});
|
||||
options && setFilterOption(options.filter(item=>!ids.includes(item.id)));
|
||||
}, [selectValue])
|
||||
|
||||
// 比较全部仓库和安装的仓库数组
|
||||
useEffect(()=>{
|
||||
if(botDetail && options){
|
||||
if(botDetail.store_id_list.length === options.length){
|
||||
setCheckValue(0);
|
||||
}else{
|
||||
setCheckValue(1);
|
||||
// 把已选择仓库筛选出来
|
||||
const {store_id_list} = botDetail;
|
||||
const allStoreId = options.map((item)=>{return item.id});
|
||||
let newMap = selectValue;
|
||||
store_id_list.map(item=>{
|
||||
if(allStoreId.includes(item)){
|
||||
newMap = newMap.concat(options.filter(i=>{return i.id === item})[0]);
|
||||
}
|
||||
})
|
||||
setSelectValue(newMap);
|
||||
}
|
||||
}
|
||||
}, [options, botDetail])
|
||||
|
||||
function getInsBotDetail(repoIds){
|
||||
getInstallBot({
|
||||
login: login,
|
||||
user_id: user_id,
|
||||
bot_id: id,
|
||||
repoIds: [...repoIds].toString()
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
const {state, market_name} = res.data;
|
||||
document.title = `${market_name}-Bot配置`
|
||||
setBotState(state);
|
||||
setBotDetail(res.data);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function selectChange(e){
|
||||
let newMap = selectValue;
|
||||
newMap = newMap.concat(options.filter(i=>{return i.id === e})[0]);
|
||||
setSelectValue(newMap);
|
||||
}
|
||||
|
||||
function deleteSelect(e){
|
||||
let newMap = selectValue;
|
||||
newMap = newMap.filter(item=>{return item.id !== e.id});
|
||||
setSelectValue(newMap);
|
||||
}
|
||||
|
||||
function updateInsBot(){
|
||||
setError(false);
|
||||
if(checkValue && !selectValue.length){
|
||||
setError(true);
|
||||
return;
|
||||
}
|
||||
// 仓库id -> 仓库identifier
|
||||
const repo_map = {};
|
||||
// 仓库id -> 仓库拥有者login
|
||||
const repo_owner_map = {};
|
||||
if(checkValue){
|
||||
selectValue.map(item=>{
|
||||
const {author:{login}} = item;
|
||||
repo_map[item.id+""] = item.identifier;
|
||||
repo_owner_map[item.id+""] = login;
|
||||
});
|
||||
}else{
|
||||
options.map(item=>{
|
||||
const {author:{login}} = item;
|
||||
repo_map[item.id+""] = item.identifier;
|
||||
repo_owner_map[item.id+""] = login;
|
||||
});
|
||||
}
|
||||
current_user && updateInstallBot({
|
||||
bot_id: id,
|
||||
password: "",
|
||||
state: botState ? 1 : 0,
|
||||
store_list: checkValue ? selectValue.map(item=>{return item.id}) : options.map(item=>{return item.id}),
|
||||
login: login,
|
||||
user_id: user_id,
|
||||
repo_map,
|
||||
repo_owner_map,
|
||||
installed_store_list: botDetail.store_id_list
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setSelectValue([]);
|
||||
const repoIds = new Set();
|
||||
options.map(item=>{
|
||||
const {id} = item;
|
||||
id && repoIds.add(id);
|
||||
})
|
||||
getInsBotDetail(repoIds);
|
||||
message.success('更改成功');
|
||||
}else{
|
||||
message.error(res.message);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return <Spin spinning={!Boolean(botDetail)}>
|
||||
<div className="dispositionDetailBox">
|
||||
<div className="oneLine mb30">
|
||||
{botDetail && <div className="oneLine">
|
||||
<img src={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></div>
|
||||
<a className="mr30 color88" href={`/${botDetail.register_login}`}>{botDetail && botDetail.register_name}</a>
|
||||
<span className="color88">{botDetail && botDetail.create_time}</span>
|
||||
</div>
|
||||
</div>}
|
||||
<Button style={{width: '53px', padding: '0'}} className="grayBorBut"><Link to={`/settings/installbot`}>返回</Link></Button>
|
||||
</div>
|
||||
{botDetail && <div className="mb10 greenCol font-16"><i className={`mr8 iconfont ${botDetail.limit_vo.juris_diction_code !== 2 ? 'icon-wanchenggouxuan' : 'icon-tishi1 font-16'}`}></i>代码库{botDetail.limit_vo.juris_diction_code === 2 ? '无' : botDetail.limit_vo.juris_diction_code === 1 ? '读写' : '只读'}权限</div>}
|
||||
{botDetail && <div className="mb20 greenCol font-16"><i className={`mr8 iconfont ${botDetail.limit_vo.juris_diction_pr !== 2 ? 'icon-wanchenggouxuan' : 'icon-tishi1 font-16'}`}></i>合并请求(PR){botDetail.limit_vo.juris_diction_pr === 2 ? '无' : botDetail.limit_vo.juris_diction_pr === 1 ? '读写' : '只读'}权限</div>}
|
||||
<div className="detailTil font-16">更改安装位置</div>
|
||||
<div>
|
||||
<Radio.Group onChange={(e)=>{setCheckValue(e.target.value)}} value={checkValue} disabled={botDetail && !botDetail.is_public && login !== botDetail.register_login}>
|
||||
<Radio value={0} className="radioBox mt10 font-16">安装到所有仓库<br/><span className="font-15 radioBoxTip">安装到用户拥有的所有仓库中</span></Radio>
|
||||
<Radio value={1} className="radioBox mt15 font-16">安装到指定仓库<br/><span className={`font-15 radioBoxTip ${error ? 'errorBot' : ''}`}>请至少选择一个仓库</span></Radio>
|
||||
</Radio.Group>
|
||||
{checkValue === 1 && <Select
|
||||
placeholder="请选择仓库名称"
|
||||
value={selectValue.map(item=>{return item.id})}
|
||||
onChange={selectChange}
|
||||
className="radioBox selectBox"
|
||||
disabled={botDetail && !botDetail.is_public && login !== botDetail.register_login}
|
||||
>
|
||||
{filterOption.map(item=>(<Select.Option key={item.id} value={item.id}>{`${item.author && item.author.name}/${item.name}`}</Select.Option>))}
|
||||
</Select>}
|
||||
{checkValue === 1 && selectValue.map((item, index)=>{return <div key={index} className="selectValueBox font-14">
|
||||
<i className="iconfont icon-daimakuicon1 font-14 mr5"></i>
|
||||
<Link className="selectValue" to={`/${item.author && item.author.login}/${item.identifier}`}>{`${item.author && item.author.name}/${item.name}`}</Link>
|
||||
{(botDetail && botDetail.is_public === 1 || (botDetail.is_public === 0 && login === botDetail.register_login)) && <i className="iconfont icon-guanbi font-12 close ml30" onClick={()=>{deleteSelect(item)}}></i>}
|
||||
</div>})}
|
||||
</div>
|
||||
<div className="detailTil font-16 mt30 mb10">更改Bot状态</div>
|
||||
<div className="ml25">
|
||||
{botDetail && <Switch defaultChecked = {botDetail.state} onChange={(checked)=>{setBotState(checked)}} disabled={botDetail && !botDetail.is_public && login !== botDetail.register_login}/>}
|
||||
<div className="mt10">
|
||||
<div className="font-15">{botState ? '启用' : '挂起'}</div>
|
||||
<span className='changeStatusTip'>{botState ? '开始使用该Bot,授予该Bot所需的仓库访问权限' : '暂停停止使用该bot,阻塞该bot拥有的仓库访问权限'}</span>
|
||||
</div>
|
||||
</div>
|
||||
{botDetail && !botDetail.is_public && login !== botDetail.register_login ? <Tooltip title="该bot为私有bot,仅bot开发者有权限更改"><Button type="primary" style={{width: '100px', height: '36px'}} className="mt30" disabled>保存</Button></Tooltip> : <Button type="primary" style={{width: '100px', height: '36px'}} className="mt30" onClick={updateInsBot}>保存</Button>}
|
||||
</div>
|
||||
</Spin>
|
||||
}
|
||||
export default DispositionDetail;
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Button, message, Modal } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getImageUrl } from 'educoder';
|
||||
import nullBot from '../../../../softbot/image/notBot.png';
|
||||
import { getAllInstallBots, deleteInstallBot } from "../../../../softbot/api";
|
||||
import './index.scss';
|
||||
import axios from "axios";
|
||||
|
||||
// 配置已安装的bot
|
||||
function DispositionBot(props){
|
||||
const {current_user} = props;
|
||||
const [botList, setBotList] = useState(undefined);
|
||||
const [delBotId, setDelBotId] = useState(undefined);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [options, setOptions] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
window.scrollTo(0,0);
|
||||
document.title='Bot安装';
|
||||
// 查询用户创建的仓库信息
|
||||
current_user && axios.get(`/users/${current_user.login}/projects.json`,{
|
||||
params:{
|
||||
limit: 1000,
|
||||
page: 1,
|
||||
category:"admin"
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
const {projects} = result.data;
|
||||
setOptions(projects);
|
||||
// getAllInstallBots需要根据用户具备管理权限的仓库去查询是否安装
|
||||
const repoIds = new Set();
|
||||
projects.map(item=>{
|
||||
const {id} = item;
|
||||
id && repoIds.add(id);
|
||||
})
|
||||
getAllInsBots(repoIds)
|
||||
}
|
||||
}).catch(error=>{
|
||||
console.log(error);
|
||||
})
|
||||
}, [])
|
||||
|
||||
function getAllInsBots(repoIds){
|
||||
getAllInstallBots({
|
||||
user_id: current_user && current_user.user_id,
|
||||
repoIds: [...repoIds].toString()
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setBotList(res.data.install_bots);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function delInstallBot(){
|
||||
if(current_user && delBotId){
|
||||
const repoIds = new Set();
|
||||
options.map(item=>{
|
||||
const {id} = item;
|
||||
id && repoIds.add(id);
|
||||
})
|
||||
deleteInstallBot({
|
||||
login: current_user.login,
|
||||
user_id: current_user.user_id,
|
||||
bot_id: delBotId.bot_id,
|
||||
store_repo_ids: delBotId.store_repo_ids,
|
||||
password: ""
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success('卸载成功');
|
||||
setDelBotId(undefined);
|
||||
setVisible(false);
|
||||
getAllInsBots(repoIds);
|
||||
}else{
|
||||
message.error(res.message);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div className="dispositionHead font-18">Bot安装</div>
|
||||
{/* bot列表显示区域 */}
|
||||
{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"/>
|
||||
<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>
|
||||
<div>
|
||||
<Button className="themeCorBorBut" style={{width: '68px', height: '36px'}}><Link to={`/settings/installbot/${item.bot_id}`}>配置</Link></Button>
|
||||
<Button className="dangerBorBut ml20" style={{width: '68px', height: '36px'}} onClick={()=>{setDelBotId(item);setVisible(true)}}>卸载</Button>
|
||||
</div>
|
||||
</div>})}
|
||||
</div> : <div className="nullBotsBox mt80">
|
||||
<img src={nullBot} alt="" width={62}/>
|
||||
<p className="font-18 showBigTip">您当前暂未安装任何Bot</p>
|
||||
<div className="showTip font-14">您可以将Bot配置到仓库来处理一些繁杂的项目任务,比如自动化关闭疑修、合并请求等,以节约时间精力提升开发效率。快去<Link to="/softbot"> Bot市场 </Link>中挑选一个适合您项目的Bot吧!</div>
|
||||
<div className="borTip"></div>
|
||||
<Button type="primary" style={{width: '112px', height: '36px', padding: 0}}><Link to={'/softbot'}>去Bot市场逛逛</Link></Button>
|
||||
</div>)}
|
||||
<Modal
|
||||
className="f8HeadModal"
|
||||
width="485px"
|
||||
title="卸载Bot"
|
||||
visible={visible}
|
||||
maskClosable={false}
|
||||
footer={[
|
||||
<Button onClick={()=>{setVisible(false)}} className="mr30 grayBorBut whiteBackBut" style={{width:"104px", height: '36px'}}>取消</Button>,
|
||||
<Button className="redFontBut grayBorBut whiteBackBut" style={{width:"104px", height: '36px'}} onClick={delInstallBot}>确认卸载</Button>
|
||||
]}
|
||||
onCancel={()=>{setVisible(false)}}
|
||||
>
|
||||
<div className="font-16 titleTip mt20"><span className="circleRed font-18">!</span>您确定要卸载此Bot吗?</div>
|
||||
<div className="deleteTip">卸载后,将回收所有分配给该bot的权限</div>
|
||||
</Modal>
|
||||
</div>
|
||||
}
|
||||
export default DispositionBot;
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
.disBotItem, .oneLine{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.dispositionHead{
|
||||
height: 65px;
|
||||
line-height: 65px;
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
color:#151d40;
|
||||
}
|
||||
.disBotItem{
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
padding: 20px 0 15px;
|
||||
.botNameLeft{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 80%;
|
||||
}
|
||||
.botName{
|
||||
display: inline-block;
|
||||
max-width: 80%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.setBotListBox{
|
||||
margin-top: 16px;
|
||||
color:#202d40;
|
||||
.statusBox{
|
||||
background-color:rgba(255, 81, 81, 0.08);
|
||||
border:1px solid#f23737;
|
||||
color:#f92d2d;
|
||||
border-radius:4px;
|
||||
padding: 4px 6px;
|
||||
line-height: 11px;
|
||||
&.active{
|
||||
background-color:rgba(58, 213, 115, 0.08);
|
||||
border-color:#25c589;
|
||||
color:#25c589;
|
||||
}
|
||||
}
|
||||
}
|
||||
.imgBox{
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.dispositionDetailBox{
|
||||
color:#202d40;
|
||||
.color88{
|
||||
color:#8893af;
|
||||
}
|
||||
a.color88:hover{color: $primary-color;}
|
||||
.botImg{
|
||||
width: 86px;
|
||||
height: 86px;
|
||||
}
|
||||
.greenCol{
|
||||
color:#202d40;
|
||||
&>.icon-tishi1{
|
||||
color:#f60011;
|
||||
}
|
||||
&>.icon-wanchenggouxuan{
|
||||
color:#00bb75;
|
||||
}
|
||||
}
|
||||
.detailTil{
|
||||
font-weight: 700;
|
||||
color:#151d40;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
}
|
||||
.radioBox{
|
||||
display: block;
|
||||
color:#202d40;
|
||||
.radioBoxTip{
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
margin-left: 24px;
|
||||
color:#99a2af;
|
||||
&.errorBot{color: #f60011;}
|
||||
}
|
||||
}
|
||||
.selectBox{
|
||||
width: 360px;
|
||||
margin: 10px 0 0 24px;
|
||||
.ant-select-selection{padding-top: 0;}
|
||||
.ant-select-selection__placeholder{
|
||||
display: block !important;
|
||||
}
|
||||
.ant-select-selection-selected-value{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.selectValueBox{
|
||||
margin: 13px 0 0px 23px;
|
||||
color:#333333;
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
padding-bottom: 10px;
|
||||
.close{
|
||||
cursor: pointer;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
}
|
||||
.changeStatusTip{color:#99a2af;}
|
||||
}
|
||||
.nullBotsBox{
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
text-align: center;
|
||||
padding: 25px 158px 65px;
|
||||
.showBigTip{
|
||||
color:#333333;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 22px !important;
|
||||
}
|
||||
.showTip{color: #666;}
|
||||
.borTip{
|
||||
width: 82%;
|
||||
margin: 20px auto 18px;
|
||||
border-top: 1px solid rgba(90, 117, 193, 0.23)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
import { AutoComplete, Button, message, Modal } from "antd";
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { getImageUrl } from 'educoder';
|
||||
import './index.scss';
|
||||
import axios from "axios";
|
||||
import { Link } from "react-router-dom";
|
||||
import { deleteBot, transferBot, registerUpdateBot, downMarket, getMarketBotById, cancelTransferBot } from "../../../../../../softbot/api";
|
||||
const { Option } = AutoComplete;
|
||||
|
||||
// 注册一个新的Bot
|
||||
function AdvancedInformation(props){
|
||||
const {match, current_user, match:{params:{id}}, botDetail, setReload} = props;
|
||||
const [ visibleMakeOver, setVisibleMakeOver] = useState(false);
|
||||
const [ newId , setNewId] = useState(undefined);
|
||||
const [ source , setSource ] = useState(undefined);
|
||||
const [ searchKey , setSearchKey ] = useState(undefined);
|
||||
// bot删除0、公开1、私有2
|
||||
const [ type, setType] = useState(undefined);
|
||||
const [ visibleDelete, setVisibleDelete] = useState(false);
|
||||
// 转让bot错误提示
|
||||
const [makeOverErrortTip, setMakeOverErrortTip] = useState(undefined);
|
||||
// 是否已经上架
|
||||
const [isMarket, setIsMarket] = useState(false);
|
||||
// 上下架刷新
|
||||
const [marketReload, setMarketReload] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取bot是否已经上架
|
||||
getMarketBotById({bot_id: id}).then(res=>{
|
||||
res && setIsMarket(res.code === 200);
|
||||
})
|
||||
}, [marketReload])
|
||||
|
||||
useEffect(()=>{
|
||||
getUserList();
|
||||
},[searchKey])
|
||||
|
||||
function getUserList(e){
|
||||
const url = `/users/list.json`;
|
||||
axios.get(url, {
|
||||
params: {
|
||||
search: searchKey,
|
||||
},
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
sourceOptions(result.data.users);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
function changeInputUser(e){
|
||||
setSearchKey(e);
|
||||
};
|
||||
|
||||
// 选择用户
|
||||
function selectInputUser(e, option){
|
||||
setNewId(option.props);
|
||||
setSearchKey(option.props.name);
|
||||
};
|
||||
|
||||
function sourceOptions(userDataSource){
|
||||
const s = userDataSource && userDataSource.map((item, key) => {
|
||||
return (
|
||||
<Option
|
||||
key={key}
|
||||
value={`${item.user_id}`}
|
||||
login={`${item.login}`}
|
||||
name={item.username}
|
||||
>
|
||||
<img
|
||||
className="user_img radius"
|
||||
width="28"
|
||||
height="28"
|
||||
src={getImageUrl(`/${item && item.image_url}`)}
|
||||
alt=""
|
||||
/>
|
||||
<span className="ml10" style={{ verticalAlign: "middle" }}>
|
||||
{item.username}
|
||||
<span className="color-grey ml10">({item.login})</span>
|
||||
</span>
|
||||
</Option>
|
||||
);
|
||||
});
|
||||
setSource(s);
|
||||
}
|
||||
|
||||
// 确定转让点击函数
|
||||
function makeOver(){
|
||||
setMakeOverErrortTip(undefined);
|
||||
if(newId){
|
||||
const {value, login} = newId
|
||||
transferBot({
|
||||
login: current_user && current_user.login,
|
||||
user_id: current_user && current_user.user_id,
|
||||
bot_id: parseInt(id),
|
||||
transfer_to_user_id: parseInt(value),
|
||||
to_logn: login
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success('操作成功');
|
||||
setSearchKey(undefined);
|
||||
setNewId(undefined);
|
||||
setVisibleMakeOver(false);
|
||||
setReload(Math.random());
|
||||
}
|
||||
})
|
||||
}else{
|
||||
setMakeOverErrortTip("请选择被转让者");
|
||||
}
|
||||
}
|
||||
|
||||
// 删除/公开/私有/下架Bot弹框
|
||||
function submitModal(){
|
||||
if(type === '3'){
|
||||
// 下架市场
|
||||
downMarket(parseInt(id), {}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success('下架成功');
|
||||
setVisibleDelete(false);
|
||||
setMarketReload(Math.random());
|
||||
}
|
||||
})
|
||||
}else if(type === '0'){
|
||||
// 删除bot
|
||||
const params = {
|
||||
login: current_user && current_user.login,
|
||||
user_id: current_user && current_user.user_id,
|
||||
bot_id: parseInt(id)
|
||||
}
|
||||
deleteBot(params).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success('删除成功');
|
||||
setVisibleDelete(false);
|
||||
props.history.push("/settings/mybot/configuration");
|
||||
}
|
||||
})
|
||||
}else{
|
||||
// 公开/私有bot
|
||||
const params = {
|
||||
...botDetail,
|
||||
bot_id: parseInt(id),
|
||||
is_public: type === '1' ? 1 : 0,
|
||||
user_id: current_user && current_user.user_id,
|
||||
}
|
||||
registerUpdateBot(params).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setVisibleDelete(false);
|
||||
message.success("更改成功");
|
||||
setReload(Math.random());
|
||||
}else{
|
||||
setVisibleDelete(false);
|
||||
message.error(res.message);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function cancelTra(){
|
||||
current_user && cancelTransferBot({
|
||||
bot_id: parseInt(id),
|
||||
login: current_user.login,
|
||||
user_id: current_user.user_id
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success("操作成功");
|
||||
setReload(Math.random());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="advancedInformationBox">
|
||||
<div className="informationHead pb15">高级选项</div>
|
||||
{/* 提示:转让中状态 */}
|
||||
{botDetail && botDetail.is_transfer_success === 2 && <div className="transferBotStatus mt15"><i className="iconfont icon-yuyue-lishi-shijian font-18 mr5 ml10"></i>此Bot正在等待转移至<a href={`/${botDetail.transfer_to_login}`}>{botDetail.transfer_to_name +' (' + botDetail.transfer_to_login +')'}</a></div>}
|
||||
<div className="disFlex itemBox">
|
||||
<div className="font-15">
|
||||
转让该Bot的所有权至其他用户
|
||||
<p className="grayBox font-14">转让可能会推迟,直到新的所有者批准转让</p>
|
||||
</div>
|
||||
{botDetail && (botDetail.is_transfer_success !== 2 ? <Button className="themeCorBorBut" style={{width:"88px", height: '36px', padding: '0'}} onClick={()=>{setVisibleMakeOver(true)}}>转让所有权</Button> :
|
||||
<Button className="themeCorBorBut" style={{width:"88px", height: '36px', padding: '0'}} onClick={cancelTra}>取消转让</Button>)}
|
||||
</div>
|
||||
<div className="disFlex itemBox">
|
||||
<div className="font-15">
|
||||
删除Bot
|
||||
<p className="grayBox font-14">该操作无法撤销,请慎重</p>
|
||||
</div>
|
||||
<Button className="dangerBorBut" style={{width:"88px", height: '36px', padding: '0'}} onClick={()=>{setType('0');setVisibleDelete(true)}}>删除Bot</Button>
|
||||
</div>
|
||||
{botDetail && !botDetail.is_public && <div className="disFlex itemBox">
|
||||
<div className="font-15">
|
||||
私有Bot
|
||||
<p className="grayBox font-14">目前您的bot将不能被其他用户安装</p>
|
||||
</div>
|
||||
<Button className="themeCorBorBut" style={{width:"88px", height: '36px', padding: '0'}} onClick={()=>{setType('1');setVisibleDelete(true)}}>公开此Bot</Button>
|
||||
</div>}
|
||||
{botDetail && !!botDetail.is_public && !isMarket && <div className="disFlex itemBox">
|
||||
<div className="font-15">
|
||||
公开Bot
|
||||
<p className="grayBox font-14">目前您的Bot所有用户都能安装</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button type="primary" style={{width:"88px", height: '36px', padding: '0'}} className="mr20"><Link to={`/settings/mybot/configuration/${id}/advanced/putaway`}>上架市场</Link></Button>
|
||||
<Button className="themeCorBorBut" style={{width:"88px", height: '36px', padding: '0'}} onClick={()=>{setType('2');setVisibleDelete(true)}}>私有此Bot</Button>
|
||||
</div>
|
||||
</div>}
|
||||
{botDetail && !!botDetail.is_public && isMarket && <div className="disFlex itemBox">
|
||||
<div className="font-15">
|
||||
公开Bot
|
||||
<p className="grayBox font-14">目前您的Bot所有用户都能安装</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button type="primary" style={{width:"88px", height: '36px', padding: '0'}} className="mr20"><Link to={`/settings/mybot/configuration/${id}/advanced/putaway/edit`}>编辑</Link></Button>
|
||||
<Button className="themeCorBorBut" style={{width:"88px", height: '36px', padding: '0'}} onClick={()=>{setType('3');setVisibleDelete(true)}}>下架市场</Button>
|
||||
</div>
|
||||
</div>}
|
||||
{/* 转让bot弹框 */}
|
||||
<Modal
|
||||
className="themeHeadModal"
|
||||
width="430px"
|
||||
title="转让可能会推迟,直到新的所有者批准转让"
|
||||
visible={visibleMakeOver}
|
||||
maskClosable={false}
|
||||
footer={null}
|
||||
onCancel={()=>{setVisibleMakeOver(false);setMakeOverErrortTip(undefined)}}>
|
||||
<div className="font-15 mt5 mb10 makeOverTip">新的所有者账号</div>
|
||||
{makeOverErrortTip && <div className="font-15 mt5 mb10 makeOverErrorTip">{makeOverErrortTip}</div>}
|
||||
<AutoComplete
|
||||
dataSource={source}
|
||||
value={searchKey}
|
||||
style={{ width: '100%' }}
|
||||
onChange={changeInputUser}
|
||||
onSelect={selectInputUser}
|
||||
placeholder="请输入账户名称"
|
||||
allowClear
|
||||
/>
|
||||
<Button type="primary" onClick={makeOver} className="makeOverSubmit" style={{width:"104px", height: '36px'}}>确认转让</Button>
|
||||
</Modal>
|
||||
{/* 删除/公开/私有Bot弹框 */}
|
||||
<Modal
|
||||
className="f8HeadModal"
|
||||
width="550px"
|
||||
title={`${type === '0' ? '删除' : type === '1' ? '公开' : type === '2' ? '私有' : '下架'}Bot`}
|
||||
visible={visibleDelete}
|
||||
maskClosable={false}
|
||||
footer={[
|
||||
<Button onClick={()=>{setVisibleDelete(false)}} className="mr30 grayBorBut whiteBackBut" style={{width:"104px", height: '36px'}}>取消</Button>,
|
||||
<Button className="redFontBut grayBorBut whiteBackBut" style={{width:"104px", height: '36px'}} onClick={submitModal}>确认{type === '0' ? '删除' : type === '1' ? '公开' : type === '2' ? '私有' : '下架'}</Button>
|
||||
]}
|
||||
onCancel={()=>{setVisibleDelete(false)}}
|
||||
>
|
||||
<div className="font-16 titleTip mt20"><span className="circleRed font-18">!</span>您确定要{type === '0' ? '删除此Bot' : type === '1' ? `公开` : type === '2' ? '私有' : '下架'}{botDetail && botDetail.bot_name}吗?</div>
|
||||
<div className="deleteTip">{type === '0' ? `该操作无法撤销,将会永久删除${botDetail && botDetail.bot_name}` : type === '1' ? `公开后,任何用户都可以安装此Bot` : type === '2' ? '私有后,此Bot将只能被‘Bot开发者账户’安装' : '下架后,社区用户将无法在市场搜索到此Bot'}</div>
|
||||
</Modal>
|
||||
</div>
|
||||
}
|
||||
export default AdvancedInformation;
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Button, Form, Input, message } from "antd";
|
||||
import MDEditor from '../../../../../../modules/tpm/challengesnew/tpm-md-editor';
|
||||
import { Box } from "../../../../../Component/layout";
|
||||
import miyao from '../../../../img/miyao.png';
|
||||
import './index.scss';
|
||||
import { Link } from "react-router-dom";
|
||||
import { getImageUrl } from 'educoder';
|
||||
import { registerUpdateBot } from "../../../../../../softbot/api";
|
||||
import UploadImage from '../../../../../../forge/Team/Component/UploadImage.jsx';
|
||||
import {CopyToClipboard} from 'react-copy-to-clipboard';
|
||||
// 修改Bot
|
||||
function BasicInformation(props){
|
||||
const {form, botDetail, match:{params:{id}}, setReload, current_user} = props;
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
const [ content , setContent ] = useState(undefined);
|
||||
const [ avatarVisible , setAvatarVisible ] = useState(false);
|
||||
const [ image , setImage ] = useState(undefined);
|
||||
const [ imageFlag , setImageFlag ] = useState(false);
|
||||
const [ imageId, setImageId] = useState(undefined);
|
||||
|
||||
function getImage(image){
|
||||
setImageFlag(true);
|
||||
setImage(image);
|
||||
}
|
||||
|
||||
function getImageId(id){
|
||||
setImageId(id);
|
||||
}
|
||||
|
||||
function onContentChange(value){
|
||||
setContent(value);
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
if(botDetail){
|
||||
setContent(botDetail.bot_des);
|
||||
setImage(botDetail.logo)
|
||||
}
|
||||
},[botDetail])
|
||||
|
||||
// 修改Bot
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
validateFields((err, fieldsValue)=>{
|
||||
if(err){
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
...botDetail,
|
||||
...fieldsValue,
|
||||
bot_id: parseInt(id),
|
||||
user_id: current_user && current_user.user_id,
|
||||
}
|
||||
registerUpdateBot(params).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success("更改成功");
|
||||
setReload(Math.random());
|
||||
}else{
|
||||
message.error(res.message);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 修改头像点击函数
|
||||
function saveUpdateImage(){
|
||||
if(imageId){
|
||||
const params = {
|
||||
...botDetail,
|
||||
bot_id: parseInt(id),
|
||||
logo: '/api/attachments/'+imageId,
|
||||
user_id: current_user && current_user.user_id,
|
||||
}
|
||||
registerUpdateBot(params).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success("成功修改头像");
|
||||
setReload(Math.random());
|
||||
}
|
||||
})
|
||||
}else{
|
||||
message.error('请先上传图片');
|
||||
}
|
||||
}
|
||||
|
||||
// webhooks校验
|
||||
function checkAddr(rule, value, callback) {
|
||||
let reg = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/;
|
||||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if (!reg.test(value)){
|
||||
callback("请输入有效的URL");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
return <Box>
|
||||
{botDetail && <div className="basicInformationBox">
|
||||
<p className="informationHead pb15 mb10">注册信息</p>
|
||||
<div className="labelBox"><span className="label">Bot所有者</span><Link to={`/${current_user.login}`}>{current_user.username}</Link></div>
|
||||
<div className="labelBox"><span className="label">Bot ID</span>{id}</div>
|
||||
<div className="labelBox"><span className="label">客户端ID</span>{botDetail.client_id}</div>
|
||||
<div className="labelBox"><span className="label">主页URL</span><Link to={`/softbot/${id}`} className="blueSpan">{window.location.origin + `/softbot/${id}`}</Link></div>
|
||||
<div className="informationHead disFlex pb15 mt20 mb10">
|
||||
客户端密钥
|
||||
</div>
|
||||
<div className="disFlex miYaoBoxItem">
|
||||
<div className="disFlex">
|
||||
<img src={miyao} alt="" className="imgBox"/>
|
||||
<div className="copyBotInfo">
|
||||
<span className="font-13 oneLineSpan">{botDetail.client_secret}</span>
|
||||
<CopyToClipboard text={botDetail.client_secret} onCopy={()=>{message.success('复制成功')}}>
|
||||
<i className="iconfont icon-fuzhiicon ml5 font-16" style={{color: '#466aff'}}></i>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="informationHead disFlex pb15 mt20 mb10">
|
||||
私钥
|
||||
</div>
|
||||
<div className="disFlex miYaoBoxItem">
|
||||
<div className="disFlex">
|
||||
<img src={miyao} alt="" className="imgBox"/>
|
||||
<div className="copyBotInfo">
|
||||
<span className="font-13 oneLineSpan">{botDetail.private_key}</span>
|
||||
<CopyToClipboard text={botDetail.private_key} onCopy={()=>{message.success('复制成功')}}>
|
||||
<i className="iconfont icon-fuzhiicon ml5 font-16" style={{color: '#466aff'}}></i>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="informationHead mt30 pb15">Bot信息</p>
|
||||
<Form className="basicInformationForm mt10" onSubmit={submit}>
|
||||
<Form.Item label="Bot名称" className="width80 botItem">
|
||||
{getFieldDecorator("bot_name",{
|
||||
rules:[{required:true,message:"请输入Bot名称"}, {min: 3, max: 50, message: "Bot名称3-50个字符"}],
|
||||
initialValue: botDetail.bot_name
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入Bot名称" maxLength={51} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="详细介绍" className="botItem introduce">
|
||||
{getFieldDecorator("bot_des",{
|
||||
rules:[]
|
||||
})(
|
||||
<MDEditor
|
||||
placeholder={"请输入详细介绍"}
|
||||
height={500}
|
||||
mdID={"order-new-description"}
|
||||
initValue={content}
|
||||
onChange={onContentChange}
|
||||
className="mt20"
|
||||
isFocus={false}
|
||||
></MDEditor>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="Webhook地址" className="width80 botItem">
|
||||
{getFieldDecorator("webhook",{
|
||||
rules:[{required:true,message:"请输入Webhook地址"},{validator:checkAddr}],
|
||||
initialValue: botDetail.webhook
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入Webhook地址" maxLength={200} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="回调地址" className="width80 botItem">
|
||||
{getFieldDecorator("oauth_callback_url",{
|
||||
rules:[{required:true,message:"请输入回调地址"},{validator:checkAddr}],
|
||||
initialValue: botDetail.oauth_callback_url
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入回调地址" maxLength={200} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item className="mt30"><Button style={{width:"129px", height: '36px'}} type="primary" htmlType="submit">保存修改</Button></Form.Item>
|
||||
</Form>
|
||||
</div>}
|
||||
<div className="headPortraitBox">
|
||||
<UploadImage url={image && image.startsWith('/api/attachments/') ? getImageUrl(`${image}`) : image} getImage={getImage} getImageId = {getImageId}/>
|
||||
<Button className="themeCorBorBut ml5" onClick={saveUpdateImage}>保存头像</Button>
|
||||
</div>
|
||||
</Box>
|
||||
}
|
||||
export default Form.create()(BasicInformation);
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
.informationHead{
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
font-weight:700;
|
||||
color:#151d40;
|
||||
font-size:16px;
|
||||
}
|
||||
.basicInformationBox{
|
||||
width: 800px;
|
||||
margin-right: 55px;
|
||||
padding-bottom: 50px;
|
||||
.labelBox{
|
||||
line-height: 40px;
|
||||
}
|
||||
.blueSpan{
|
||||
color: $primary-color;
|
||||
}
|
||||
.label{
|
||||
display: inline-block;
|
||||
width: 76px;
|
||||
}
|
||||
.tipBox{
|
||||
background-color:rgba(70, 106, 255, 0.07);
|
||||
border-radius:4px;
|
||||
color:#151d40;
|
||||
padding: 8px 13px;
|
||||
}
|
||||
.imgBox{
|
||||
width: 30px;
|
||||
margin-right: 25px;
|
||||
}
|
||||
.grayCol{
|
||||
color:rgba(13, 22, 71, 0.65);
|
||||
}
|
||||
.miYaoBoxItem{
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
&:hover{background-color:rgba(125, 148, 246, 0.05);}
|
||||
.copyBotInfo{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.oneLineSpan{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 680px;
|
||||
}
|
||||
}
|
||||
.basicInformationForm{
|
||||
.width80{
|
||||
width: 80%;
|
||||
}
|
||||
.botItem{
|
||||
margin-bottom: 5px;
|
||||
.ant-form-item-label{
|
||||
line-height: 30px;
|
||||
}
|
||||
&.introduce .ant-form-item-control-wrapper{
|
||||
margin-top: -15px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.jurisdictionBox, .advancedInformationBox{
|
||||
width: 85%;
|
||||
padding-bottom: 45px;
|
||||
}
|
||||
.advancedInformationBox{
|
||||
.grayBox{
|
||||
color:#99a2af;
|
||||
}
|
||||
.itemBox{
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
}
|
||||
.transferBotStatus{
|
||||
background-color:rgba(70, 106, 255, 0.09);
|
||||
border:1px solid $primary-color;
|
||||
border-radius:4px;
|
||||
color:rgba(0, 0, 0, 0.65);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.icon-yuyue-lishi-shijian{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
.themeHeadModal{
|
||||
.ant-modal-header{
|
||||
height: 55px;
|
||||
padding: 8px 25px;
|
||||
background-color:$primary-color;
|
||||
}
|
||||
.ant-modal-title{
|
||||
text-align: left;
|
||||
color:#ffffff;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
.ant-modal-close-x{
|
||||
line-height: 38px;
|
||||
font-size: 18px;
|
||||
color:#ffffff;
|
||||
}
|
||||
.makeOverTip{color:#202d40;}
|
||||
.makeOverSubmit{margin: 70px 145px;}
|
||||
.ant-modal-body{
|
||||
background-image:linear-gradient(42.46deg,#ffffff 0%,#ffffff 48.54%,#ebf0ff 100%);
|
||||
}
|
||||
.makeOverErrorTip{color:#f60011;}
|
||||
}
|
||||
.putawayBox{
|
||||
width: 100%;
|
||||
.putaHead{
|
||||
color:#151d40;
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
}
|
||||
.oneItem.ant-form-item{
|
||||
margin-bottom: 6px;
|
||||
&.introduce .ant-form-item-control-wrapper{
|
||||
margin-top: -15px;
|
||||
}
|
||||
}
|
||||
.selectBox{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.selectOne{
|
||||
width: 48%;
|
||||
}
|
||||
}
|
||||
.putawayBut[disabled]{
|
||||
background-color:rgba(70, 106, 255, 0.56);
|
||||
border: none;
|
||||
color:#ffffff;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
import React from "react";
|
||||
import { Button, Checkbox, Form, Input, message, Select } from "antd";
|
||||
import { useState } from "react";
|
||||
import '../../new/index.scss';
|
||||
import './index.scss';
|
||||
import { registerUpdateBot } from "../../../../../../softbot/api";
|
||||
import { useEffect } from "react";
|
||||
const {Option} = Select;
|
||||
// 修改注册bot的权限
|
||||
function Jurisdiction(props){
|
||||
const {form, botDetail, setReload, match:{params:{id}}, current_user } = props;
|
||||
const {getFieldDecorator, validateFields , setFieldsValue, getFieldsValue } = form;
|
||||
const resource = [{value: 2, name: '无权限'}, {value: 0, name: '只读'}, {value: 1, name: '读写'}];
|
||||
const [codeSelectValue , setCodeSelectValue ] = useState(undefined);
|
||||
const [prSelectValue , setPrSelectValue ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(botDetail){
|
||||
const {limit_and_events:{juris_diction_code, juris_diction_pr}} = botDetail
|
||||
setCodeSelectValue(juris_diction_code);
|
||||
setPrSelectValue(juris_diction_pr);
|
||||
}
|
||||
}, [botDetail])
|
||||
|
||||
function codeControl(e){
|
||||
setCodeSelectValue(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
function prControl(e){
|
||||
setPrSelectValue(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
// 修改注册bot的权限
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
validateFields((err, fieldsValue)=>{
|
||||
if(err){
|
||||
return;
|
||||
}
|
||||
const {juris_diction_code, juris_diction_pr, event_code, event_pr} = fieldsValue;
|
||||
const params = {
|
||||
...botDetail,
|
||||
bot_id: parseInt(id),
|
||||
limit_and_events: {
|
||||
event_code: event_code ? event_code.toString() : "",
|
||||
event_pr: event_pr ? event_pr.toString() : "",
|
||||
juris_diction_code,
|
||||
juris_diction_pr
|
||||
},
|
||||
user_id: current_user && current_user.user_id,
|
||||
}
|
||||
registerUpdateBot(params).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success("更改成功");
|
||||
setReload(Math.random());
|
||||
}else{
|
||||
message.error(res.data)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 校验仓库访问权限
|
||||
function jurisDictionPrValidator(rule, value, callback){
|
||||
const {juris_diction_code} = getFieldsValue();
|
||||
if(juris_diction_code === 2 && value === 2){
|
||||
callback('请至少选择一个权限');
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
// 校验用户必选一个订阅事件
|
||||
function eventValidator(rule, value, callback, type){
|
||||
const {event_code, event_pr} = getFieldsValue();
|
||||
const resourceValue = resource[0].value;
|
||||
const boolPr = (event_code === undefined || (event_code && event_code.length === 0)) && prSelectValue === resourceValue;
|
||||
const boolCode = (event_pr === undefined || (event_pr && event_pr.length === 0)) && codeSelectValue === resourceValue;
|
||||
const bool = prSelectValue !== resourceValue && codeSelectValue !== resourceValue && (event_pr === undefined || (event_pr && event_pr.length === 0)) && (event_code === undefined || (event_code && event_code.length === 0))
|
||||
if(boolPr || boolCode){
|
||||
callback("请至少选择一个订阅");
|
||||
return;
|
||||
}
|
||||
if(bool && type === "pr"){
|
||||
callback("请至少选择一个订阅");
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
return <Form className="createExploitForm jurisdictionBox" onSubmit={submit}>
|
||||
{botDetail && <div>
|
||||
<div className="resourceTitle font-16 pb15 mb10">仓库访问权限</div>
|
||||
<Form.Item label="代码库权限" className="resourceBox botItem dashedBorder">
|
||||
<div className="color-99">代码库git推送、分支的创建与删除</div>
|
||||
{getFieldDecorator("juris_diction_code",{initialValue: botDetail.limit_and_events.juris_diction_code, getValueFromEvent: codeControl})(
|
||||
<Select>
|
||||
{resource.map(item=>{return <Option value={item.value} key={item.value}>{item.name}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="dashedBor"></div>
|
||||
<Form.Item label="合并请求(PR)权限" className="resourceBox botItem">
|
||||
<div className="color-99">合并请求的打开、关闭、编辑、分配</div>
|
||||
{getFieldDecorator("juris_diction_pr",{rules:[{validator: jurisDictionPrValidator}],initialValue: botDetail.limit_and_events.juris_diction_pr, getValueFromEvent: prControl})(
|
||||
<Select>
|
||||
{resource.map(item=>{return <Option value={item.value} key={item.value}>{item.name}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
{(codeSelectValue !== resource[0].value || prSelectValue !== resource[0].value) && <div className="resourceTitle font-16 pb15 mb10">订阅事件</div>}
|
||||
{codeSelectValue !== resource[0].value && <Form.Item label="代码库事件" className="botItem width50 checkBox three dashedBorder">
|
||||
{getFieldDecorator("event_code", {
|
||||
initialValue: botDetail.limit_and_events.event_code ? botDetail.limit_and_events.event_code.split(',').map(item=>item = parseInt(item)) : [],
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback, "code")}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Checkbox value={0}>推送<br/><span className="color-99">git推送到存储库</span></Checkbox>
|
||||
<Checkbox value={1}>创建<br/><span className="color-99">创建分支或标签</span></Checkbox>
|
||||
<Checkbox value={2}>删除<br/><span className="color-99">删除分支或标签</span></Checkbox>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
{(codeSelectValue !== resource[0].value && prSelectValue !== resource[0].value) && <div className="dashedBor"></div>}
|
||||
{prSelectValue !== resource[0].value && <Form.Item label="合并请求事件" className="botItem width50 checkBox">
|
||||
{getFieldDecorator("event_pr", {
|
||||
initialValue: botDetail.limit_and_events.event_pr ? botDetail.limit_and_events.event_pr.split(',').map(item=>item = parseInt(item)) : [],
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback, "pr")}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Checkbox value={3}>合并请求<br/><span className="color-99">合并请求被打开、被关闭、被重新打开或被编辑</span></Checkbox>
|
||||
<Checkbox value={4}>合并请求分配<br/><span className="color-99">合并请求被分配或取消分配</span></Checkbox>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
<Form.Item className="mt30"><Button style={{width:"129px", height: '36px'}} type="primary" htmlType="submit">保存修改</Button></Form.Item></div>}
|
||||
</Form>
|
||||
}
|
||||
export default Form.create()(Jurisdiction);
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
import React, {forwardRef, useState, useEffect} from "react";
|
||||
import { Button, Checkbox, Form, Input, message, Select } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import './index.scss';
|
||||
import MdEditor from "../../../../../../modules/tpm/challengesnew/tpm-md-editor";
|
||||
import { marketBot, getAllBotCategory, getMarketBotById, updateMarketBot } from "../../../../../../softbot/api";
|
||||
import moment from "moment";
|
||||
const {Option} = Select;
|
||||
|
||||
function Putaway(props){
|
||||
const {form, current_user, match:{params:{id}}, botDetail, location:{pathname}} = props;
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
const [ resource, setResource] = useState([]);
|
||||
const [ resourceFirst, setResourceFirst] = useState([]);
|
||||
const [ resourceSecond, setResourceSecond] = useState([]);
|
||||
const [ content , setContent ] = useState(undefined);
|
||||
const [ disabled , setDisabled ] = useState(undefined);
|
||||
const [ detail, setDetail] = useState(undefined);
|
||||
const [ isEdit, setIsEdit] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取分类列表
|
||||
getAllBotCategory().then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setResource(res.data.category_list);
|
||||
setResourceFirst(res.data.category_list);
|
||||
setResourceSecond(res.data.category_list);
|
||||
}
|
||||
})
|
||||
if(pathname && pathname.endsWith("/putaway/edit")){
|
||||
setIsEdit(true);
|
||||
setDisabled(true);
|
||||
getMarketBotById({bot_id: id}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setDetail({
|
||||
...res.data
|
||||
});
|
||||
setContent(res.data.market_intro);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
setIsEdit(false);
|
||||
if(botDetail){
|
||||
const {bot_des, bot_name, webhook} = botDetail;
|
||||
setDetail({
|
||||
bot_id: id,
|
||||
category: "",
|
||||
first_func: undefined,
|
||||
logo: "",
|
||||
market_desc: undefined,
|
||||
market_name: bot_name,
|
||||
market_time: undefined,
|
||||
second_func: undefined,
|
||||
webhook: webhook
|
||||
})
|
||||
setContent(bot_des);
|
||||
}
|
||||
}
|
||||
}, [pathname, botDetail])
|
||||
|
||||
// 监听同意协议
|
||||
function onChange(e){
|
||||
setDisabled(e.target.checked);
|
||||
}
|
||||
|
||||
function onContentChange(value){
|
||||
setContent(value);
|
||||
};
|
||||
|
||||
// 注册新的bot
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
validateFields((err, fieldsValue)=>{
|
||||
if(err){
|
||||
return;
|
||||
}
|
||||
const {logo} = botDetail;
|
||||
if(isEdit){
|
||||
// 更改bot上架信息
|
||||
updateMarketBot({
|
||||
...fieldsValue,
|
||||
category: "",
|
||||
is_receive_ag1: 1,
|
||||
is_receive_ag2: 0,
|
||||
logo: logo,
|
||||
market_time: "",
|
||||
login: current_user && current_user.login,
|
||||
user_id: current_user && current_user.user_id,
|
||||
bot_id: parseInt(id),
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success("修改成功");
|
||||
}else{
|
||||
message.error(res.data);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
// 上架bot
|
||||
marketBot({
|
||||
...fieldsValue,
|
||||
login: current_user && current_user.login,
|
||||
user_id: current_user && current_user.user_id,
|
||||
bot_id: parseInt(id),
|
||||
category: "",
|
||||
is_receive_ag1: disabled ? 1 : 0,
|
||||
is_receive_ag2: 0,
|
||||
logo: logo,
|
||||
market_time: "",
|
||||
}).then(res=>{
|
||||
if(res && res.code === -1){
|
||||
message.error(res.data);
|
||||
}else{
|
||||
message.success("成功上架市场");
|
||||
props.history.push('/softbot/'+id);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="putawayBox">
|
||||
<div className="putaHead pb10 font-18 mb5">
|
||||
<a href="#" className="font-16">高级选项></a><b>上架Bot市场</b>
|
||||
</div>
|
||||
{detail && <Form className="putawayForm" onSubmit={submit}>
|
||||
<Form.Item label="上架Bot市场名称" className="width50 oneItem">
|
||||
{getFieldDecorator("market_name",{
|
||||
rules:[{required:true,message:"请输入上架Bot市场名称"}],
|
||||
initialValue: detail.market_name
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入上架Bot市场名称" maxLength={50} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="简要介绍" className="oneItem">
|
||||
{getFieldDecorator("market_desc",{
|
||||
rules:[{required:true,message:"请输入简要介绍"}],
|
||||
initialValue: detail.market_desc
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入简要介绍的内容" maxLength={200} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="selectBox">
|
||||
<Form.Item label="主要功能" className="selectOne oneItem">
|
||||
{getFieldDecorator("first_func",{
|
||||
rules:[{required:true,message:"请选择主要功能"}],
|
||||
initialValue: detail.first_func
|
||||
})(
|
||||
<Select placeholder="请选择" onChange={(value)=>{setResourceSecond(resource.filter(item => item !== value))}}>
|
||||
{resourceFirst.map(item=>{return <Option value={item} key={item}>{item}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="次要功能" className="selectOne oneItem">
|
||||
{getFieldDecorator("second_func",{
|
||||
initialValue: detail.second_func
|
||||
})(
|
||||
<Select placeholder="请选择" onChange={(value)=>{setResourceFirst(resource.filter(item => item !== value))}}>
|
||||
{resourceSecond.map(item=>{return <Option value={item} key={item}>{item}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item label="详细介绍" className="oneItem introduce">
|
||||
{getFieldDecorator("market_intro",{
|
||||
rules:[{required:true,message:"请输入详细介绍"}],
|
||||
initialValue: content
|
||||
})(
|
||||
<MdEditor
|
||||
placeholder={"请输入详细介绍"}
|
||||
height={500}
|
||||
mdID={"order-new-description"}
|
||||
initValue={content}
|
||||
onChange={onContentChange}
|
||||
className="mt20"
|
||||
imageExpand={false}
|
||||
></MdEditor>
|
||||
)}
|
||||
</Form.Item>
|
||||
{/* 此webhook用于bot上架之后,接收bot安装者的操作记录,由于此功能未上线,所以暂时屏蔽编辑入口 */}
|
||||
{/* <Form.Item label="订阅接收Webhook地址" className="width50 oneItem">
|
||||
{getFieldDecorator("webhook",{
|
||||
rules:[{required:true,message:"请输入Webhook地址"}],
|
||||
initialValue: detail.webhook
|
||||
})(
|
||||
<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>
|
||||
</Form>}
|
||||
</div>
|
||||
}
|
||||
export default Form.create()(forwardRef(Putaway));
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
import React, {useEffect, useState} from "react";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import Loadable from "react-loadable";
|
||||
import Loading from "../../../../../Loading";
|
||||
import { Link } from "react-router-dom";
|
||||
import { TPMIndexHOC } from "../../../../../modules/tpm/TPMIndexHOC";
|
||||
import { Box } from "../../../../Component/layout";
|
||||
import './index.scss';
|
||||
import { Button, message } from "antd";
|
||||
import { getRegisterBot } from "../../../../../softbot/api";
|
||||
|
||||
const BasicInformation = Loadable({
|
||||
loader: () => import("./compenent/basicInformation"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Jurisdiction = Loadable({
|
||||
loader: () => import("./compenent/jurisdiction"),
|
||||
loading: Loading,
|
||||
});
|
||||
const AdvancedInformation = Loadable({
|
||||
loader: () => import("./compenent/advancedInformation"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Putaway = Loadable({
|
||||
loader: () => import("./compenent/putaway"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
// 对自己注册的bot进行配置页面
|
||||
function ConfigurateBot(props){
|
||||
const {match:{params:{id}}, current_user, location:{pathname}, history} = props;
|
||||
const [reload, setReload] = useState(undefined);
|
||||
const [botDetail, setBotDetail] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
getRegisterBot({
|
||||
user_id: current_user && current_user.user_id,
|
||||
bot_id: id
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
const {bot_name} = res.data.bot_output_vo;
|
||||
document.title = `${bot_name}-Bot开发者配置`
|
||||
setBotDetail(res.data.bot_output_vo);
|
||||
}else if(res && res.code === 500){
|
||||
message.error("您没有权限访问该bot");
|
||||
history.push('/settings/mybot/configuration');
|
||||
}
|
||||
})
|
||||
}, [reload])
|
||||
|
||||
return <div className="configurateBotBox">
|
||||
<div className="configurateBotHead mb30">
|
||||
<span className="font-18 configurateBotTitle">Bot开发者配置</span>
|
||||
<Button style={{width: '53px', padding: '0'}} className="grayBorBut"><Link to={`/settings/mybot/configuration`}>返回</Link></Button>
|
||||
</div>
|
||||
<Box>
|
||||
<div className="leftBox mr30">
|
||||
<Link to={`/settings/mybot/configuration/${id}/basic`} className={pathname.lastIndexOf("/jurisdiction") === -1 && pathname.lastIndexOf("/advanced") === -1 ?"active itemBox":"itemBox"}>基本信息</Link>
|
||||
<Link to={`/settings/mybot/configuration/${id}/jurisdiction`} className={pathname.lastIndexOf("/jurisdiction") > -1 ?"active itemBox":"itemBox"}>权限&订阅</Link>
|
||||
<Link to={`/settings/mybot/configuration/${id}/advanced`} className={pathname.lastIndexOf("/advanced") > -1 ?"active itemBox":"itemBox"}>高级选项</Link>
|
||||
</div>
|
||||
<Switch>
|
||||
{/* bot上架市场 */}
|
||||
<Route
|
||||
path="/settings/mybot/configuration/:id/advanced/putaway"
|
||||
render={(p) => (
|
||||
<Putaway {...props} {...p} botDetail={botDetail} setReload={setReload}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/mybot/configuration/:id/advanced/putaway/edit"
|
||||
render={(p) => (
|
||||
<Putaway {...props} {...p} botDetail={botDetail} setReload={setReload}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/mybot/configuration/:id/basic"
|
||||
render={(p)=>(<BasicInformation {...props} {...p} botDetail={botDetail} setReload={setReload}/>)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/mybot/configuration/:id/jurisdiction"
|
||||
render={(p)=>(<Jurisdiction {...props} {...p} botDetail={botDetail} setReload={setReload}/>)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/mybot/configuration/:id/advanced"
|
||||
render={(p)=>(<AdvancedInformation {...props} {...p} botDetail={botDetail} setReload={setReload}/>)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/mybot/configuration/:id"
|
||||
render={(p)=>(<BasicInformation {...props} {...p} botDetail={botDetail} setReload={setReload}/>)}
|
||||
></Route>
|
||||
</Switch>
|
||||
</Box>
|
||||
</div>
|
||||
}
|
||||
export default TPMIndexHOC(ConfigurateBot);
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
.configurateBotBox{
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
color:#202d40;
|
||||
}
|
||||
.configurateBotHead, .disFlex{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.configurateBotHead{
|
||||
padding: 25px 0 15px;
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
.configurateBotTitle{font-weight: bold;}
|
||||
}
|
||||
.leftBox{
|
||||
width: 150px;
|
||||
min-height: 317px;
|
||||
background-color: #f8faff;
|
||||
border-radius: 4px;
|
||||
.itemBox{
|
||||
display: block;
|
||||
color:#202d40;
|
||||
padding: 4px 0 4px 15px;
|
||||
&.active{
|
||||
background-color:rgba(70, 106, 255, 0.06);
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Button, Icon, message, Modal } from "antd";
|
||||
import './index.scss';
|
||||
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 moment from "moment";
|
||||
|
||||
function ExploitBot(props){
|
||||
const {current_user} = props;
|
||||
const [makeOverList, setMakeOverList] = useState([]);
|
||||
const [botList, setBotList] = useState(undefined);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [refuseBotDetail, setRefuseBotDetail] = useState(undefined);
|
||||
const [reload, setReload] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
window.scrollTo(0,0);
|
||||
document.title='我的Bot';
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
getMyBot({
|
||||
user_id: current_user && current_user.user_id,
|
||||
page_no: 1,
|
||||
page_size: 1000
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setBotList(res.data.list);
|
||||
}
|
||||
})
|
||||
getTransferToBot({
|
||||
state: "2",
|
||||
user_id: current_user && current_user.user_id,
|
||||
login: current_user && current_user.login
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setMakeOverList(res.data.bot_list);
|
||||
}
|
||||
})
|
||||
}, [reload])
|
||||
|
||||
// 接受转让的bot
|
||||
function receiveBot(info){
|
||||
receiveTransferBot({
|
||||
bot_id: info.bot_id,
|
||||
transfer_from_id: info.transfer_id,
|
||||
transfer_to_login: current_user && current_user.login,
|
||||
transfer_to_id: current_user && current_user.user_id
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success('操作成功');
|
||||
setReload(Math.random());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 拒绝转让的bot
|
||||
function refuseBot(){
|
||||
refuseTransferBot({
|
||||
bot_id: refuseBotDetail.bot_id,
|
||||
transfer_from_bot: refuseBotDetail.transfer_id,
|
||||
transfer_to_bot: current_user && current_user.user_id
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setVisible(false);
|
||||
setRefuseBotDetail(undefined);
|
||||
message.success('操作成功');
|
||||
setReload(Math.random());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return <div>
|
||||
<div className="exploitHead font-18">
|
||||
<span>我的Bot</span>
|
||||
<Button type="primary" style={{width: '100px', height: '36px'}}><Link to={`/settings/mybot/new`}>Bot注册</Link></Button>
|
||||
</div>
|
||||
|
||||
{/* bot转让接收按钮显示区域 */}
|
||||
{makeOverList.length > 0 && <div className="makeOverBox">
|
||||
{makeOverList.map((item, index)=>{return <div className="makeOverItem" key={index}>
|
||||
<div>
|
||||
<p className="botName font-15">{item.bot_name}</p>
|
||||
<p className="userName"><a href={`/${item.fromlogin}`} className="fromUserNameBot">{item.fromname}</a> {moment(item.transfer_date).format('YYYY-MM-DD HH:mm')}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button className="themeCorBorBut" style={{width: '68px', height: '36px'}} onClick={()=>{receiveBot(item)}}>接收</Button>
|
||||
<Button className="dangerBorBut ml20" style={{width: '68px', height: '36px'}} onClick={()=>{setRefuseBotDetail(item); setVisible(true)}}>拒绝</Button>
|
||||
</div>
|
||||
</div>})}
|
||||
</div>}
|
||||
|
||||
{/* bot列表显示区域 */}
|
||||
{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"/>
|
||||
<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>
|
||||
<Button className="themeCorBorBut ml30" style={{width: '68px', height: '36px'}}><Link to={`/settings/mybot/configuration/${item.bot_id}`}>编辑</Link></Button>
|
||||
</div>})}
|
||||
</div> : <div className="nullBotsBox mt80">
|
||||
<img src={nullBot} alt="" width={62}/>
|
||||
<p className="font-18 showBigTip">您当前暂未注册任何Bot</p>
|
||||
<div className="showTip font-14">Bot可以帮助您自动化处理一些繁杂的项目任务,欢迎您<Link to="/settings/mybot/new"> 注册一个Bot </Link>基于GitLink API进行开发。<br/>此外您还可以在<Link to="/softbot"> Bot市场 </Link>中共享或安装他人的Bot</div>
|
||||
</div>)}
|
||||
|
||||
<Modal
|
||||
className="f8HeadModal"
|
||||
width="550px"
|
||||
title={`拒绝${refuseBotDetail && refuseBotDetail.bot_name}Bot`}
|
||||
visible={visible}
|
||||
maskClosable={false}
|
||||
footer={[
|
||||
<Button onClick={()=>{setVisible(false)}} className="mr30 grayBorBut whiteBackBut" style={{width:"104px", height: '36px'}}>取消</Button>,
|
||||
<Button className="redFontBut grayBorBut whiteBackBut" style={{width:"104px", height: '36px'}} onClick={refuseBot}>确认</Button>
|
||||
]}
|
||||
onCancel={()=>{setVisible(false)}}
|
||||
>
|
||||
<div className="font-16 titleTip mt20"><span className="circleRed font-18">!</span>您确定要拒绝此Bot吗?</div>
|
||||
<div className="deleteTip">拒绝后,开发Bot权限将返回至转让人。</div>
|
||||
</Modal>
|
||||
</div>
|
||||
}
|
||||
export default ExploitBot;
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
.makeOverResultItem, .makeOverItem, .softBotItem{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.exploitHead{
|
||||
height: 65px;
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color:#151d40;
|
||||
}
|
||||
.makeOverResultItem{
|
||||
background-color:rgba(255, 0, 0, 0.04);
|
||||
border:1px solid #f60011;
|
||||
border-radius:4px;
|
||||
margin-top: 16px;
|
||||
padding: 0 15px;
|
||||
height: 32px;
|
||||
color:rgba(0, 0, 0, 0.65);
|
||||
.circle{
|
||||
display: inline-block;
|
||||
width:14px;
|
||||
height:14px;
|
||||
text-align: center;
|
||||
line-height: 14px;
|
||||
border-radius: 50%;
|
||||
background-color:#cc0209;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.makeOverBox{
|
||||
background-color:rgba(59, 105, 255, 0.03);
|
||||
border:1px solid #ebf1ff;
|
||||
margin-top: 16px;
|
||||
padding: 12px 30px 0px 12px;
|
||||
.makeOverItem{
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
padding: 10px 0;
|
||||
color:#99a2af;
|
||||
&:first-child{
|
||||
margin-top: -12px;
|
||||
}
|
||||
.fromUserNameBot{
|
||||
color:#99a2af;
|
||||
&:hover{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
.botName{
|
||||
color:#202d40;
|
||||
}
|
||||
}
|
||||
|
||||
.softBotListBox{
|
||||
margin-top: 16px;
|
||||
color:#202d40;
|
||||
.softBotItem{
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
padding: 20px 0 15px;
|
||||
}
|
||||
.imgBox{
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
.nullBotsBox{
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
text-align: center;
|
||||
padding: 25px 158px 65px;
|
||||
.showBigTip{
|
||||
color:#333333;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 22px !important;
|
||||
}
|
||||
.showTip{color: #666;}
|
||||
}
|
||||
.botOneLine{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
&.botNameWarpBox{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.botNameBox{
|
||||
max-width: 80%;
|
||||
display: inline-block;
|
||||
}
|
||||
.statusBot{
|
||||
border: 1px solid $primary-color;
|
||||
border-radius: 5px;
|
||||
color: $primary-color;
|
||||
padding: 4px 6px 4px;
|
||||
background-color: rgba(70, 106, 255, 0.09);
|
||||
line-height: 12px;
|
||||
&.public{
|
||||
background-color: rgba(58, 213, 115, 0.08);
|
||||
border-color: #25c589;
|
||||
color: #25c589;
|
||||
}
|
||||
&.private{
|
||||
background-color: rgba(230, 126, 34, 0.08);
|
||||
border-color: rgba(230, 126, 34, 1);
|
||||
color: rgba(230, 126, 34, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
import React, { forwardRef, useState, useEffect } from "react";
|
||||
import { Button, Checkbox, Form, Input, message, Radio, Select } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import MDEditor from '../../../../../modules/tpm/challengesnew/tpm-md-editor';
|
||||
import './index.scss';
|
||||
import { registerBot } from "../../../../../softbot/api";
|
||||
const {Option} = Select;
|
||||
|
||||
function Disposition(props){
|
||||
const {form, current_user} = props;
|
||||
const { getFieldDecorator, validateFields , setFieldsValue, getFieldsValue } = form;
|
||||
const resource = [{value: 2, name: '无权限'}, {value: 0, name: '只读'}, {value: 1, name: '读写'}];
|
||||
|
||||
const [ content , setContent ] = useState(undefined);
|
||||
const [ codeSelectValue , setCodeSelectValue ] = useState(resource[0].value);
|
||||
const [ prSelectValue , setPrSelectValue ] = useState(resource[0].value);
|
||||
|
||||
useEffect(()=>{
|
||||
document.title='注册新的Bot';
|
||||
}, [])
|
||||
|
||||
function onContentChange(value){
|
||||
setContent(value);
|
||||
};
|
||||
|
||||
function codeControl(e){
|
||||
setCodeSelectValue(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
function prControl(e){
|
||||
setPrSelectValue(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
// webhooks校验
|
||||
function checkAddr(rule, value, callback) {
|
||||
let reg = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/;
|
||||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if (!reg.test(value)){
|
||||
callback("请输入有效的URL");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
// 校验仓库访问权限
|
||||
function jurisDictionPrValidator(rule, value, callback){
|
||||
const {juris_diction_code} = getFieldsValue();
|
||||
if(juris_diction_code === 2 && value === 2){
|
||||
callback('请至少选择一个权限');
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
// 校验用户必选一个订阅事件
|
||||
function eventValidator(rule, value, callback, type){
|
||||
const {event_code, event_pr} = getFieldsValue();
|
||||
const resourceValue = resource[0].value;
|
||||
const boolPr = (event_code === undefined || (event_code && event_code.length === 0)) && prSelectValue === resourceValue;
|
||||
const boolCode = (event_pr === undefined || (event_pr && event_pr.length === 0)) && codeSelectValue === resourceValue;
|
||||
const bool = prSelectValue !== resourceValue && codeSelectValue !== resourceValue && (event_pr === undefined || (event_pr && event_pr.length === 0)) && (event_code === undefined || (event_code && event_code.length === 0))
|
||||
if(boolPr || boolCode){
|
||||
callback("请至少选择一个订阅");
|
||||
return;
|
||||
}
|
||||
if(bool && type === "pr"){
|
||||
callback("请至少选择一个订阅");
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
// 注册新的bot
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
validateFields((err, fieldsValue)=>{
|
||||
if(err){
|
||||
return;
|
||||
}
|
||||
const {bot_name, bot_des, webhook, is_public, juris_diction_code, juris_diction_pr, event_code, event_pr, oauth_callback_url} = fieldsValue;
|
||||
const param = {
|
||||
bot_name, bot_des, webhook, is_public,
|
||||
login: current_user && current_user.login,
|
||||
user_id: current_user && current_user.user_id,
|
||||
logo: `/api/attachments/${window.location.host !== 'www.gitlink.org.cn' ? 347246 : 412603}`,
|
||||
limit_and_events: {
|
||||
event_code: event_code ? event_code.toString() : "",
|
||||
event_pr: event_pr ? event_pr.toString() : "",
|
||||
juris_diction_code,
|
||||
juris_diction_pr
|
||||
},
|
||||
oauth_callback_url
|
||||
}
|
||||
registerBot(param).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
message.success('注册成功');
|
||||
props.history.push('/settings/mybot/configuration/'+res.data.bot_id);
|
||||
}else{
|
||||
message.success('注册失败: '+res.data);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="createExploitBox">
|
||||
<div className="ceHead pb5 font-16 mb10">
|
||||
<Link to={`/settings/mybot`} className="blue-Purple">我的Bot</Link><span className="ceTitle font-18"><i className="iconfont icon-youjiantou ml5 mr5 font-12"></i>注册新的Bot</span>
|
||||
</div>
|
||||
<Form className="createExploitForm" onSubmit={submit}>
|
||||
<Form.Item label="Bot名称" className="width50 botItem">
|
||||
{getFieldDecorator("bot_name",{
|
||||
rules:[{required:true,message:"请输入Bot名称"}, {min: 3, max: 50, message: "Bot名称3-50个字符"}]
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入Bot名称" maxLength={51} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="详细介绍" className="botItem introduce">
|
||||
{getFieldDecorator("bot_des",{
|
||||
rules:[]
|
||||
})(
|
||||
<MDEditor
|
||||
placeholder={"请输入详细介绍"}
|
||||
height={500}
|
||||
mdID={"order-new-description"}
|
||||
initValue={content}
|
||||
onChange={onContentChange}
|
||||
className="mt20"
|
||||
></MDEditor>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="Webhook地址" className="width50 botItem">
|
||||
{getFieldDecorator("webhook",{
|
||||
rules:[{required:true,message:"请输入Webhook地址"},{validator:checkAddr}]
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入Webhook地址" maxLength={200} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="回调地址" className="width50 botItem">
|
||||
{getFieldDecorator("oauth_callback_url",{
|
||||
rules:[{required:true,message:"请输入回调地址"},{validator:checkAddr}]
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入回调地址" maxLength={200} className="height36" autoSize={true}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="resourceTitle font-16 pb15 mb10">仓库访问权限</div>
|
||||
<Form.Item label="代码库权限" className="resourceBox botItem dashedBorder">
|
||||
<div className="color-99">代码库git推送、分支的创建与删除</div>
|
||||
{getFieldDecorator("juris_diction_code",{initialValue: resource[0].value, getValueFromEvent: codeControl})(
|
||||
<Select>
|
||||
{resource.map(item=>{return <Option value={item.value} key={item.value}>{item.name}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="dashedBor"></div>
|
||||
<Form.Item label="合并请求(PR)权限" className="resourceBox botItem">
|
||||
<div className="color-99">合并请求的打开、关闭、编辑、分配</div>
|
||||
{getFieldDecorator("juris_diction_pr",{rules:[{validator: jurisDictionPrValidator}],initialValue: resource[0].value, getValueFromEvent: prControl})(
|
||||
<Select>
|
||||
{resource.map(item=>{return <Option value={item.value} key={item.value}>{item.name}</Option>})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
{(codeSelectValue !== resource[0].value || prSelectValue !== resource[0].value) && <div className="resourceTitle font-16 pb15 mb10">订阅事件</div>}
|
||||
{codeSelectValue !== resource[0].value && <Form.Item label="代码库事件" className="botItem width50 checkBox three dashedBorder">
|
||||
{getFieldDecorator("event_code",{
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback, "code")}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Checkbox value={0}>推送<br/><span className="color-99">git推送到存储库</span></Checkbox>
|
||||
<Checkbox value={1}>创建<br/><span className="color-99">创建分支或标签</span></Checkbox>
|
||||
<Checkbox value={2}>删除<br/><span className="color-99">删除分支或标签</span></Checkbox>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
{(codeSelectValue !== resource[0].value && prSelectValue !== resource[0].value) && <div className="dashedBor"></div>}
|
||||
{prSelectValue !== resource[0].value && <Form.Item label="合并请求事件" className="botItem width50 checkBox">
|
||||
{getFieldDecorator("event_pr",{
|
||||
rules:[{validator: (rule, value, callback)=>{eventValidator(rule, value, callback, "pr")}}]
|
||||
})(
|
||||
<Checkbox.Group>
|
||||
<Checkbox value={3}>合并请求<br/><span className="color-99">合并请求被打开、被关闭、被重新打开或被编辑</span></Checkbox>
|
||||
<Checkbox value={4}>合并请求分配<br/><span className="color-99">合并请求被分配或取消分配</span></Checkbox>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>}
|
||||
<Form.Item label="公/私有设置" className="botItem resourceTitleItem">
|
||||
{getFieldDecorator("is_public",{initialValue: 0})(
|
||||
<Radio.Group>
|
||||
<Radio value={1}>公开</Radio>
|
||||
<Radio value={0}>私有</Radio>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item><Button style={{width:"129px", height: '36px'}} type="primary" htmlType="submit">注册Bot</Button></Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
}
|
||||
export default Form.create()(forwardRef(Disposition));
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
.ceHead{
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
.ceTitle{
|
||||
font-weight:700;
|
||||
color:#151d40;
|
||||
}
|
||||
}
|
||||
#resource1, #resource2{
|
||||
position: relative;
|
||||
top: -15px;
|
||||
width: 88px;
|
||||
}
|
||||
.resourceBox .ant-form-item-children{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
.resourceBox .ant-select{
|
||||
width: auto;
|
||||
}
|
||||
.resourceBox .has-error .ant-select{
|
||||
color: #f5222d;
|
||||
}
|
||||
.width50{
|
||||
width: 60%;
|
||||
&.checkBox{
|
||||
width: 68%;
|
||||
}
|
||||
.height36{
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
.botItem{
|
||||
.ant-form-item-label{
|
||||
line-height: 30px;
|
||||
}
|
||||
label{
|
||||
color:#202d40;
|
||||
font-size:15px;
|
||||
&.ant-checkbox-wrapper{
|
||||
line-height: 30px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
&::after{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.introduce .ant-form-item-control-wrapper{
|
||||
margin-top: -15px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.checkBox .ant-checkbox-group{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
.color-99{padding-left: 24px;}
|
||||
}
|
||||
&.three .ant-checkbox-wrapper{
|
||||
width: 34%;
|
||||
margin-left: 0px;
|
||||
padding-left: 12px;
|
||||
&:first-child, &:last-child{
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dashedBor{
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.resourceBox + .dashedBor{
|
||||
margin-top: -15px;
|
||||
}
|
||||
.resourceTitle, .resourceTitleItem .ant-form-item-label{
|
||||
font-weight:700;
|
||||
color:#151d40;
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
label{font-size: 16px;}
|
||||
}
|
||||
.resourceTitleItem .ant-form-item-label{padding-bottom: 10px;}
|
||||
.resourceTitleItem .ant-checkbox-wrapper{margin-right: 40px;}
|
||||
.color-99{
|
||||
color:#99a2af;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -44,6 +44,10 @@ const ManageNew = Loadable({
|
|||
loader: () => import("./ManageWebNew"),
|
||||
loading: Loading,
|
||||
});
|
||||
const SoftBot = Loadable({
|
||||
loader: () => import("./softBot"),
|
||||
loading: Loading,
|
||||
});
|
||||
class Index extends Component {
|
||||
render() {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
|
|
@ -107,6 +111,16 @@ class Index extends Component {
|
|||
</Link>
|
||||
</p>
|
||||
</li> */}
|
||||
<li
|
||||
className={pathname.indexOf("settings/softbot") > -1 ? "active" : ""}
|
||||
>
|
||||
<p>
|
||||
<Link to={`/${owner}/${projectsId}/settings/softbot`} className="w-100">
|
||||
<i className="iconfont icon-a-jiqirenzhineng-02 font-18 mr10 color-grey-6"></i>
|
||||
已安装Bot
|
||||
</Link>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
{/* <li
|
||||
className={
|
||||
|
|
@ -182,6 +196,12 @@ class Index extends Component {
|
|||
<Manage {...this.props} {...props} {...this.state} />
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:owner/:projectsId/settings/softbot"
|
||||
render={(props) => (
|
||||
<SoftBot {...this.props} {...props} {...this.state} />
|
||||
)}
|
||||
></Route>
|
||||
{/* 修改仓库信息 */}
|
||||
<Route
|
||||
path="/:owner/:projectsId/settings"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import React, { Component } from "react";
|
||||
import { Form, Input, Checkbox, Select , Spin } from "antd";
|
||||
import { Form, Input, Checkbox, Select , Spin, Button } from "antd";
|
||||
import Title from '../Component/Title';
|
||||
import {WhiteBack} from '../Component/layout';
|
||||
import DivertModal from '../Divert/DivertModal';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from "axios";
|
||||
import "./setting.scss";
|
||||
import { unInstallMarketBot } from "../../softbot/api";
|
||||
const { TextArea } = Input;
|
||||
const { Option } = Select;
|
||||
|
||||
|
|
@ -194,17 +195,26 @@ class Setting extends Component {
|
|||
deleteProject = () => {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const { projectName } = this.state;
|
||||
const {current_user:{login, user_id}, projectDetail:{project_id}} = this.props;
|
||||
this.props.confirm({
|
||||
content: <span style={{display:"block",textAlign:"left"}}>该操作无法撤销!且将会一并删除相关的疑修、合并请求、工作流、里程碑、动态等数据。<br/>是否确认删除 <span style={{fontWeight:"bold"}}>{owner}/{projectName}({projectsId})</span>?</span>,
|
||||
onOk: () => {
|
||||
const url = `/${owner}/${projectsId}.json`;
|
||||
axios.delete(url).then((result) => {
|
||||
this.props.showNotification("仓库删除成功!");
|
||||
this.props.history.push(`/${owner}`);
|
||||
// 卸载bot(判断bot是否安装后端做了判断)
|
||||
unInstallMarketBot({
|
||||
login,
|
||||
user_id,
|
||||
store_id: project_id,
|
||||
store_identifier: projectsId
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
window.scrollTo(0,0);
|
||||
this.props.showNotification("仓库删除成功!");
|
||||
this.props.history.push(`/${owner}`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -400,9 +410,9 @@ class Setting extends Component {
|
|||
</div>
|
||||
{
|
||||
is_transfering ?
|
||||
<a onClick={this.CancelDivertProject} className="red_deleteBtn">取消转移</a>
|
||||
<Button onClick={this.CancelDivertProject} className="red_deleteBtn">取消转移</Button>
|
||||
:
|
||||
<a onClick={this.DivertProject} className="red_deleteBtn">转移</a>
|
||||
<Button onClick={this.DivertProject} className="red_deleteBtn">转移</Button>
|
||||
}
|
||||
</div>
|
||||
<div className="flex-a-center padding15-10">
|
||||
|
|
@ -413,9 +423,9 @@ class Setting extends Component {
|
|||
无法撤消,且删除后,与仓库关联的项目/任务/合并请求/版本发布等,均会被删除
|
||||
</p>
|
||||
</div>
|
||||
<a onClick={this.deleteProject} className="red_deleteBtn">
|
||||
<Button onClick={this.deleteProject} className="red_deleteBtn">
|
||||
删除本仓库
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</WhiteBack>
|
||||
|
|
|
|||
|
|
@ -86,12 +86,13 @@ function Index(props) {
|
|||
}
|
||||
|
||||
function gotoEditWebhook(webhook){
|
||||
if(webhook.isJianMu){
|
||||
setEditId(webhook.id);
|
||||
setEditVisible(true);
|
||||
}else{
|
||||
props.history.push(`/${owner}/${projectsId}/settings/webhooks/${webhook.id}`);
|
||||
}
|
||||
props.history.push(`/${owner}/${projectsId}/settings/webhooks/${webhook.id}`);
|
||||
// if(webhook.isJianMu){
|
||||
// setEditId(webhook.id);
|
||||
// setEditVisible(true);
|
||||
// }else{
|
||||
// props.history.push(`/${owner}/${projectsId}/settings/webhooks/${webhook.id}`);
|
||||
// }
|
||||
}
|
||||
|
||||
return(
|
||||
|
|
@ -104,7 +105,7 @@ function Index(props) {
|
|||
content="您确定要删除此Webhook吗?"
|
||||
subTitle={deleteWebHookModSubTitle}
|
||||
/>
|
||||
<Modal
|
||||
{/* <Modal
|
||||
visible={editVisible}
|
||||
onCancel={()=>{setEditVisible(false)}}
|
||||
title="请谨慎编辑此Webhook"
|
||||
|
|
@ -115,7 +116,7 @@ function Index(props) {
|
|||
>
|
||||
<AlignCenter className="editWebhookModalTitle font-20 mb10"><i className="iconfont icon-shanchu_tc_icon mr10 font-36" style={{color: '#ca0002'}}></i>您确定要编辑此Webhook吗?</AlignCenter>
|
||||
<p className="task-hide-2" style={{WebkitLineClamp:5}}>该Webhook由流水线创建, 编辑此Webhook后, 将大概率导致本仓库相关流水线失效。确定要修改此Webhook?</p>
|
||||
</Modal>
|
||||
</Modal> */}
|
||||
<Banner>
|
||||
<span>Webhooks(网络钩子)</span>
|
||||
<Button type="primary" size="large" onClick={addFunc}>添加Webhook</Button>
|
||||
|
|
@ -128,15 +129,15 @@ function Index(props) {
|
|||
{data.map((i,k)=>{
|
||||
return(
|
||||
<List.Item key={k}>
|
||||
<i className={`iconfont mr12 font-17 ${i.isJianMu ? 'icon-gongzuoliuicon color-grey-6' : 'icon-a-xuanzhongwebhookicon color-grey-d'}`}></i>
|
||||
<i className={`iconfont mr12 font-17 color-grey-6 ${i.isJianMu ? 'icon-gongzuoliuicon' : i.type === "softbot" ? 'icon-a-zu37 font-20' : 'icon-a-xuanzhongwebhookicon'}`}></i>
|
||||
<span className="webName">{i.isJianMu ? <Tooltip title="该Webhook由流水线创建,请勿编辑或删除以防流水线失效"><span className="webName spanBox">{i.url}</span></Tooltip> : i.url}</span>
|
||||
{!i.isJianMu && <span>
|
||||
{!i.isJianMu && i.type !== "softbot" && <span>
|
||||
<Button ghost type={"primary"} onClick={()=>{gotoEditWebhook(i)}}>编辑</Button>
|
||||
<Button ghost className="ml20" type="danger" onClick={()=>{deleteFunc(i)}}>删除</Button>
|
||||
</span>}
|
||||
{i.isJianMu && <span>
|
||||
<Popover content={"该webhook由流水线创建,不可编辑或删除,更改配置将导致流水线失效"} title="无法编辑此Webhook" overlayClassName="disabledButPopover"><Button type={"primary"} onClick={()=>{gotoEditWebhook(i)}} disabled>编辑</Button></Popover>
|
||||
<Popover content={"该webhook由流水线创建,不可编辑或删除,更改配置将导致流水线失效"} title="无法删除此Webhook" overlayClassName="disabledButPopover"><Button className="ml20" type="danger" onClick={()=>{deleteFunc(i)}} disabled>删除</Button></Popover>
|
||||
{(i.isJianMu || i.type === "softbot") && <span>
|
||||
<Popover content={`该webhook由${i.isJianMu ? '流水线创建,不可编辑或删除,更改配置将导致流水线失效' : '已安装的Bot创建,请勿编辑或删除以防Bot失效'}`} title="无法编辑此Webhook" overlayClassName="disabledButPopover"><Button ghost type={"primary"} onClick={()=>{gotoEditWebhook(i)}}>查看</Button></Popover>
|
||||
<Popover content={`该webhook由${i.isJianMu ? '流水线创建,不可编辑或删除,更改配置将导致流水线失效' : '已安装的Bot创建,请勿编辑或删除以防Bot失效'}`} title="无法删除此Webhook" overlayClassName="disabledButPopover"><Button className="ml20" type="danger" disabled>删除</Button></Popover>
|
||||
</span>}
|
||||
</List.Item>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
const [ visible , setVisible ] = useState(false);
|
||||
const [ data , setData ] = useState(undefined);
|
||||
const [ eventFlag , setEventFlag ] = useState(false);
|
||||
const [ unEdit, setUnEdit] = useState(false);
|
||||
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
const { id , owner , projectsId } = match.params;
|
||||
|
|
@ -69,6 +70,10 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
const url = `/${owner}/${projectsId}/webhooks/${id}/edit.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result){
|
||||
const reg1 = new RegExp("https://(.*?)jianmuhub.com(.*?)");
|
||||
const reg2 = new RegExp("https://jianmu.gitlink.org.cn(.*?)");
|
||||
const {url, type} = result.data;
|
||||
setUnEdit(reg1.test(url) || reg2.test(url) || type === "softbot")
|
||||
let e = compareArray(result.data.events);
|
||||
setFieldsValue({
|
||||
...result.data,
|
||||
|
|
@ -190,7 +195,7 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
<span>
|
||||
<Link to={`/${owner}/${projectsId}/settings/webhooks`} className="color-blue">Webhooks</Link>
|
||||
<i className="iconfont icon-youjiantou ml5 mr5 font-12"></i>
|
||||
<span>{id ? "更新" : "添加"}Webhook</span>
|
||||
<span>{ unEdit ? '查看' : id ? "更新" : "添加"}Webhook</span>
|
||||
</span>
|
||||
</Banner>
|
||||
<div>
|
||||
|
|
@ -206,21 +211,21 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
}
|
||||
]
|
||||
})(
|
||||
<Input placeholder="请输入目标URL" maxLength="200" size="large" autocomplete='off'/>
|
||||
<Input placeholder="请输入目标URL" maxLength={200} size="large" autoComplete='off' disabled = {unEdit}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="Webhook密钥" colon={false}>
|
||||
{getFieldDecorator("secret",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input.Password placeholder="请输入Webhook密钥" autocomplete='new-password' size="large" maxLength={"200"}/>
|
||||
<Input.Password placeholder="请输入Webhook密钥" autoComplete='new-password' size="large" maxLength={200} disabled = {unEdit}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="Http请求方法" colon={false}>
|
||||
{getFieldDecorator("http_method",{
|
||||
rules:[]
|
||||
})(
|
||||
<Select onSelect={changeHTTP} size="large">
|
||||
<Select onSelect={changeHTTP} size="large" disabled = {unEdit}>
|
||||
<Select.Option value="GET">GET</Select.Option>
|
||||
<Select.Option value="POST">POST</Select.Option>
|
||||
</Select>
|
||||
|
|
@ -234,7 +239,7 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
{getFieldDecorator("content_type",{
|
||||
rules:[]
|
||||
})(
|
||||
<Select size="large">
|
||||
<Select size="large" disabled = {unEdit}>
|
||||
<Select.Option value="json">application/json</Select.Option>
|
||||
<Select.Option value="form">application/x-www-form-urlencoded</Select.Option>
|
||||
</Select>
|
||||
|
|
@ -244,7 +249,7 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
{getFieldDecorator("eventCondition",{
|
||||
rules:[]
|
||||
})(
|
||||
<Radio.Group onChange={changeCondition}>
|
||||
<Radio.Group onChange={changeCondition} disabled = {unEdit}>
|
||||
<Radio style={radioStyle} value="push">只是push事件</Radio>
|
||||
<Radio style={radioStyle} value="all">所有事件</Radio>
|
||||
<Radio style={radioStyle} value="forevent">自定义事件</Radio>
|
||||
|
|
@ -254,7 +259,7 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
<div className="eventCb"
|
||||
style={{display:condition === "forevent" ?"block":"none"}}
|
||||
>
|
||||
<Checkbox.Group value={event} onChange={(e)=>{setEvent(e)}}>
|
||||
<Checkbox.Group value={event} onChange={(e)=>{setEvent(e)}} disabled = {unEdit}>
|
||||
<p className="color-grey-3 mb10">代码库事件</p>
|
||||
<div className="colSpan">
|
||||
<span>
|
||||
|
|
@ -284,14 +289,14 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
<Checkbox value="pull_request_assign">合并请求分配</Checkbox>
|
||||
<span>合并请求被分配或取消分配</span>
|
||||
</span>
|
||||
{/* <span>
|
||||
<Checkbox value="pull_request_milestone">合并请求收入里程碑</Checkbox>
|
||||
<span>合并请求被记录或取消记录于里程碑中</span>
|
||||
</span> */}
|
||||
{/* <span>
|
||||
<Checkbox value="pull_request_comment">合并请求被评论</Checkbox>
|
||||
<span>合并请求评论被创建、编辑或删除</span>
|
||||
</span> */}
|
||||
{/* <span>
|
||||
<Checkbox value="pull_request_milestone">合并请求收入里程碑</Checkbox>
|
||||
<span>合并请求被记录或取消记录于里程碑中</span>
|
||||
</span> */}
|
||||
{/* <span>
|
||||
<Checkbox value="pull_request_label">合并请求标签</Checkbox>
|
||||
<span>合并请求的标签被更新或清除</span>
|
||||
|
|
@ -305,6 +310,25 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
<span>合并请求被同步</span>
|
||||
</span> */}
|
||||
</div>
|
||||
{/* <p className="color-grey-3 mt10 mb10">疑修事件</p>
|
||||
<div className="colSpan">
|
||||
<span>
|
||||
<Checkbox value="issues_only">疑修</Checkbox>
|
||||
<span>疑修已打开、已关闭、已重新打开或编辑</span>
|
||||
</span>
|
||||
<span>
|
||||
<Checkbox value="issue_assign">疑修已指派</Checkbox>
|
||||
<span>疑修已被指派或取消指派</span>
|
||||
</span>
|
||||
<span>
|
||||
<Checkbox value="issue_label">已标记疑修</Checkbox>
|
||||
<span>疑修标记被更新或清除</span>
|
||||
</span>
|
||||
<span>
|
||||
<Checkbox value="issue_comment">疑修评论</Checkbox>
|
||||
<span>疑修评论被创建、编辑或删除</span>
|
||||
</span>
|
||||
</div> */}
|
||||
</Checkbox.Group>
|
||||
{ eventFlag && <span style={{color:"#DF0002"}}>请选择自定义事件!</span>}
|
||||
</div>
|
||||
|
|
@ -318,7 +342,7 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
{getFieldDecorator("branch_filter",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input size="large"/>
|
||||
<Input size="large" disabled = {unEdit}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
|
|
@ -329,13 +353,13 @@ function New({ form , match , showNotification , history, projectDetail }) {
|
|||
{getFieldDecorator("active",{
|
||||
valuePropName:"checked"
|
||||
})(
|
||||
<Checkbox>激活<span className="subCbDesc">激活后触发事件的信息将发送到此Webhook地址</span></Checkbox>
|
||||
<Checkbox disabled = {unEdit}>激活<span className="subCbDesc">激活后触发事件的信息将发送到此Webhook地址</span></Checkbox>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Button type="primary" onClick={submit} className="ml20" style={{width:id?"":"100px"}}>{id ? "更新Webhook":"添加"}</Button>
|
||||
<Button type="primary" onClick={submit} className="ml20" style={{width:id?"":"100px"}} disabled = {unEdit}>{id ? "更新Webhook":"添加"}</Button>
|
||||
{
|
||||
id && <Button type="danger" className="ml20" onClick={deleteFunc}>删除</Button>
|
||||
id && <Button type="danger" className="ml20" onClick={deleteFunc} disabled = {unEdit}>删除</Button>
|
||||
}
|
||||
</Form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -283,4 +283,69 @@
|
|||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.softBotClass{
|
||||
.softBoxHeadTitle{
|
||||
color:#151d40;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
}
|
||||
.botListBox{
|
||||
margin-top: 16px;
|
||||
color:#202d40;
|
||||
.statusBox{
|
||||
background-color:rgba(255, 81, 81, 0.08);
|
||||
border:1px solid#f23737;
|
||||
color:#f92d2d;
|
||||
border-radius:4px;
|
||||
padding: 4px 6px;
|
||||
line-height: normal;
|
||||
&.active{
|
||||
background-color:rgba(58, 213, 115, 0.08);
|
||||
border-color:#25c589;
|
||||
color:#25c589;
|
||||
}
|
||||
}
|
||||
.disBotItem{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
padding: 20px 0 15px;
|
||||
}
|
||||
.flexCenter{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.botName{
|
||||
display: inline-block;
|
||||
max-width: 600px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.imgBox{
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
.nullBotBox{
|
||||
background-color: #fafcff;
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
text-align: center;
|
||||
padding: 25px 158px 65px;
|
||||
.showBigTip{
|
||||
color:#333333;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 22px !important;
|
||||
}
|
||||
.showTip{color: #666;}
|
||||
.borTip{
|
||||
width: 82%;
|
||||
margin: 20px auto 18px;
|
||||
border-top: 1px solid rgba(90, 117, 193, 0.23)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import React, {useState} from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Button } from "antd";
|
||||
import nullBot from '../../softbot/image/notBot.png';
|
||||
import './setting.scss';
|
||||
import { useEffect } from "react";
|
||||
import { getStoreAllInstallBots } from "../../softbot/api";
|
||||
import { getImageUrl } from 'educoder';
|
||||
|
||||
function SoftBot(props){
|
||||
const {projectDetail} = props;
|
||||
const [botList, setBotList] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取仓库安装的所有bot
|
||||
projectDetail && getStoreAllInstallBots({
|
||||
store_id: projectDetail.project_id
|
||||
}).then(res=>{
|
||||
if(res && res.code === 200){
|
||||
setBotList(res.data.install_bot_infos);
|
||||
}
|
||||
})
|
||||
|
||||
// 网页标题
|
||||
if(projectDetail && document.title.indexOf('已安装Bot-') === -1){
|
||||
const { author, name} = projectDetail;
|
||||
document.title = `已安装Bot-${author.name}/${name}`;
|
||||
}
|
||||
}, [projectDetail])
|
||||
|
||||
return <div className="softBotClass">
|
||||
<div className="softBoxHeadTitle font-18">Bot安装</div>
|
||||
{/* bot列表显示区域 */}
|
||||
{botList && (botList.length > 0 ? <div className="botListBox">
|
||||
{botList.map((item, index)=>{return <div className="disBotItem" key={index}>
|
||||
<div className="flexCenter">
|
||||
<img src={getImageUrl(item.logo)} alt="" className="imgBox mr20"/>
|
||||
<span className="font-15 botName">{item.bot_name}</span>
|
||||
<span className={`statusBox font-12 ml10 ${item.state === 0 ? '' : 'active'}`}>{item.state === 0 ? '挂起' : '启用'}</span>
|
||||
</div>
|
||||
<Button className="themeCorBorBut" style={{width: '68px', height: '36px'}}><Link to={`/settings/installbot/${item.bot_id}`}>配置</Link></Button>
|
||||
</div>})}
|
||||
</div> : <div className="nullBotBox mt20">
|
||||
<img src={nullBot} alt="" width={62}/>
|
||||
<p className="font-18 showBigTip">您当前暂未安装任何Bot</p>
|
||||
<div className="showTip font-14">Bot可以帮助项目贡献者处理繁杂的项目任务,比如关闭疑修、合并请求等,以节约时间精力提升开发效率。快去市场中挑选一个适合您项目的Bot吧!</div>
|
||||
<div className="borTip"></div>
|
||||
<Button type="primary" style={{width: '112px', height: '36px', padding: 0}}><Link to={'/softbot'}>去Bot市场逛逛</Link></Button>
|
||||
</div>)}
|
||||
</div>
|
||||
}
|
||||
export default SoftBot
|
||||
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
|||
import { Upload , Icon , message } from "antd";
|
||||
import { getUploadActionUrl } from 'educoder';
|
||||
|
||||
function UploadImage({ getImage , url }){
|
||||
function UploadImage({ getImage , url, getImageId }){
|
||||
const [ imageUrl , setImageUrl ] = useState(undefined);
|
||||
useEffect(()=>{
|
||||
if(url){
|
||||
|
|
@ -33,6 +33,8 @@ function UploadImage({ getImage , url }){
|
|||
// 上传完成后
|
||||
function handleChange(info){
|
||||
if(info && info.file && info.file.status === "done"){
|
||||
console.log('info', info);
|
||||
getImageId && getImageId(info.file.response.id);
|
||||
getBase64(info.file.originFileObj, imageUrl =>
|
||||
setImageUrl(imageUrl)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -59,8 +59,9 @@ class Index extends Component {
|
|||
return {
|
||||
fileList: newFileList,
|
||||
};
|
||||
},()=>{
|
||||
this.fileIdList(this.state.fileList);
|
||||
});
|
||||
this.fileIdList(this.state.fileList);
|
||||
} else {
|
||||
this.props.showNotification(response.data.message)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,28 +3,34 @@ import { Button, Checkbox, Form, Icon, Radio, Input, message } from 'antd';
|
|||
import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
|
||||
import DelModal from './components/ModalFun';
|
||||
import { weekModal, monthModal } from './components/config';
|
||||
import { getWiki, wikiPages, addWiki, updateWiki } from './api';
|
||||
import { getWiki, wikiPages, addWiki, updateWiki, markdownToTree, treeToMd } from './api';
|
||||
import './Index.scss';
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
|
||||
export default Form.create()(({ form, history, showNotification, projectDetail, match, project }) => {
|
||||
// const permission = projectDetail && projectDetail.permission !== "Reporter";
|
||||
const {location:{pathname, search}} = history;
|
||||
const permission = projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter";
|
||||
|
||||
const { getFieldDecorator, validateFields, setFieldsValue } = form;
|
||||
const [fileArrInit, setFileArrInit] = useState(null);
|
||||
let projectsId = match.params.projectsId;
|
||||
let owner = match.params.owner;
|
||||
|
||||
let wikiName = history.location.pathname.split('/').pop();
|
||||
if (wikiName === 'add') {
|
||||
wikiName = '';
|
||||
let wikiName = ''
|
||||
let key = '';
|
||||
if (!pathname.endsWith('/wiki/add')) {
|
||||
wikiName = decodeURI(pathname.split('/')[4]);
|
||||
key = pathname.split('/')[5];
|
||||
}else{
|
||||
key = search.split('=').pop();
|
||||
}
|
||||
|
||||
const [content, setContent] = useState(undefined);
|
||||
const [fileArrInit, setFileArrInit] = useState(null);
|
||||
const [content, setContent] = useState("欢迎来到Wiki");
|
||||
const [modal, setModal] = useState(false);
|
||||
const [operateFlag, setOperateFlag ] = useState(false);
|
||||
const [modalType, setModalType] = useState();
|
||||
const [sidebar, setSidebar] = useState(undefined);
|
||||
const [menuList, setMenuList] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail){
|
||||
|
|
@ -41,14 +47,15 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
wikiName && project && getWiki({
|
||||
owner,
|
||||
repo: projectsId,
|
||||
pagename: wikiName,
|
||||
pageName: wikiName,
|
||||
projectId: project.id
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
setContent(res.data.md_content);
|
||||
const content = Base64.decode(res.data.md_content);
|
||||
setContent(content);
|
||||
setFieldsValue({
|
||||
name: res.data.name,
|
||||
content: res.data.md_content,
|
||||
title: res.data.name,
|
||||
md_content: content,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -63,6 +70,22 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
}).then(res => {
|
||||
if (res && res.message === "200" && Array.isArray(res.data)) {
|
||||
setFileArrInit(res.data);
|
||||
// 仓库存在没有创建过wiki的情况
|
||||
if(res.data.length){
|
||||
// 获取sidebar文件内容
|
||||
getWiki({
|
||||
owner,
|
||||
repo: projectsId,
|
||||
pageName: '_Sidebar',
|
||||
projectId: project.id
|
||||
}).then(res => {
|
||||
if (res && res.data) {
|
||||
const sidebarDecode = Base64.decode(res.data.md_content);
|
||||
setSidebar(sidebarDecode);
|
||||
key && setMenuList(markdownToTree(sidebarDecode))
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setFileArrInit([]);
|
||||
}
|
||||
|
|
@ -81,38 +104,42 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
function onContentChange(value) {
|
||||
setContent(value);
|
||||
setFieldsValue({
|
||||
content: value
|
||||
md_content: value
|
||||
});
|
||||
};
|
||||
|
||||
// 保存wiki文件,包括新增和修改
|
||||
function saveFile() {
|
||||
if(operateFlag) return;
|
||||
setOperateFlag(true);
|
||||
if(operateFlag) {
|
||||
setOperateFlag(true);
|
||||
return;
|
||||
};
|
||||
validateFields((err, values) => {
|
||||
if (!err) {
|
||||
let regEn = /[\[\]`\/:*?''<>|%-+_]/g;
|
||||
if (regEn.test(values.name)) {
|
||||
message.error('文件名不能有特殊字符');
|
||||
let regEn = /[\\/:*?"<>|[\]-]/g;
|
||||
if (regEn.test(values.title)) {
|
||||
message.error('文件名不能有特殊字符: \\ / : * ? \" < > | [ \] -');
|
||||
setOperateFlag(false);
|
||||
return;
|
||||
}
|
||||
const {md_content, title} = values;
|
||||
if (wikiName) {
|
||||
updateWiki({
|
||||
owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id,
|
||||
pagename: wikiName,
|
||||
...values,
|
||||
pageName: wikiName,
|
||||
title,
|
||||
commit_message: '',
|
||||
}).then(res => {
|
||||
dealRes(res);
|
||||
setOperateFlag(false);
|
||||
});
|
||||
content_base64: Base64.encode(md_content)
|
||||
}).then(res => dealRes(res, title));
|
||||
} else {
|
||||
// 由于wiki在底层是由wikiname标识的md文件,所以对于所有的页面,都不能重名
|
||||
if (Array.isArray(fileArrInit)) {
|
||||
for (const item of fileArrInit) {
|
||||
if (item.name === values.name) {
|
||||
if (item.name === title) {
|
||||
message.error('不能与已有文件标题相同');
|
||||
setOperateFlag(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -121,31 +148,127 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id,
|
||||
pagename: values.name,
|
||||
...values,
|
||||
commit_message: '',
|
||||
}).then(res =>{
|
||||
dealRes(res);
|
||||
setOperateFlag(false);
|
||||
});
|
||||
pageName: title,
|
||||
title,
|
||||
message: '',
|
||||
content_base64: Base64.encode(md_content)
|
||||
}).then(res => dealRes(res, title));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function dealRes(res) {
|
||||
if (res && res.message === "200") {
|
||||
showNotification("操作成功");
|
||||
goBack();
|
||||
function dealRes(res, title) {
|
||||
if (res && (res.message === "201" || res.message === "200")) {
|
||||
if(wikiName){
|
||||
// 如果有更改wiki文件名,则修改sidebar
|
||||
if(wikiName === title){
|
||||
message.success("操作成功");
|
||||
goBack();
|
||||
}else{
|
||||
// 找到相同的key值node修改title参数,再保存sidebar
|
||||
const newMenuList = menuList;
|
||||
editTitleByKey(newMenuList, key, title);
|
||||
updateSidebar(treeToMd(newMenuList), title);
|
||||
}
|
||||
}else{
|
||||
// 根据路由判断是新增的一级页面还是子页面
|
||||
if(search.includes("?key")){
|
||||
// 遍历menuList找到相同key的节点,children push[[页面]]
|
||||
const list = menuList;
|
||||
addChildrenByKey(list, key, title);
|
||||
updateSidebar(treeToMd(list), title);
|
||||
}else{
|
||||
if(fileArrInit.length){
|
||||
updateSidebar(`${sidebar}\n[[${title}]]`, title);
|
||||
}else{
|
||||
// 仓库第一次创建wiki文件
|
||||
addWiki({
|
||||
owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id,
|
||||
pageName: "_Sidebar",
|
||||
title: "_Sidebar",
|
||||
message: '',
|
||||
content_base64: Base64.encode(`[[${title}]]`)
|
||||
}).then(res => {
|
||||
if(res && res.message === "201"){
|
||||
message.success("操作成功");
|
||||
goBack();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (res && res.message === "500") {
|
||||
setOperateFlag(false);
|
||||
message.error('请检查格式是否正确或文件名是否重复');
|
||||
} else {
|
||||
showNotification(res.message || "操作失败");
|
||||
setOperateFlag(false);
|
||||
showNotification(res.data || "操作失败");
|
||||
}
|
||||
}
|
||||
|
||||
function updateSidebar(content, newTitle){
|
||||
updateWiki({
|
||||
owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id,
|
||||
pageName: '_Sidebar',
|
||||
title: '_Sidebar',
|
||||
commit_message: '',
|
||||
content_base64: Base64.encode(content)
|
||||
}).then(res => {
|
||||
if (res && res.message === "200") {
|
||||
message.success("操作成功");
|
||||
if(newTitle){
|
||||
// 用户修改了wiki名称
|
||||
history.push(`/${owner}/${projectsId}/wiki/${encodeURI(newTitle)}/${key}`)
|
||||
return;
|
||||
}
|
||||
goBack();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 递归menuList, 找到相同key值修改title名称
|
||||
function editTitleByKey(list, key, newTitle){
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
if (element.key == key) {
|
||||
const {title} = element;
|
||||
element.title = `${title.substring(0, title.indexOf('[['))}[[${newTitle}]]`;
|
||||
return true;
|
||||
}else if (element.children.length) {
|
||||
let result = editTitleByKey(element.children, key, newTitle);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 递归menuList 创建子页面
|
||||
function addChildrenByKey(list, key, name){
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
if (element.key == key) {
|
||||
const {title} = element;
|
||||
element.children.push({
|
||||
title: `\t${title.substring(0, title.indexOf('- '))}[[${name}]]`,
|
||||
children: []
|
||||
});
|
||||
return true;
|
||||
}else if (element.children.length) {
|
||||
let result = addChildrenByKey(element.children, key, name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
// history.push(`/${owner}/${projectsId}/wiki`);
|
||||
history.go(-1);
|
||||
}
|
||||
|
||||
|
|
@ -179,12 +302,12 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
if (value === '周报') {
|
||||
setContent(weekModal);
|
||||
setFieldsValue({
|
||||
content: weekModal
|
||||
md_content: weekModal
|
||||
});
|
||||
} else if (value === '月报') {
|
||||
setContent(monthModal);
|
||||
setFieldsValue({
|
||||
content: monthModal
|
||||
md_content: monthModal
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -200,7 +323,7 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
<h4 className="mt20 mb0">标题</h4>
|
||||
{helper(
|
||||
"",
|
||||
"name",
|
||||
"title",
|
||||
[
|
||||
{ required: true, message: "请输入标题" },
|
||||
// { pattern: /[^`\[\]\/:*?''<>|%-+_]/g, message: '不允许部分特殊字符' }
|
||||
|
|
@ -221,9 +344,10 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
onChange={onContentChange}
|
||||
className="mt20 wikiMEDEditor"
|
||||
></MDEditor>
|
||||
{getFieldDecorator('content', {
|
||||
{getFieldDecorator('md_content', {
|
||||
rules: [{ required: true, message: "请输入wiki内容" }],
|
||||
validateFirst: true
|
||||
validateFirst: true,
|
||||
initialValue: "欢迎来到Wiki"
|
||||
})(<Input style={{ display: 'none' }} />)}
|
||||
</Form.Item>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,49 @@
|
|||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
import { Button, Dropdown, Icon, Input, Menu, Tooltip, Select, Upload, message, Spin } from 'antd';
|
||||
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
||||
import { Button, Dropdown, Icon, Input, Menu, Tooltip, Select, Upload, message, Spin, Modal } from 'antd';
|
||||
import { getImageUrl, timeAgo } from 'educoder';
|
||||
import cookie from 'react-cookies';
|
||||
import CopyTool from '../Component/CopyTool';
|
||||
import DelModal from './components/ModalFun';
|
||||
import Welcome from './Welcome';
|
||||
import RenderHtml from "../../components/render-html";
|
||||
|
||||
import { wikiPages, getWiki, deleteWiki } from './api';
|
||||
import { wikiPages, getWiki, parseSidebar, updateWiki, treeToMd } from './api';
|
||||
import { httpUrl, TokenKey } from './fetch';
|
||||
import './Index.scss';
|
||||
import './components/ModalFun/index.scss';
|
||||
import { isArray } from 'lodash';
|
||||
import { Base64 } from 'js-base64';
|
||||
import Sidebar from './components/sidebar';
|
||||
import UploadWiki from './components/uploadWiki';
|
||||
const Search = Input.Search;
|
||||
const InputGroup = Input.Group;
|
||||
const { Option } = Select;
|
||||
|
||||
export default (props) => {
|
||||
const { match, history, showNotification, project, projectDetail } = props;
|
||||
const {location:{pathname}} = history;
|
||||
const permission = projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter";
|
||||
|
||||
let projectsId = match.params.projectsId;
|
||||
let owner = match.params.owner;
|
||||
let wikiName = match.params.wikiName;
|
||||
|
||||
const [fileArrInit, setFileArrInit] = useState(null);
|
||||
const [checkItem, setCheckItem] = useState({});
|
||||
const [itemDetail, setItemDetail] = useState({});
|
||||
|
||||
const [fileArr, setFileArr] = useState([]);
|
||||
const [fileList, setFileList] = useState([]);
|
||||
const [fileListInit, setFileListInit] = useState([]);
|
||||
const [reload, setReload] = useState();
|
||||
|
||||
const [urlType, setUrlType] = useState('HTTPS');
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [menuName, setMenuName] = useState(undefined);
|
||||
const [addMenuError, setAddMenuError] = useState(undefined);
|
||||
const [sidebar, setSidebar] = useState(undefined);
|
||||
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
window.scrollTo(0,0);
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail){
|
||||
|
|
@ -48,18 +62,43 @@ export default (props) => {
|
|||
owner: owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id
|
||||
}).then(res => {
|
||||
}).then(async res => {
|
||||
if (res && res.message === "200" && isArray(res.data)) {
|
||||
setFileArr(res.data);
|
||||
setFileArrInit(res.data);
|
||||
if (res.data.length) {
|
||||
if (wikiName) {
|
||||
let activeItem = res.data.filter(item => { return item.name == wikiName })[0] || res.data[0];
|
||||
setCheckItem(activeItem)
|
||||
} else {
|
||||
setCheckItem(res.data[0]);
|
||||
}
|
||||
};
|
||||
// 解析sidebar获取目录list
|
||||
const info = await parseSidebar(res.data, {
|
||||
owner: owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id
|
||||
})
|
||||
if(!info){return}
|
||||
const {menuList, sidebar} = info;
|
||||
setSidebar(sidebar)
|
||||
setFileList(menuList);
|
||||
setFileListInit(menuList);
|
||||
let firstWikiNode = undefined;
|
||||
// if(!pathname.endsWith('/wiki')){
|
||||
// firstWikiNode = findNodeByKey(menuList, pathname.split('/').pop())
|
||||
// }
|
||||
const arr = pathname.split('/');
|
||||
if(!pathname.endsWith('/wiki')){
|
||||
firstWikiNode = findNodeByName(menuList, arr[arr.length-2]);
|
||||
} else{
|
||||
// 找到目录树中第一个[[页面]]
|
||||
firstWikiNode = findFirstWiki(menuList);
|
||||
}
|
||||
// 当前选中wiki被删除
|
||||
!firstWikiNode && (firstWikiNode = findFirstWiki(menuList));
|
||||
// 设置目录 选中节点初始化
|
||||
setDefaultSelectedKeys([firstWikiNode.key+""]);
|
||||
const title = firstWikiNode.title.trim().substring(2,firstWikiNode.title.trim().length-2);
|
||||
const firstWiki = res.data.filter(item => { return title === item.name })[0];
|
||||
setCheckItem({
|
||||
...firstWiki,
|
||||
key: firstWikiNode.key
|
||||
})
|
||||
history.push(`/${owner}/${projectsId}/wiki/${title}/${firstWikiNode.key}`);
|
||||
} else {
|
||||
setFileArr([]);
|
||||
setFileArrInit([]);
|
||||
|
|
@ -72,7 +111,7 @@ export default (props) => {
|
|||
project && checkItem.name && getWiki({
|
||||
owner: owner,
|
||||
repo: projectsId,
|
||||
pagename: checkItem.name,
|
||||
pageName: checkItem.name,
|
||||
projectId: project.id
|
||||
}).then(res => {
|
||||
if (res && res.message === "200") {
|
||||
|
|
@ -83,40 +122,66 @@ export default (props) => {
|
|||
});
|
||||
}, [project, checkItem]);
|
||||
|
||||
function changeSearchValue(e) {
|
||||
let value = e.target.value;
|
||||
let newFileArr = [];
|
||||
for (const item of fileArrInit) {
|
||||
if (item.name.indexOf(value) > -1) {
|
||||
newFileArr.push(item);
|
||||
function findNodeByName(list, name){
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
if (element.title.indexOf(`[[${name}]]`) !== -1) {
|
||||
return element;
|
||||
}else if (element.children.length) {
|
||||
let result = findNodeByName(element.children, name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
setFileArr(newFileArr);
|
||||
}
|
||||
|
||||
// 删除wiki模态框
|
||||
function deleteFileModal(e, item) {
|
||||
e.stopPropagation();
|
||||
DelModal({
|
||||
title: '删除页面',
|
||||
contentTitle: `您确定要删除“${item.name}”此页面吗?`,
|
||||
content: '此操作将删除该页面,请进行确认以防文件的丢失',
|
||||
onOk: () => {
|
||||
deleteWiki({
|
||||
owner: owner,
|
||||
repo: projectsId,
|
||||
pagename: item.name,
|
||||
projectId: project.id
|
||||
}).then(res => {
|
||||
if (res && res.message === "200") {
|
||||
message.success('删除成功');
|
||||
setReload(Math.random());
|
||||
} else {
|
||||
message.error('删除失败');
|
||||
}
|
||||
});
|
||||
// 初始化页面:递归menulist找到第一个页面
|
||||
function findFirstWiki(list){
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
if (element.title.trim().startsWith('[[')) {
|
||||
return element;
|
||||
}else if (element.children.length) {
|
||||
let result = findFirstWiki(element.children);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function changeSearchValue(value) {
|
||||
if(value){
|
||||
// 过滤不包含wiki的内容, 重新渲染页面
|
||||
let filterList = filterTree(fileListInit, value);
|
||||
setFileList(filterList);
|
||||
}else{
|
||||
setFileList(JSON.parse(JSON.stringify(fileListInit)));
|
||||
}
|
||||
}
|
||||
|
||||
function filterTree(list, value, arr = []){
|
||||
if(!list.length) return []
|
||||
for(let item of list){
|
||||
const bool = item.title.match(new RegExp(value, 'i'));
|
||||
let node = JSON.parse(JSON.stringify(item));
|
||||
node.children = [];
|
||||
bool && arr.push(node);
|
||||
if(item.children && item.children.length){
|
||||
filterTree(item.children, value, bool ? node.children : arr);
|
||||
}
|
||||
}
|
||||
return arr
|
||||
// return list.filter(item => {
|
||||
// if (item.title.includes(value)) {
|
||||
// return true
|
||||
// }
|
||||
// // if(item.children && item.children.length > 0){
|
||||
// // item.children = findUnHasValue(item.children , value);
|
||||
// return findUnHasValue(item.children , value);
|
||||
// // }
|
||||
// })
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -125,11 +190,64 @@ export default (props) => {
|
|||
}
|
||||
|
||||
function addFile() {
|
||||
// 新增一级页面
|
||||
history.push(`/${owner}/${projectsId}/wiki/add`);
|
||||
}
|
||||
|
||||
// 新增目录
|
||||
function addMenu(){
|
||||
if(!menuName){
|
||||
setAddMenuError('请输入目录名称');
|
||||
return;
|
||||
}
|
||||
if(new RegExp("^[ ]+$").test(menuName)){
|
||||
setAddMenuError("不能仅输入空格");
|
||||
return;
|
||||
}
|
||||
const regEn = /[\\/:*?"<>|[\]-]/g;
|
||||
if (regEn.test(menuName)) {
|
||||
setAddMenuError("不能有特殊字符: \\ / : * ? \" < > | [ \] -");
|
||||
return;
|
||||
}
|
||||
// 校验一级目录中是否有相同名称
|
||||
const oneMenuList = fileList.filter(item=>!item.title.trim().startsWith('[[')).map(i=>i.title.trim())
|
||||
if(oneMenuList.includes(`- ${menuName}`)){
|
||||
setAddMenuError('不能与已有文件标题相同');
|
||||
return;
|
||||
}
|
||||
|
||||
// 重写fileList,再转换成md内容,最后保存sidebar
|
||||
const newSidebarList = fileList.concat({title: `- ${menuName}`, children: []})
|
||||
updateWikiFun(newSidebarList, 1);
|
||||
}
|
||||
|
||||
// 更新wiki sidebar文件
|
||||
function updateWikiFun(sidebarList, type){
|
||||
updateWiki({
|
||||
owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id,
|
||||
pageName: '_Sidebar',
|
||||
title: '_Sidebar',
|
||||
commit_message: '',
|
||||
content_base64: Base64.encode(treeToMd(sidebarList))
|
||||
}).then(res => {
|
||||
if (res && res.message === "200") {
|
||||
if(type === 1){
|
||||
setVisible(false);
|
||||
setMenuName(undefined);
|
||||
message.success('操作成功');
|
||||
}
|
||||
// setFileList(sidebarList);
|
||||
setReload(Math.random());
|
||||
}else {
|
||||
message.error(res.data || "操作失败");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function goEdit() {
|
||||
history.push(`/${owner}/${projectsId}/wiki/edit/${encodeURI(checkItem.name)}`);
|
||||
history.push(`/${owner}/${projectsId}/wiki/${encodeURI(checkItem.name)}/${checkItem.key}/edit`);
|
||||
}
|
||||
|
||||
function preview() {
|
||||
|
|
@ -155,67 +273,18 @@ export default (props) => {
|
|||
window.open(`${httpUrl}/api/wikiExport/wikiExport-wrapper?repoName=${projectsId}&owner=${owner}&type=${type}&projectName=${project.name}&projectId=${project.id}`);
|
||||
}
|
||||
|
||||
function beforeUpload(file) {
|
||||
if (!['md', 'txt'].includes(file.name.split('.').pop())) {
|
||||
message.error('只能上传md、txt文件');
|
||||
return false;
|
||||
}
|
||||
|
||||
let regEn = /[\[\]`\/:*?''<>|%-+_]/g;
|
||||
if (regEn.test(file.name)) {
|
||||
message.error('文件名不能有特殊字符');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const item of fileArrInit) {
|
||||
if (item.name === file.name) {
|
||||
message.error('不能上传与已有文件相同文件名的文件');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const isLt100M = file.size / 1024 / 1024 < 100;
|
||||
if (!isLt100M) {
|
||||
showNotification(`文件大小必须小于${100}MB!`);
|
||||
}
|
||||
return isLt100M;
|
||||
}
|
||||
|
||||
const uploadProps = {
|
||||
name: 'multipartFile',
|
||||
withCredentials: true,
|
||||
action: `${httpUrl}/api/wikiExport/uploadWiki/${owner}/${projectsId}/${project && project.id}`, //?token=${sessionStorage.taskToken}
|
||||
showUploadList: false,
|
||||
headers: {
|
||||
Authorization: cookie.load(TokenKey) || sessionStorage.taskToken,
|
||||
},
|
||||
beforeUpload: beforeUpload,
|
||||
onChange(info) {
|
||||
if (info.file.status === 'uploading') {
|
||||
console.log(info.file, info.fileList);
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
if (info.file.response.message === '200') {
|
||||
message.success(`${info.file.name} 上传成功`);
|
||||
setReload(Math.random());
|
||||
} else if (info.file.response.message === '500') {
|
||||
let data = JSON.parse(info.file.response.data);
|
||||
message.error(data && data.message ? data.message : '文件上传失败');
|
||||
} else {
|
||||
message.error('文件上传失败');
|
||||
}
|
||||
} else if (info.file.status === 'error') {
|
||||
message.error(`${info.file.name} 上传失败`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function changeitem(item){
|
||||
history.push(`/${owner}/${projectsId}/wiki/${item.name}`)
|
||||
setCheckItem(item);
|
||||
const {name, key} = item;
|
||||
const wiki = fileArrInit.filter(item => { return item.name == name })[0];
|
||||
setCheckItem({
|
||||
...wiki,
|
||||
key
|
||||
})
|
||||
history.push(`/${owner}/${projectsId}/wiki/${name}/${key}`);
|
||||
}
|
||||
|
||||
return (
|
||||
< Spin spinning={!fileArrInit} className="opacitySpin">
|
||||
<Spin spinning={!fileArrInit} className="opacitySpin">
|
||||
{fileArrInit && fileArrInit.length ?
|
||||
<div className="wiki-main">
|
||||
<div className="wiki-head">
|
||||
|
|
@ -223,18 +292,15 @@ export default (props) => {
|
|||
<span className="head-title">
|
||||
{
|
||||
permission ?
|
||||
<Button type="default" onClick={addFile}><Icon type="plus" />新增页面</Button>
|
||||
: "Wiki文档"
|
||||
<div>
|
||||
<Button type="default" onClick={()=>{setVisible(true)}} className='mr15'><Icon type="plus"/>新增目录</Button>
|
||||
<Button type="default" onClick={addFile}><Icon type="plus" />新增页面</Button>
|
||||
</div> : "Wiki文档"
|
||||
}
|
||||
</span>
|
||||
<div>
|
||||
{
|
||||
permission &&
|
||||
<Upload {...uploadProps}>
|
||||
<Tooltip placement="top" title={'支持导入txt、markdown格式文件'}>
|
||||
<Button type="default" className="ml10"><Icon type="plus" />导入模板</Button>
|
||||
</Tooltip>
|
||||
</Upload>
|
||||
permission && <UploadWiki {...props} fileList={fileList} setFileList={setFileList} fileArrInit={fileArrInit} setReload={setReload}/>
|
||||
}
|
||||
<Dropdown overlay={menu}>
|
||||
<Button type="default" className="ml10">导出<Icon type="caret-down" /></Button>
|
||||
|
|
@ -246,27 +312,18 @@ export default (props) => {
|
|||
|
||||
<div className="wiki-body">
|
||||
<div className="wiki-nav-parent">
|
||||
<div className="wiki-nav">
|
||||
<Search
|
||||
// allowClear
|
||||
placeholder="输入关键字搜索文件"
|
||||
className="wiki-search"
|
||||
onChange={changeSearchValue}
|
||||
/>
|
||||
<div className='wiki-nav-scroll'>
|
||||
<div className="wiki-nav">
|
||||
<Search
|
||||
allowClear
|
||||
placeholder="输入关键字搜索文件"
|
||||
className="wiki-search"
|
||||
onSearch={changeSearchValue}
|
||||
/>
|
||||
|
||||
{
|
||||
fileArr.map(item => {
|
||||
return <div className="wiki-nav-title-parent" key={item.name}>
|
||||
<div className={`wiki-nav-title ${item.name === checkItem.name ? 'active' : ''}`} onClick={() => { changeitem(item) }}>
|
||||
<div className="nav-title-left">
|
||||
<i className="iconfont icon-wenjianjia2 mr3"></i>
|
||||
<span className="nav-title-left-text">{item.name}</span>
|
||||
</div>
|
||||
{permission && <i className="iconfont icon-shanchuicon1 delete-title-icon color-grey-6" onClick={(e) => { deleteFileModal(e, item) }}></i>}
|
||||
</div>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
{/* 读sidebar转换成目录树 */}
|
||||
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history} permission={permission} defaultSelectedKeys={defaultSelectedKeys}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
@ -287,20 +344,25 @@ export default (props) => {
|
|||
<h3 className="wiki-detail-title">{checkItem.name}</h3>
|
||||
<span className="user-box mr10" onClick={() => { checkItem.commit && goUser(checkItem.commit.author.name) }}>
|
||||
{itemDetail.image_url && <img alt="头像" className="head-log-small" src={getImageUrl(`/${itemDetail.image_url}`)} />}
|
||||
<span >{itemDetail.userName}</span>
|
||||
<span className='font-14'>{itemDetail.userName}</span>
|
||||
</span>
|
||||
<span className="time-ago">上次修改于{checkItem.commit ? timeAgo(checkItem.commit.author.when) : '刚刚'}</span>
|
||||
<span className="time-ago font-14">上次修改于{checkItem.commit ? timeAgo(checkItem.commit.author.date) : '刚刚'}</span>
|
||||
</div>
|
||||
{permission && <Button type="primary" onClick={goEdit}>编辑</Button>}
|
||||
</div>
|
||||
|
||||
{itemDetail&&itemDetail.simple_content&&<RenderHtml className="wiki-content-detail editor-content-panel" value={ itemDetail&&itemDetail.md_content } url={history.location}/>}
|
||||
{itemDetail&&itemDetail.md_content&&<RenderHtml className="wiki-content-detail editor-content-panel" value={ itemDetail && Base64.decode(itemDetail.md_content) } url={history.location}/>}
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
:
|
||||
<Welcome {...props} reloadList={setReload} />
|
||||
<Welcome {...props} reloadList={setReload} fileList={fileList} setFileList={setFileList} fileArrInit={fileArrInit} setReload={setReload}/>
|
||||
}
|
||||
<Modal title="新增目录" visible={visible} onCancel={()=>{setVisible(false);setAddMenuError(undefined);setMenuName(undefined);}} onOk={addMenu} className="wikiAddMenu myself-modal" width={450} centered>
|
||||
<div className='mb10'>目录名称: </div>
|
||||
<Input placeholder='请输入目录名称' value={menuName} onChange={(e)=>{setMenuName(e.target.value);setAddMenuError(undefined)}} maxLength={50}></Input>
|
||||
<div className='wikiAddMenuError mt5'>{addMenuError}</div>
|
||||
</Modal>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ body {
|
|||
}
|
||||
|
||||
.head-log-small {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
margin-right: 0.35rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
|
@ -113,12 +113,8 @@ body {
|
|||
max-height: 60vh;
|
||||
.wiki-search {
|
||||
padding: 0 14px;
|
||||
.ant-input {
|
||||
padding-left: 30px;
|
||||
}
|
||||
.ant-input-suffix {
|
||||
left: 26px;
|
||||
right: auto;
|
||||
right: 20px;
|
||||
}
|
||||
&:hover .ant-input,
|
||||
&:focus .ant-input {
|
||||
|
|
@ -128,8 +124,12 @@ body {
|
|||
}
|
||||
|
||||
.wiki-nav-parent {
|
||||
width: 260px;
|
||||
width: 280px;
|
||||
flex: none;
|
||||
// .wiki-nav-scroll{
|
||||
// width: 280px;
|
||||
// overflow-y: scroll;
|
||||
// }
|
||||
}
|
||||
|
||||
.ant-form-item-children .ant-input:hover {
|
||||
|
|
@ -223,13 +223,19 @@ body {
|
|||
}
|
||||
|
||||
.wiki-nav {
|
||||
min-height: 300px;
|
||||
min-height: 500px;
|
||||
padding: 20px 0;
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(153, 153, 153, 0.22);
|
||||
overflow-y: scroll;
|
||||
flex: none;
|
||||
color: #333;
|
||||
.ant-tree.ant-tree-directory > li.ant-tree-treenode-selected > span.ant-tree-node-content-wrapper::before, .ant-tree.ant-tree-directory .ant-tree-child-tree > li.ant-tree-treenode-selected > span.ant-tree-node-content-wrapper::before{
|
||||
background-color: #F4F6FF;
|
||||
}
|
||||
.ant-tree.ant-tree-directory > li span.ant-tree-node-content-wrapper.ant-tree-node-selected, .ant-tree.ant-tree-directory .ant-tree-child-tree > li span.ant-tree-node-content-wrapper.ant-tree-node-selected{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.wiki-nav-title {
|
||||
|
|
@ -305,8 +311,19 @@ body {
|
|||
.wiki-preview {
|
||||
overflow-y: scroll;
|
||||
height: 100%;
|
||||
.previewWiki{
|
||||
overflow-y: auto;
|
||||
.ant-tree-title{
|
||||
font-size: 16px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
.previewContent{
|
||||
flex: none;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.wiki-body {
|
||||
width: 90%;
|
||||
width: 95%;
|
||||
}
|
||||
.ant-btn-primary {
|
||||
background-color: $wikiColor;
|
||||
|
|
@ -323,7 +340,7 @@ body {
|
|||
align-items: center;
|
||||
padding: 0 12rem 0 2rem;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
height: 8vh;
|
||||
background: rgb(39, 47, 76);
|
||||
color: #fff;
|
||||
}
|
||||
|
|
@ -368,7 +385,7 @@ body {
|
|||
padding: 20px 0;
|
||||
width: 20vw;
|
||||
border-bottom: 0;
|
||||
height: 80vh;
|
||||
height: 92vh;
|
||||
}
|
||||
.wiki-content-detail {
|
||||
padding: 0 40px;
|
||||
|
|
@ -385,4 +402,13 @@ body {
|
|||
}
|
||||
.wikiMEDEditor .editormd-dialog-footer{
|
||||
padding: 0;
|
||||
}
|
||||
.wikiAddMenu{
|
||||
&.myself-modal .ant-modal-body{
|
||||
text-align: left;
|
||||
padding: 40px 30px;
|
||||
}
|
||||
}
|
||||
.wikiAddMenuError{
|
||||
color: red;
|
||||
}
|
||||
|
|
@ -1,43 +1,54 @@
|
|||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
import { Input, Button, Select, Dropdown, Icon, Menu, message } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Input, Button, Select, Dropdown, Icon, Menu, message, Tree } from 'antd';
|
||||
import CopyTool from '../Component/CopyTool';
|
||||
import { wikiPages, getWiki, } from './api';
|
||||
import { wikiPages, getWiki, parseSidebar, } from './api';
|
||||
import { httpUrl } from './fetch';
|
||||
import './Index.scss';
|
||||
import { Base64 } from 'js-base64';
|
||||
import RenderHtml from '../../components/render-html';
|
||||
const {DirectoryTree, TreeNode} = Tree;
|
||||
const InputGroup = Input.Group;
|
||||
const { Option } = Select;
|
||||
|
||||
export default (props) => {
|
||||
|
||||
const { match, history, showNotification, project, projectDetail } = props;
|
||||
const permission = projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter";
|
||||
const { match, history, showNotification } = props;
|
||||
|
||||
let projectsId = match.params.projectsId;
|
||||
let owner = match.params.owner;
|
||||
|
||||
let projectName = match.params.projectName;
|
||||
let projectId = match.params.projectId;
|
||||
let key = 1;
|
||||
|
||||
const [checkItem, setCheckItem] = useState({});
|
||||
const [itemDetail, setItemDetail] = useState({});
|
||||
|
||||
const [fileArr, setFileArr] = useState([]);
|
||||
const [urlType, setUrlType] = useState('HTTPS');
|
||||
const [fileList, setFileList] = useState([]);
|
||||
|
||||
useEffect(()=>{
|
||||
if(checkItem){
|
||||
if(checkItem && checkItem.name){
|
||||
document.title = `${checkItem.name}-维基预览`
|
||||
}
|
||||
}, [checkItem, projectDetail])
|
||||
}, [checkItem])
|
||||
|
||||
useEffect(() => {
|
||||
projectsId && wikiPages({
|
||||
owner: owner,
|
||||
repo: projectsId,
|
||||
projectId: projectId,
|
||||
}).then(res => {
|
||||
}).then(async res => {
|
||||
if (res && res.message === "200") {
|
||||
setFileArr(res.data);
|
||||
// 解析sidebar获取目录list
|
||||
const info = await parseSidebar(res.data, {
|
||||
owner: owner,
|
||||
repo: projectsId,
|
||||
projectId
|
||||
})
|
||||
const {menuList, sidebar} = info;
|
||||
setFileList(menuList);
|
||||
if (res.data.length) {
|
||||
setCheckItem(res.data[0]);
|
||||
};
|
||||
|
|
@ -45,14 +56,14 @@ export default (props) => {
|
|||
showNotification("加载失败")
|
||||
}
|
||||
});
|
||||
}, [project])
|
||||
}, [])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
projectsId && checkItem.name && getWiki({
|
||||
owner: owner,
|
||||
repo: projectsId,
|
||||
pagename: checkItem.name,
|
||||
pageName: checkItem.name,
|
||||
projectId: projectId,
|
||||
}).then(res => {
|
||||
if (res && res.message === "200") {
|
||||
|
|
@ -61,11 +72,7 @@ export default (props) => {
|
|||
showNotification("加载失败")
|
||||
}
|
||||
});
|
||||
}, [project, checkItem]);
|
||||
|
||||
function goEdit() {
|
||||
history.push(`/${owner}/${projectsId}/wiki/edit/${checkItem.name}`);
|
||||
}
|
||||
}, [checkItem]);
|
||||
|
||||
function goBack() {
|
||||
history.push(`/${owner}/${projectsId}/wiki`);
|
||||
|
|
@ -90,6 +97,28 @@ export default (props) => {
|
|||
window.open(`${httpUrl}/api/wikiExport/wikiExport-wrapper?repoName=${projectsId}&owner=${owner}&type=${type}&projectName=${projectName}&projectId=${projectId}`);
|
||||
}
|
||||
|
||||
function renderTreeNodes(data) {
|
||||
return data && data.length > 0 && data.map((item) => {
|
||||
let title = item.title.trim();
|
||||
const isFile = title.startsWith('[[') && title.endsWith(']]');
|
||||
title = isFile ? title.substring(2,title.length-2) : title.substring(2, title.length);
|
||||
return (
|
||||
<TreeNode title={title} key={key++} isLeaf={isFile} dataRef={item}>
|
||||
{renderTreeNodes(item.children)}
|
||||
</TreeNode>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function selectTree(keys,event){
|
||||
let {title, key} = event.node.props.dataRef;
|
||||
title = title.trim()
|
||||
const isFile = title.startsWith('[[') && title.endsWith(']]');
|
||||
if(isFile){
|
||||
title = title.substring(2,title.length-2);
|
||||
setCheckItem({name: title, key});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="wiki-preview">
|
||||
|
|
@ -99,16 +128,16 @@ export default (props) => {
|
|||
<span className="ml10">{projectName}</span>
|
||||
</div>
|
||||
<div className="preview-head-right">
|
||||
<span className="copy-desc">克隆地址</span>
|
||||
<span className="copy-desc font-16">克隆地址</span>
|
||||
{
|
||||
checkItem.wiki_clone_link && <InputGroup className="copy-url" compact>
|
||||
fileArr.length && fileArr[0].wiki_clone_link ? <InputGroup className="copy-url" compact>
|
||||
<Select dropdownClassName="wiki-url-type" defaultValue="HTTPS" onChange={(v) => { setUrlType(v) }}>
|
||||
<Option value="HTTPS">HTTPS</Option>
|
||||
<Option value="SSH">SSH</Option>
|
||||
</Select>
|
||||
<Input id="wikiUrl" value={urlType === 'HTTPS' ? checkItem.wiki_clone_link.https : checkItem.wiki_clone_link.ssh} />
|
||||
<Input id="wikiUrl" value={urlType === 'HTTPS' ? fileArr[0].wiki_clone_link.https : fileArr[0].wiki_clone_link.ssh} />
|
||||
<CopyTool className="copy-wiki" inputId="wikiUrl"/>
|
||||
</InputGroup>
|
||||
</InputGroup> : ''
|
||||
}
|
||||
|
||||
<Dropdown overlay={menu}>
|
||||
|
|
@ -119,8 +148,8 @@ export default (props) => {
|
|||
|
||||
|
||||
<div className="wiki-body">
|
||||
<div className="wiki-nav">
|
||||
{
|
||||
<div className="wiki-nav previewWiki">
|
||||
{/* {
|
||||
fileArr.map(item => {
|
||||
return <div className={`${item.name === checkItem.name ? 'active' : ''} wiki-nav-title`} key={item.name} onClick={() => { setCheckItem(item) }}>
|
||||
<span className="nav-title-left" >
|
||||
|
|
@ -129,17 +158,19 @@ export default (props) => {
|
|||
</span>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
} */}
|
||||
{/* 读sidebar转换成目录树 */}
|
||||
{fileList && fileList.length ? <DirectoryTree onSelect={selectTree} defaultExpandAll>
|
||||
{renderTreeNodes(fileList)}
|
||||
</DirectoryTree> : ''}
|
||||
</div>
|
||||
|
||||
<div className="wiki-content">
|
||||
<div className="wiki-content previewContent">
|
||||
<div className="wiki-content-head">
|
||||
<h3>{checkItem.name}</h3>
|
||||
{permission && <Button type="primary" onClick={goEdit}>编辑</Button>}
|
||||
<h3 className='mt10 pb10'>{checkItem.name}</h3>
|
||||
</div>
|
||||
|
||||
<div className="wiki-content-detail editor-content-panel markdown-body" dangerouslySetInnerHTML={{ __html: itemDetail && itemDetail.simple_content }} ></div>
|
||||
{itemDetail && itemDetail.md_content && <RenderHtml className="wiki-content-detail editor-content-panel markdown-body" value={Base64.decode(itemDetail.md_content)} url={history.location}/>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,30 +2,14 @@ import React from 'react';
|
|||
import { Button } from 'antd';
|
||||
// import { addWiki } from '../api';
|
||||
import './index.scss';
|
||||
import UploadWiki from '../components/uploadWiki';
|
||||
|
||||
export default ({ project, isManager, history, showNotification, match, reloadList }) => {
|
||||
export default (props) => {
|
||||
const { project, isManager, history, showNotification, match, reloadList } = props;
|
||||
|
||||
let projectsId = match.params.projectsId;
|
||||
let owner = match.params.owner;
|
||||
|
||||
// function addFile() {
|
||||
// addWiki({
|
||||
// owner,
|
||||
// repo: projectsId,
|
||||
// pagename: 'Home',
|
||||
// name: 'Home',
|
||||
// content: 'Welcome to the wiki!',
|
||||
// commit_message: '',
|
||||
// }).then(res => {
|
||||
// if (res.message === "200") {
|
||||
// reloadList(Math.random());
|
||||
// showNotification("创建成功");
|
||||
// } else {
|
||||
// showNotification("创建失败");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
function addFile() {
|
||||
history.push(`/${owner}/${projectsId}/wiki/add`);
|
||||
}
|
||||
|
|
@ -42,8 +26,10 @@ export default ({ project, isManager, history, showNotification, match, reloadLi
|
|||
<div className="wiki-line"></div>
|
||||
|
||||
{
|
||||
isManager ? <Button type="primary" onClick={addFile}>创建Wiki文档</Button>
|
||||
: <p className="welcome-des">该项目暂时没有创建Wiki</p>
|
||||
isManager ? <div>
|
||||
<Button type="primary" onClick={addFile}>创建Wiki文档</Button>
|
||||
<UploadWiki {...props} butClass="welcomeUploadBtn"/>
|
||||
</div> : <p className="welcome-des">该项目暂时没有创建Wiki</p>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -52,4 +52,8 @@
|
|||
color: #333333;
|
||||
font-weight: 500;
|
||||
}
|
||||
.welcomeUploadBtn{
|
||||
color: $primary-color;
|
||||
border-color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue