forked from Gitlink/forgeplus-react
Compare commits
42 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
f1a6078184 | |
|
|
9aa68eedd6 | |
|
|
90b87a3e9a | |
|
|
914e653b13 | |
|
|
42ce1b31cc | |
|
|
5aac2da26e | |
|
|
c7a69239e5 | |
|
|
9fe7d6471c | |
|
|
35509a24ab | |
|
|
c24a1eca51 | |
|
|
2a1dd2e9cf | |
|
|
aa64820873 | |
|
|
cb38a97da2 | |
|
|
092de9828c | |
|
|
060d9bd4e9 | |
|
|
3a3d043d8e | |
|
|
49141aa7a6 | |
|
|
37d76676a5 | |
|
|
420788ec66 | |
|
|
e1f450cf54 | |
|
|
ab2368dad4 | |
|
|
833f1bfe1a | |
|
|
75a612391c | |
|
|
223b2dbe5d | |
|
|
e3f336acc1 | |
|
|
33d9b6e6dc | |
|
|
d792ba3563 | |
|
|
df2c2d0f06 | |
|
|
0e2f84f5dd | |
|
|
0dcf0ca19c | |
|
|
6f7288729b | |
|
|
f6d5d6c64e | |
|
|
d50e26fb12 | |
|
|
dbcf595d55 | |
|
|
8ba6ad24b0 | |
|
|
3b58518e37 | |
|
|
89bc05060c | |
|
|
2d2b1c25f9 | |
|
|
3ce5494b7f | |
|
|
0b84de9bcd | |
|
|
4da3d1a7ff | |
|
|
155ff88a26 |
16
.babelrc
16
.babelrc
|
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"presets": [
|
|
||||||
"es2015",
|
|
||||||
"react",
|
|
||||||
"stage-2"
|
|
||||||
],
|
|
||||||
"plugins": [[
|
|
||||||
"transform-runtime",
|
|
||||||
{
|
|
||||||
"helpers": false,
|
|
||||||
"polyfill": false,
|
|
||||||
"regenerator": true,
|
|
||||||
"moduleName": "babel-runtime"
|
|
||||||
}
|
|
||||||
]]
|
|
||||||
}
|
|
||||||
16
README.md
16
README.md
|
|
@ -1,16 +0,0 @@
|
||||||
<h3>前端react环境安装:</h3>
|
|
||||||
<p>1、 安装node v6.9.x;此安装包含了node和npm。</p>
|
|
||||||
<p>2、 安装cnpm(命令行): npm install -g cnpm --registry=https://registry.npm.taobao.org</p>
|
|
||||||
<p>3、 安装依赖的js库(public/react目录下<即项目package.json所在目录>,开启命令行): cnpm install</p>
|
|
||||||
<p>4、 如果你的ruby服务使用的是3000端口,则需要在package.json中修改"port"参数的值</p>
|
|
||||||
<p>5、 启动服务(命令行-目录同3): npm start</p>
|
|
||||||
<p>6、 build初始化 npm run build</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3>分支信息:</h3>
|
|
||||||
<p>相关代码提交到对应分支,能上线的代码先提交到develop分支上测试版,测试通过后合并提交到master分支上线正式版</p>
|
|
||||||
<p>master:开发环境(正式环境)</p>
|
|
||||||
<p>develop:测试环境</p>
|
|
||||||
<p>dev_local:本地版本</p>
|
|
||||||
<p>dev_chain:含有区块链相关内容的分支</p>
|
|
||||||
<p>PS:新增加的需求功能先新建新分支开发,在测试版测试没问题后再分别合并到develop和master分支</p>
|
|
||||||
|
|
@ -17,7 +17,16 @@ const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
|
||||||
const paths = require("./paths");
|
const paths = require("./paths");
|
||||||
const getClientEnvironment = require("./env");
|
const getClientEnvironment = require("./env");
|
||||||
|
|
||||||
|
// Some apps do not use client-side routing with pushState.
|
||||||
|
// For these, "homepage" can be set to "." to enable relative asset paths.
|
||||||
let publicPath = "/react/build/";
|
let publicPath = "/react/build/";
|
||||||
|
// let nodeEnv = process.env.NODE_ENV
|
||||||
|
// if (nodeEnv === 'testBuild') {
|
||||||
|
// publicPath = 'https://testforgeplus.trustie.net/react/build/';
|
||||||
|
// }
|
||||||
|
// if (nodeEnv === 'production') {
|
||||||
|
// publicPath = 'https://forgeplus.trustie.net/react/build/';
|
||||||
|
// }
|
||||||
const publicUrl = publicPath.slice(0, -1);
|
const publicUrl = publicPath.slice(0, -1);
|
||||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
|
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
|
||||||
const env = getClientEnvironment(publicPath);
|
const env = getClientEnvironment(publicPath);
|
||||||
|
|
@ -132,6 +141,7 @@ module.exports = {
|
||||||
name: "static/media/[name].[hash:8].[ext]",
|
name: "static/media/[name].[hash:8].[ext]",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Process JS with Babel.
|
||||||
{
|
{
|
||||||
test: /\.(js|jsx|mjs)$/,
|
test: /\.(js|jsx|mjs)$/,
|
||||||
include: paths.appSrc,
|
include: paths.appSrc,
|
||||||
|
|
@ -151,8 +161,10 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
|
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: MiniCssExtractPlugin.loader,
|
loader: MiniCssExtractPlugin.loader,
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "forge",
|
"name": "educoder",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
"array-flatten": "^2.1.2",
|
"array-flatten": "^2.1.2",
|
||||||
"autoprefixer": "7.1.6",
|
"autoprefixer": "7.1.6",
|
||||||
"axios": "^0.18.1",
|
"axios": "^0.18.1",
|
||||||
|
"babel-core": "6.26.0",
|
||||||
"babel-eslint": "7.2.3",
|
"babel-eslint": "7.2.3",
|
||||||
"babel-jest": "20.0.3",
|
"babel-jest": "20.0.3",
|
||||||
"babel-loader": "7.1.2",
|
"babel-loader": "7.1.2",
|
||||||
|
|
@ -22,11 +23,9 @@
|
||||||
"chalk": "1.1.3",
|
"chalk": "1.1.3",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"clipboard": "^2.0.6",
|
"clipboard": "^2.0.6",
|
||||||
"code-prettify": "^0.1.0",
|
|
||||||
"codemirror": "^5.53.0",
|
"codemirror": "^5.53.0",
|
||||||
"connected-react-router": "4.4.1",
|
"connected-react-router": "4.4.1",
|
||||||
"css-loader": "^3.5.2",
|
"css-loader": "^3.5.2",
|
||||||
"dompurify": "^2.0.15",
|
|
||||||
"dotenv": "4.0.0",
|
"dotenv": "4.0.0",
|
||||||
"dotenv-expand": "4.2.0",
|
"dotenv-expand": "4.2.0",
|
||||||
"echarts": "^4.7.0",
|
"echarts": "^4.7.0",
|
||||||
|
|
@ -86,10 +85,9 @@
|
||||||
"react-hot-loader": "^4.12.20",
|
"react-hot-loader": "^4.12.20",
|
||||||
"react-infinite-scroller": "^1.2.4",
|
"react-infinite-scroller": "^1.2.4",
|
||||||
"react-loadable": "^5.3.1",
|
"react-loadable": "^5.3.1",
|
||||||
"react-monaco-editor": "0.37",
|
"react-monaco-editor": "0.36",
|
||||||
"react-player": "^1.15.3",
|
"react-player": "^1.15.3",
|
||||||
"react-redux": "5.0.7",
|
"react-redux": "5.0.7",
|
||||||
"react-resizable": "^1.10.1",
|
|
||||||
"react-router": "^4.2.0",
|
"react-router": "^4.2.0",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-split-pane": "^0.1.91",
|
"react-split-pane": "^0.1.91",
|
||||||
|
|
@ -111,9 +109,7 @@
|
||||||
"webpack-dev-server": "^3.10.3",
|
"webpack-dev-server": "^3.10.3",
|
||||||
"webpack-manifest-plugin": "^2.2.0",
|
"webpack-manifest-plugin": "^2.2.0",
|
||||||
"whatwg-fetch": "2.0.3",
|
"whatwg-fetch": "2.0.3",
|
||||||
"wrap-md-editor": "^0.2.20",
|
"wrap-md-editor": "^0.2.20"
|
||||||
"xterm": "4.8.1",
|
|
||||||
"xterm-addon-fit": "0.4.0"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --max_old_space_size=15360 scripts/start.js",
|
"start": "node --max_old_space_size=15360 scripts/start.js",
|
||||||
|
|
@ -184,13 +180,7 @@
|
||||||
"port": "3007",
|
"port": "3007",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/runtime": "7.0.0-beta.51",
|
"@babel/runtime": "7.0.0-beta.51",
|
||||||
"babel-cli": "^6.26.0",
|
|
||||||
"babel-core": "^6.26.0",
|
|
||||||
"babel-plugin-import": "^1.13.0",
|
"babel-plugin-import": "^1.13.0",
|
||||||
"babel-plugin-transform-runtime": "^6.23.0",
|
|
||||||
"babel-preset-es2015": "^6.24.1",
|
|
||||||
"babel-preset-react": "^6.24.1",
|
|
||||||
"babel-preset-stage-2": "^6.24.1",
|
|
||||||
"compression-webpack-plugin": "^1.1.12",
|
"compression-webpack-plugin": "^1.1.12",
|
||||||
"concat": "^1.0.3",
|
"concat": "^1.0.3",
|
||||||
"happypack": "^5.0.1",
|
"happypack": "^5.0.1",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1617,7 +1617,7 @@ a.decoration {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt8 {
|
.mt8 {
|
||||||
margin-top: 8px !important;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt9 {
|
.mt9 {
|
||||||
|
|
@ -2325,9 +2325,6 @@ input::-ms-clear {
|
||||||
background-color: #F5F5F5;
|
background-color: #F5F5F5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-close{
|
|
||||||
top:8px!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.newContainer {
|
.newContainer {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
|
@ -2346,6 +2343,7 @@ input::-ms-clear {
|
||||||
/*中间部分宽度固定为1200*/
|
/*中间部分宽度固定为1200*/
|
||||||
.newMain {
|
.newMain {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
padding-bottom: 110px;
|
||||||
min-width: 1200px;
|
min-width: 1200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3928,7 +3926,6 @@ html>body #ajax-indicator {
|
||||||
.markdown-body {
|
.markdown-body {
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
width:100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.RightPaneDrawer .ant-drawer-content {
|
.RightPaneDrawer .ant-drawer-content {
|
||||||
|
|
@ -3946,7 +3943,7 @@ html>body #ajax-indicator {
|
||||||
|
|
||||||
.head-nav {
|
.head-nav {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
height: 70px;
|
height: 60px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: 780px;
|
min-width: 780px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
@ -3959,29 +3956,29 @@ html>body #ajax-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
height: 70px;
|
height: 60px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.head-nav ul#header-nav li {
|
.head-nav ul#header-nav li {
|
||||||
float: left;
|
float: left;
|
||||||
height: 70px;
|
height: 60px;
|
||||||
line-height: 70px;
|
line-height: 60px;
|
||||||
|
margin-right: 30px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding:0px 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.head-nav ul#header-nav li a {
|
.head-nav ul#header-nav li a {
|
||||||
display: block;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: #333;
|
color: #333333;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.head-nav ul#header-nav li a:hover,.head-nav ul#header-nav li.active a {
|
.head-nav ul#header-nav li a:hover {
|
||||||
color: #5091FF;
|
color: #5091FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3989,10 +3986,13 @@ html>body #ajax-indicator {
|
||||||
margin-right: 0px
|
margin-right: 0px
|
||||||
}
|
}
|
||||||
|
|
||||||
.head-nav ul#header-nav li.active{
|
.head-nav ul#header-nav li.active a:first-child {
|
||||||
/* background-color: #3B3B3B; */
|
color: #459be5 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.head-nav ul#header-nav li.active p {
|
||||||
|
color: #459be5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.head-nav ul#header-nav li p:hover {
|
.head-nav ul#header-nav li p:hover {
|
||||||
color: #cccccc;
|
color: #cccccc;
|
||||||
|
|
@ -4005,23 +4005,23 @@ html>body #ajax-indicator {
|
||||||
color: #fff
|
color: #fff
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .head-nav ul#header-nav li.active div ul li a {
|
.head-nav ul#header-nav li.active div ul li a {
|
||||||
color: #000 !important;
|
color: #000 !important;
|
||||||
} */
|
}
|
||||||
|
|
||||||
/* .head-nav ul#header-nav li.active div ul li a:hover {
|
.head-nav ul#header-nav li.active div ul li a:hover {
|
||||||
color: #FFF !important;
|
color: #FFF !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.head-nav ul#header-nav li.active ul li a {
|
.head-nav ul#header-nav li.active ul li a {
|
||||||
color: #000 !important;
|
color: #000 !important;
|
||||||
} */
|
}
|
||||||
|
|
||||||
/* .head-nav ul#header-nav li.active ul li a:hover {
|
.head-nav ul#header-nav li.active ul li a:hover {
|
||||||
color: #FFF !important;
|
color: #FFF !important;
|
||||||
} */
|
}
|
||||||
|
|
||||||
/* .head-nav ul#header-nav li.active:after {
|
.head-nav ul#header-nav li.active:after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
|
|
@ -4031,7 +4031,7 @@ html>body #ajax-indicator {
|
||||||
height: 2px;
|
height: 2px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
background-color: #459be5;
|
background-color: #459be5;
|
||||||
} */
|
}
|
||||||
|
|
||||||
.head-right {
|
.head-right {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
@ -4107,6 +4107,21 @@ em.vertical-line {
|
||||||
|
|
||||||
/* 右侧内容宽度变化的话,需要调整posi-search right的值*/
|
/* 右侧内容宽度变化的话,需要调整posi-search right的值*/
|
||||||
|
|
||||||
|
/*底部*/
|
||||||
|
.newFooter {
|
||||||
|
max-height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newFooter {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: #323232;
|
||||||
|
clear: both;
|
||||||
|
min-width: 1200px;
|
||||||
|
z-index: 8;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.footercon {
|
.footercon {
|
||||||
border-bottom: 1px solid #47494d;
|
border-bottom: 1px solid #47494d;
|
||||||
|
|
@ -6699,16 +6714,4 @@ ul.count_ul li:not(:last-child):after {
|
||||||
|
|
||||||
.ant-notification {
|
.ant-notification {
|
||||||
z-index: 10001 !important;
|
z-index: 10001 !important;
|
||||||
}
|
|
||||||
input.ant-input-lg::placeholder{
|
|
||||||
font-size: 14px !important;
|
|
||||||
}
|
|
||||||
p{
|
|
||||||
margin-bottom: 0px!important;
|
|
||||||
}
|
|
||||||
.toprightNum{
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
top:4px;
|
|
||||||
color: #999;
|
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 733 KiB After Width: | Height: | Size: 502 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,111 +1,40 @@
|
||||||
|
|
||||||
.CodeMirror-merge {
|
.maxW50{max-width: 50%;}
|
||||||
position: relative;
|
.minW50{min-width: 50%;}
|
||||||
white-space: pre;
|
.width70{width: 70%;}
|
||||||
|
.width30{width: 30%;}
|
||||||
|
.custom-commit-tabs .ant-tabs-bar{border-bottom: none;}
|
||||||
|
.maxW200px{max-width: 200px;}
|
||||||
|
.pr_tags_open{
|
||||||
|
background: #28BD6C !important;
|
||||||
|
color: #fff !important;
|
||||||
|
border: none !important;
|
||||||
}
|
}
|
||||||
|
.pr_tags_closed{
|
||||||
.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
|
background: #FA6400 !important;
|
||||||
min-height:50px;
|
color: #fff !important;
|
||||||
|
border: none !important;
|
||||||
}
|
}
|
||||||
|
.pr_tags_merged{
|
||||||
.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 48%; }
|
background: #4C9ED3 !important;
|
||||||
.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 4%; }
|
color: #fff !important;
|
||||||
.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
|
border: none !important;
|
||||||
.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
|
|
||||||
|
|
||||||
.CodeMirror-merge-pane {
|
|
||||||
display: inline-block;
|
|
||||||
white-space: normal;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
}
|
||||||
.CodeMirror-merge-pane-rightmost {
|
.ant-btn-success{
|
||||||
position: absolute;
|
border: 1px solid #28BD6C !important;
|
||||||
right: 0px;
|
color: #28BD6C !important;
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
.ant-btn-success:hover{
|
||||||
.CodeMirror-merge-gap {
|
background: #28BD6C !important;
|
||||||
z-index: 2;
|
color:#fff !important;
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
background: #515151;
|
|
||||||
}
|
}
|
||||||
|
.display-flex{display: flex !important;}
|
||||||
.CodeMirror-merge-scrolllock-wrap {
|
.merge-flex1{flex:1}
|
||||||
position: absolute;
|
.ant-tag.pr-branch-tag{
|
||||||
bottom: 0; left: 50%;
|
border-radius: 12px;
|
||||||
}
|
height: 24px;
|
||||||
.CodeMirror-merge-scrolllock {
|
background: rgba(241,248,255,1);
|
||||||
position: relative;
|
border: none;
|
||||||
left: -50%;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
padding: 0 10px;
|
||||||
color: #d8d8d8;
|
}
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
|
|
||||||
position: absolute;
|
|
||||||
left: 0; top: 0;
|
|
||||||
right: 0; bottom: 0;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-merge-copy {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #ce374b;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-merge-copy-reverse {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #44c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
|
|
||||||
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
|
|
||||||
|
|
||||||
.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
|
|
||||||
background-position: bottom left;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
|
|
||||||
background-position: bottom left;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-merge-r-chunk { background: #9a6868; }
|
|
||||||
.CodeMirror-merge-r-chunk-start { /*border-top: 1px solid #ee8; */}
|
|
||||||
.CodeMirror-merge-r-chunk-end {/* border-bottom: 1px solid #ee8; */}
|
|
||||||
.CodeMirror-merge-r-connect { fill:#9a6868;}
|
|
||||||
|
|
||||||
.CodeMirror-merge-l-chunk { background: #eef; }
|
|
||||||
.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
|
|
||||||
.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
|
|
||||||
.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
|
|
||||||
|
|
||||||
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
|
|
||||||
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
|
|
||||||
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
|
|
||||||
|
|
||||||
.CodeMirror-merge-collapsed-widget:before {
|
|
||||||
content: "(...)";
|
|
||||||
}
|
|
||||||
.CodeMirror-merge-collapsed-widget {
|
|
||||||
cursor: pointer;
|
|
||||||
color: #88b;
|
|
||||||
background: #eef;
|
|
||||||
border: 1px solid #ddf;
|
|
||||||
font-size: 90%;
|
|
||||||
padding: 0 3px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }
|
|
||||||
|
|
@ -1,30 +1,53 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name=”Keywords” Content=”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″>
|
<meta name=”Keywords” Content=”EduCoder,信息技术实践教学,精品课程网,慕课MOOC″>
|
||||||
<meta name=”Keywords” Content=”TrustieOpenSourceProject″>
|
<meta name=”Keywords” Content=”实践课程,项目实战,java实训,python实战,人工智能技术,后端开发学习,移动开发入门″>
|
||||||
<meta name=”Keywords” Content=”issue,bug,tracker,软件工程,课程实践″>
|
<meta name=”Keywords” Content=”翻转课堂,高效课堂创建,教学模式″>
|
||||||
<meta name=”Description” Content=”持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动”>
|
<meta name=”Keywords” Content=”实训项目,python教程,C语言入门,java书,php后端开发,app前端开发,数据库技术″>
|
||||||
|
<meta name=”Keywords” Content=”在线竞赛,计算机应用大赛,编程大赛,大学生计算机设计大赛,全国高校绿色计算机大赛″>
|
||||||
|
<meta name=”Description”
|
||||||
|
Content=”EduCoder是信息技术类实践教学平台。EduCoder涵盖了计算机、大数据、云计算、人工智能、软件工程、物联网等专业课程。超10000个实训案例及22000个技能评测点,建立学、练、评、测一体化实验环境。”>
|
||||||
|
<meta name=”Description”
|
||||||
|
Content=”EduCoder实践课程,旨在于通过企业级实战实训案例,帮助众多程序员提升各项业务能力。解决学生、学员、企业员工等程序设计能力、算法设计能力、问题求解能力、应用开发能力、系统运维能力等。”>
|
||||||
|
<meta name=”Description”
|
||||||
|
Content=”EduCoder翻转课堂教学模式,颠覆了传统教学模式,让教师与学生的关系由“权威”变成了“伙伴”。将学习的主动权转交给学生,使学生可个性化化学,学生的学习主体得到了彰显。”>
|
||||||
|
<meta name=”Description” Content=”EduCoder实训项目为单个知识点关卡实践训练,帮助学生巩固单一弱点,强化学习。 ”>
|
||||||
|
<meta name=”Description” Content=”EduCoder实践教学平台,各类大赛为进一步提高各类学生综合运用高级语言程序设计能力,培养创新意识和实践探索精神,发掘优秀软件人才。 ”>
|
||||||
<meta name="theme-color" content="#000000">
|
<meta name="theme-color" content="#000000">
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
|
<script type="text/javascript">
|
||||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
|
window.__isR = true;
|
||||||
|
if (
|
||||||
|
(navigator.userAgent.indexOf('MSIE 9') != -1
|
||||||
|
|| navigator.userAgent.indexOf('MSIE 10') != -1)
|
||||||
|
&&
|
||||||
|
location.pathname.indexOf("/compatibility") == -1) {
|
||||||
|
location.href = '/compatibility.html'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<link rel=" stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
|
||||||
|
<link rel=" stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
|
||||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
|
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
|
||||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/merge.css">
|
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/merge.css">
|
||||||
<%= htmlWebpackPlugin.tags.headTags %>
|
<%= htmlWebpackPlugin.tags.headTags %>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!--用于markdown转html -->
|
<!--用于markdown转html -->
|
||||||
<div id="md_div" style="display: none;"></div>
|
<div id="md_div" style="display: none;"></div>
|
||||||
<div id="root" class="page -layout-v -fit widthunit"></div>
|
<div id="root" class="page -layout-v -fit widthunit"></div>
|
||||||
<div id="picture_display" style="display: none;"></div>
|
<div id="picture_display" style="display: none;"></div>
|
||||||
<script src="%PUBLIC_URL%js/jquery-1.8.3.min.js"></script>
|
|
||||||
<script src="%PUBLIC_URL%js/js_min_all.js"></script>
|
<script src="%PUBLIC_URL%js/js_min_all.js"></script>
|
||||||
<script src="%PUBLIC_URL%js/codemirror/codemirror.js"></script>
|
<script src="%PUBLIC_URL%js/codemirror/codemirror.js"></script>
|
||||||
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
|
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
|
||||||
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
|
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
|
||||||
<%= htmlWebpackPlugin.tags.bodyTags %>
|
<%= htmlWebpackPlugin.tags.bodyTags %>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -66,7 +66,6 @@ body {
|
||||||
margin:10px 0px!important;
|
margin:10px 0px!important;
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
line-height: 2 !important;
|
line-height: 2 !important;
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body>p {
|
.markdown-body>p {
|
||||||
|
|
|
||||||
42
src/App.js
42
src/App.js
|
|
@ -9,7 +9,13 @@ import {
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import LoginDialog from './modules/login/LoginDialog';
|
import LoginDialog from './modules/login/LoginDialog';
|
||||||
import 'babel-polyfill';
|
import Notcompletedysl from './modules/user/Notcompletedysl';
|
||||||
|
import Trialapplicationysl from './modules/login/Trialapplicationysl';
|
||||||
|
import Trialapplicationreview from './modules/user/Trialapplicationreview';
|
||||||
|
import AccountProfile from "./modules/user/AccountProfile";
|
||||||
|
import Accountnewprofile from './modules/user/Accountnewprofile';
|
||||||
|
import Certifiedprofessional from './modules/modals/Certifiedprofessional';
|
||||||
|
|
||||||
import Loading from './Loading'
|
import Loading from './Loading'
|
||||||
|
|
||||||
import Loadable from 'react-loadable';
|
import Loadable from 'react-loadable';
|
||||||
|
|
@ -40,11 +46,6 @@ const Projects = Loadable({
|
||||||
loader: () => import('./forge/Index'),
|
loader: () => import('./forge/Index'),
|
||||||
loading: Loading,
|
loading: Loading,
|
||||||
})
|
})
|
||||||
//forge项目-devOps详情
|
|
||||||
const OpsDetail = Loadable({
|
|
||||||
loader: () => import('./forge/DevOps/opsDetail'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
//403页面
|
//403页面
|
||||||
const Shixunauthority = Loadable({
|
const Shixunauthority = Loadable({
|
||||||
loader: () => import('./modules/403/Shixunauthority'),
|
loader: () => import('./modules/403/Shixunauthority'),
|
||||||
|
|
@ -70,10 +71,6 @@ const OrganizeIndex = Loadable({
|
||||||
loader: () => import('./forge/Team/Index'),
|
loader: () => import('./forge/Team/Index'),
|
||||||
loading: Loading,
|
loading: Loading,
|
||||||
})
|
})
|
||||||
const EducoderLogin = Loadable({
|
|
||||||
loader: () => import('./modules/login/EducoderLogin'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
@ -211,18 +208,15 @@ class App extends Component {
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ConfigProvider locale={zhCN}>
|
<ConfigProvider locale={zhCN}>
|
||||||
<MuiThemeProvider theme={theme}>
|
<MuiThemeProvider theme={theme}>
|
||||||
|
<Accountnewprofile {...this.props}{...this.state} />
|
||||||
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
|
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
|
||||||
|
<Notcompletedysl {...this.props} {...this.state}></Notcompletedysl>
|
||||||
|
<Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl>
|
||||||
|
<Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview>
|
||||||
|
<AccountProfile {...this.props} {...this.state} />
|
||||||
|
<Certifiedprofessional {...this.props} {...this.state} ModalCancelsy={this.ModalCancelsy} ModalshowCancelsy={this.ModalshowCancelsy} />
|
||||||
<Router>
|
<Router>
|
||||||
<Switch>
|
<Switch>
|
||||||
{/*项目*/}
|
|
||||||
<Route
|
|
||||||
path={"/projects/:owner/:projectId/devops/:opsId/detail"}
|
|
||||||
render={
|
|
||||||
(props) => {
|
|
||||||
return (<OpsDetail {...this.props} {...props} {...this.state} />)
|
|
||||||
}
|
|
||||||
}>
|
|
||||||
</Route>
|
|
||||||
{/*项目*/}
|
{/*项目*/}
|
||||||
<Route
|
<Route
|
||||||
path={"/projects"}
|
path={"/projects"}
|
||||||
|
|
@ -232,14 +226,6 @@ class App extends Component {
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
|
||||||
path="/register"
|
|
||||||
render={
|
|
||||||
(props) => {
|
|
||||||
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{/*403*/}
|
{/*403*/}
|
||||||
<Route path="/403" component={Shixunauthority} />
|
<Route path="/403" component={Shixunauthority} />
|
||||||
|
|
||||||
|
|
@ -247,7 +233,7 @@ class App extends Component {
|
||||||
<Route path={"/organize"}
|
<Route path={"/organize"}
|
||||||
render={
|
render={
|
||||||
(props) => {
|
(props) => {
|
||||||
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
|
return (<OrganizeIndex {...this.props} {...props} {...this.state} />)
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { requestProxy } from "./indexEduplus2RequestProxy";
|
import { requestProxy } from "./indexEduplus2RequestProxy";
|
||||||
import { broadcastChannelOnmessage, isDev, queryString } from 'educoder';
|
import { broadcastChannelOnmessage , isDev, queryString } from 'educoder';
|
||||||
import { notification } from 'antd';
|
import { notification } from 'antd';
|
||||||
|
import cookie from 'react-cookies';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
const $ = window.$;
|
||||||
|
let timestamp;
|
||||||
|
let checkSubmitFlg = false;
|
||||||
let message501 = false;
|
let message501 = false;
|
||||||
|
|
||||||
broadcastChannelOnmessage('refreshPage', () => {
|
broadcastChannelOnmessage('refreshPage', () => {
|
||||||
window.location.reload();
|
window.location.reload()
|
||||||
})
|
})
|
||||||
|
|
||||||
function locationurl(list) {
|
function locationurl(list) {
|
||||||
if (window.location.port !== "3007") {
|
if (window.location.port === "3007") {
|
||||||
window.location.href = list
|
|
||||||
|
} else {
|
||||||
|
window.location.href = list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO 开发期多个身份切换
|
// TODO 开发期多个身份切换
|
||||||
|
|
@ -24,22 +29,59 @@ if (isDev) {
|
||||||
parsed = queryString.parse(_search);
|
parsed = queryString.parse(_search);
|
||||||
}
|
}
|
||||||
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
|
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
|
||||||
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
|
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
|
||||||
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
|
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
|
||||||
}
|
}
|
||||||
|
function clearAllCookie() {
|
||||||
|
cookie.remove('_educoder_session', { path: '/' });
|
||||||
|
cookie.remove('autologin_trustie', { path: '/' });
|
||||||
|
setpostcookie()
|
||||||
|
}
|
||||||
|
clearAllCookie();
|
||||||
|
function setpostcookie() {
|
||||||
|
|
||||||
|
const str = window.location.pathname;
|
||||||
|
let newdomain = ".educoder.net"
|
||||||
|
if (str.indexOf("/wxcode") !== -1) {
|
||||||
|
console.log("123")
|
||||||
|
cookie.remove('_educoder_session', { path: '/' });
|
||||||
|
cookie.remove('autologin_trustie', { path: '/' });
|
||||||
|
const _params = window.location.search;
|
||||||
|
if (_params) {
|
||||||
|
let _search = _params.split('?')[1];
|
||||||
|
let _educoder_sessions = _search.split('&')[0].split('=');
|
||||||
|
cookie.save('_educoder_session', _educoder_sessions[1], { domain: '.educoder.net', path: '/' });
|
||||||
|
let autologin_trusties = _search.split('&')[1].split('=');
|
||||||
|
cookie.save('autologin_trustie', autologin_trusties[1], { domain: '.educoder.net', path: '/' });
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setpostcookie();
|
||||||
|
|
||||||
window._debugType = debugType;
|
window._debugType = debugType;
|
||||||
export function initAxiosInterceptors(props) {
|
export function initAxiosInterceptors(props) {
|
||||||
// 判断网络是否连接
|
initOnlineOfflineListener()
|
||||||
initOnlineOfflineListener();
|
// TODO 避免重复的请求 https://github.com/axios/axios#cancellation
|
||||||
|
var
|
||||||
|
proxy = "http://localhost:3000"
|
||||||
|
proxy = "https://testforgeplus.trustie.net"
|
||||||
|
|
||||||
var proxy = "https://testforgeplus.trustie.net";
|
const requestMap = {};
|
||||||
|
window.setfalseInRequestMap = function (keyName) {
|
||||||
|
requestMap[keyName] = false;
|
||||||
|
}
|
||||||
//响应前的设置
|
//响应前的设置
|
||||||
axios.interceptors.request.use(
|
axios.interceptors.request.use(
|
||||||
config => {
|
config => {
|
||||||
if(config.url.indexOf("http") !== -1) {
|
setpostcookie()
|
||||||
|
clearAllCookie()
|
||||||
|
|
||||||
|
if (config.url.indexOf(proxy) !== -1 || config.url.indexOf(':') !== -1) {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
requestProxy(config);
|
requestProxy(config)
|
||||||
|
|
||||||
let url = `/api${config.url}`;
|
let url = `/api${config.url}`;
|
||||||
|
|
||||||
if (`${config[0]}` !== `true`) {
|
if (`${config[0]}` !== `true`) {
|
||||||
|
|
@ -53,18 +95,26 @@ export function initAxiosInterceptors(props) {
|
||||||
} else {
|
} else {
|
||||||
config.url = url;
|
config.url = url;
|
||||||
}
|
}
|
||||||
|
setpostcookie();
|
||||||
|
}
|
||||||
|
if (config.url.indexOf('update_file') === -1) {
|
||||||
|
requestMap[config.url] = true;
|
||||||
|
|
||||||
|
window.setTimeout("setfalseInRequestMap('" + config.url + "')", 900)
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
axios.interceptors.response.use(function (response) {
|
axios.interceptors.response.use(function (response) {
|
||||||
|
|
||||||
|
// console.log(".............")
|
||||||
if (response === undefined) {
|
if (response === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const config = response.config;
|
const config = response.config
|
||||||
if (response.data.status === -1) {
|
if (response.data.status === -1) {
|
||||||
if (window.location.pathname.startsWith('/tasks/')) {
|
if (window.location.pathname.startsWith('/tasks/')) {
|
||||||
props.showSnackbar(response.data.message || '服务器异常,请联系管理员。')
|
props.showSnackbar(response.data.message || '服务器异常,请联系管理员。')
|
||||||
|
|
@ -77,10 +127,12 @@ export function initAxiosInterceptors(props) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new axios.Cancel('Operation canceled by the user.');
|
throw new axios.Cancel('Operation canceled by the user.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.data.status === 403 || response.data.status === "403") {
|
if (response.data.status === 403 || response.data.status === "403") {
|
||||||
|
|
||||||
locationurl('/403');
|
locationurl('/403');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,6 +159,10 @@ export function initAxiosInterceptors(props) {
|
||||||
message501 = false
|
message501 = false
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
requestMap[response.config.url] = false;
|
||||||
|
setpostcookie();
|
||||||
return response;
|
return response;
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
|
|
||||||
class Loading extends Component {
|
class Loading extends Component {
|
||||||
|
|
@ -9,6 +12,7 @@ class Loading extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
// Loading
|
||||||
return (
|
return (
|
||||||
<div className="App" style={{ minHeight: '800px', width: "100%" }}>
|
<div className="App" style={{ minHeight: '800px', width: "100%" }}>
|
||||||
<style>
|
<style>
|
||||||
|
|
|
||||||
|
|
@ -11,26 +11,11 @@ export function getImageUrl(path) {
|
||||||
// https://www.educoder.net
|
// https://www.educoder.net
|
||||||
// https://testbdweb.trustie.net
|
// https://testbdweb.trustie.net
|
||||||
// const local = 'http://localhost:3000'
|
// const local = 'http://localhost:3000'
|
||||||
const local = 'https://testforgeplus.trustie.net';
|
const local = 'https://testforgeplus.trustie.net/'
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
return `${local}/${path}`
|
return `${local}/${path}`
|
||||||
}
|
}
|
||||||
return `${path}`;
|
return `/${path}`;
|
||||||
}
|
|
||||||
|
|
||||||
export function getImage(path) {
|
|
||||||
// https://www.educoder.net
|
|
||||||
// https://testbdweb.trustie.net
|
|
||||||
// const local = 'http://localhost:3000'
|
|
||||||
const local = 'https://testforgeplus.trustie.net/';
|
|
||||||
if(path.indexOf("http://")===-1){
|
|
||||||
if (isDev) {
|
|
||||||
return `${local}/images/${path}`
|
|
||||||
}
|
|
||||||
return `/${path}`;
|
|
||||||
}else{
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getcdnImageUrl(path) {
|
export function getcdnImageUrl(path) {
|
||||||
|
|
@ -93,6 +78,14 @@ export function setImagesUrl(path){
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUrl(path, goTest) {
|
export function getUrl(path, goTest) {
|
||||||
|
// https://www.educoder.net
|
||||||
|
// https://testbdweb.trustie.net
|
||||||
|
|
||||||
|
// 如果想所有url定位到测试版,可以反注释掉下面这行
|
||||||
|
//goTest = true
|
||||||
|
// testbdweb.educoder.net testbdweb.trustie.net
|
||||||
|
// const local = goTest ? 'https://testeduplus2.educoder.net' : 'http://localhost:3000'
|
||||||
|
// const local = 'https://testeduplus2.educoder.net'
|
||||||
const local = 'https://testforgeplus.trustie.net'
|
const local = 'https://testforgeplus.trustie.net'
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
return `${local}${path?path:''}`
|
return `${local}${path?path:''}`
|
||||||
|
|
@ -162,28 +155,28 @@ export function getmyUrl(geturl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUploadActionUrl(path, goTest) {
|
export function getUploadActionUrl(path, goTest) {
|
||||||
return `${getUrl()}/api/attachments.json${isDev ?`${isDev ?`?debug=${window._debugType || 'admin'}` : ""}` : ""}`;
|
return `${getUrl()}/api/attachments.json?debug=${window._debugType || 'admin'}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUploadLogoActionUrl() {
|
export function getUploadLogoActionUrl() {
|
||||||
return `${getUrl()}/api/resumes/logo.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`;
|
return `${getUrl()}/api/resumes/logo.json?debug=${window._debugType || 'admin'}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUploadActionUrltwo(id) {
|
export function getUploadActionUrltwo(id) {
|
||||||
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
|
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json?debug=${window._debugType || 'admin'}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUploadActionUrlthree() {
|
export function getUploadActionUrlthree() {
|
||||||
return `${getUrlmys()}/api/jupyters/import_with_tpm.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
|
return `${getUrlmys()}/api/jupyters/import_with_tpm.json?debug=${window._debugType || 'admin'}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getupload_git_file(id) {
|
export function getupload_git_file(id) {
|
||||||
return `${getUrlmys()}/api/shixuns/${id}/upload_git_file.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
|
return `${getUrlmys()}/api/shixuns/${id}/upload_git_file.json?debug=${window._debugType || 'admin'}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getUploadActionUrlOfAuth(id) {
|
export function getUploadActionUrlOfAuth(id) {
|
||||||
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
|
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json?debug=${window._debugType || 'admin'}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRandomNumber(type) {
|
export function getRandomNumber(type) {
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ function CommentItem({
|
||||||
const commentAvatar = (author) => (
|
const commentAvatar = (author) => (
|
||||||
<img
|
<img
|
||||||
className="item-flex flex-image"
|
className="item-flex flex-image"
|
||||||
src={author.image_url ? getImageUrl(`/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
|
src={author.image_url ? getImageUrl(`images/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,13 @@ export const formatDelta = (deltas) => {
|
||||||
alt="${alt}"
|
alt="${alt}"
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
|
// text = "<img src="+url+" width='60px' height='30px' onclick='' alt="+alt+"/>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
formatted.push(text);
|
formatted.push(text);
|
||||||
});
|
});
|
||||||
|
console.log(formatted);
|
||||||
return formatted.join('');
|
return formatted.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getUploadLogoActionUrl as getUploadLogoActionUrl,
|
getUploadLogoActionUrl as getUploadLogoActionUrl,
|
||||||
getImageUrl as getImageUrl,getImage as getImage, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
|
getImageUrl as getImageUrl, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
|
||||||
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
|
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
|
||||||
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl
|
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl
|
||||||
} from './UrlTool';
|
} from './UrlTool';
|
||||||
|
|
@ -76,4 +76,10 @@ export { default as AliyunUploader } from './components/media/AliyunUploader'
|
||||||
export { default as ImageLayer2 } from './hooks/ImageLayer2'
|
export { default as ImageLayer2 } from './hooks/ImageLayer2'
|
||||||
|
|
||||||
// 外部
|
// 外部
|
||||||
|
export { default as CBreadcrumb } from '../modules/courses/common/CBreadcrumb'
|
||||||
export { CNotificationHOC as CNotificationHOC } from '../modules/courses/common/CNotificationHOC'
|
export { CNotificationHOC as CNotificationHOC } from '../modules/courses/common/CNotificationHOC'
|
||||||
|
export { default as ModalWrapper } from '../modules/courses/common/ModalWrapper'
|
||||||
|
export { default as NoneData } from '../modules/courses/coursesPublic/NoneData'
|
||||||
|
|
||||||
|
export { default as WordNumberTextarea } from '../modules/modals/WordNumberTextarea'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,25 +12,32 @@ function ImageLayer2(props) {
|
||||||
setShowImage(false)
|
setShowImage(false)
|
||||||
setImageSrc('')
|
setImageSrc('')
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
const onDelegateClick = (event) => {
|
||||||
$(parentSel).delegate(childSel, "click", (event) => {
|
const imageSrc = event.target.src || event.target.getAttribute('src') || event.target.getAttribute('href')
|
||||||
const imageSrc = event.target.src || event.target.getAttribute('src') || event.target.getAttribute('href')
|
// 判断imageSrc是否是图片
|
||||||
// 判断imageSrc是否是图片
|
const fileName = event.target.innerHTML.trim()
|
||||||
const fileName = event.target.innerHTML.trim();
|
if (isImageExtension((imageSrc && imageSrc.trim())) || isImageExtension(fileName) || event.target.tagName == 'IMG' || (imageSrc && imageSrc.indexOf('base64,')) != -1) {
|
||||||
|
// 非回复里的头像图片; 非emoticons
|
||||||
if (isImageExtension((imageSrc && imageSrc.trim())) || isImageExtension(fileName) || event.target.tagName == 'IMG' || (imageSrc && imageSrc.indexOf('base64,')) != -1) {
|
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
|
||||||
// 非回复里的头像图片; 非emoticons
|
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1 ) {
|
||||||
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
|
setShowImage(true)
|
||||||
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1 ) {
|
setImageSrc(imageSrc)
|
||||||
setShowImage(true)
|
|
||||||
setImageSrc(imageSrc)
|
|
||||||
}
|
|
||||||
event.stopPropagation()
|
|
||||||
event.preventDefault && event.preventDefault()
|
|
||||||
event.originalEvent.preventDefault()
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
})
|
event.stopPropagation()
|
||||||
|
event.preventDefault && event.preventDefault()
|
||||||
|
event.originalEvent.preventDefault()
|
||||||
|
// event.originalEvent.stopPropagation()
|
||||||
|
// event.originalEvent.cancelBubble = true
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
$(parentSel)
|
||||||
|
.delegate(childSel, "click", onDelegateClick);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
$(parentSel).undelegate(childSel, "click", onDelegateClick )
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,12 @@
|
||||||
import marked from 'marked'
|
import marked from 'marked'
|
||||||
import { escape } from 'marked/src/helpers'
|
import { escape, unescape } from 'marked/src/helpers'
|
||||||
|
import { renderToString } from 'katex';
|
||||||
function indentCodeCompensation(raw, text) {
|
|
||||||
const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
|
|
||||||
if (matchIndentToCode === null) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
const indentToCode = matchIndentToCode[1];
|
|
||||||
return text
|
|
||||||
.split('\n')
|
|
||||||
.map(node => {
|
|
||||||
const matchIndentInNode = node.match(/^\s+/);
|
|
||||||
if (matchIndentInNode === null) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
const [indentInNode] = matchIndentInNode;
|
|
||||||
if (indentInNode.length >= indentToCode.length) {
|
|
||||||
return node.slice(indentToCode.length);
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
})
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const latexRegex = /\$+([^\$\n]+?)\$+/g
|
||||||
//兼容之前的 ##标题式写法
|
//兼容之前的 ##标题式写法
|
||||||
|
const headingRegex = /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/
|
||||||
let toc = []
|
let toc = []
|
||||||
let ctx = ["<ul>"]
|
let ctx = ["<ul>"]
|
||||||
const renderer = new marked.Renderer()
|
|
||||||
const headingRegex = /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/
|
|
||||||
|
|
||||||
export function cleanToc() {
|
export function cleanToc() {
|
||||||
toc = []
|
toc = []
|
||||||
|
|
@ -48,115 +27,90 @@ function buildToc(coll, k, level, ctx) {
|
||||||
});
|
});
|
||||||
ctx.push("</ul>")
|
ctx.push("</ul>")
|
||||||
}
|
}
|
||||||
ctx.push("</li>");
|
ctx.push("</li>")
|
||||||
k = buildToc(coll, k, level, ctx)
|
k = buildToc(coll, k, level, ctx)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTocContent() {
|
export function getTocContent() {
|
||||||
buildToc(toc, 0, 0, ctx);
|
buildToc(toc, 0, 0, ctx)
|
||||||
ctx.push("</ul>");
|
ctx.push("</ul>")
|
||||||
return ctx.join("");
|
return ctx.join("")
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenizer = {
|
const tokenizer = {
|
||||||
heading(src) {
|
heading(src) {
|
||||||
const cap = headingRegex.exec(src)
|
const cap = headingRegex.exec(src);
|
||||||
if (cap) {
|
if (cap) {
|
||||||
return {
|
return {
|
||||||
type: 'heading',
|
type: 'heading',
|
||||||
raw: cap[0],
|
raw: cap[0],
|
||||||
depth: cap[1].length,
|
depth: cap[1].length,
|
||||||
text: cap[2]
|
text: cap[2]
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fences(src) {
|
codespan(src) {
|
||||||
const cap = this.rules.block.fences.exec(src)
|
const cap = this.rules.inline.code.exec(src);
|
||||||
if (cap) {
|
if (cap) {
|
||||||
const raw = cap[0]
|
let text = cap[2].replace(/\n/g, ' ');
|
||||||
let text = indentCodeCompensation(raw, cap[3] || '')
|
const hasNonSpaceChars = /[^ ]/.test(text);
|
||||||
const lang = cap[2] ? cap[2].trim() : cap[2]
|
const hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' ');
|
||||||
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
|
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
|
||||||
const id = next_id()
|
text = text.substring(1, text.length - 1)
|
||||||
const expression = text
|
}
|
||||||
text = id
|
let match = text.match(latexRegex)
|
||||||
math_expressions[id] = { type: 'block', expression }
|
if (match) {
|
||||||
|
text = text.replace(latexRegex, (_, $1) => {
|
||||||
|
return renderToString(unescape($1))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
text = escape(text, true);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'code',
|
type: 'codespan',
|
||||||
raw,
|
raw: cap[0],
|
||||||
lang,
|
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const latexRegex = /(?:\${2})([^\n`]+?)(?:\${2})/gi
|
const renderer = {
|
||||||
let katex_count = 0
|
code(code, infostring, escaped) {
|
||||||
const next_id = () => `__special_katext_id_${katex_count++}__`
|
const lang = (infostring || '').match(/\S*/)[0];
|
||||||
let math_expressions = {}
|
if (!lang) {
|
||||||
|
return '<pre class="prettyprint linenums"><code>'
|
||||||
|
+ (escaped ? code : escape(code, true))
|
||||||
|
+ '</code></pre>';
|
||||||
|
}
|
||||||
|
|
||||||
export function getMathExpressions() {
|
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
|
||||||
return math_expressions
|
return `<p class='editormd-tex'>${renderToString(unescape(code))}</p>`
|
||||||
}
|
} else {
|
||||||
|
return `<pre class="prettyprint linenums"><code class="language-${infostring}">${escaped ? code : escape(code, true)}</code></pre>\n`
|
||||||
export function resetMathExpressions() {
|
}
|
||||||
katex_count = 0
|
|
||||||
math_expressions = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function replace_math_with_ids(text) {
|
|
||||||
let rs = text.replace(latexRegex, (_match, expression) => {
|
|
||||||
const id = next_id()
|
|
||||||
math_expressions[id] = { type: 'inline', expression }
|
|
||||||
return id
|
|
||||||
})
|
|
||||||
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
|
|
||||||
const original_listitem = renderer.listitem
|
|
||||||
renderer.listitem = function (text, task, checked) {
|
|
||||||
return original_listitem(replace_math_with_ids(text), task, checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
const original_paragraph = renderer.paragraph
|
|
||||||
renderer.paragraph = function (text) {
|
|
||||||
return original_paragraph(replace_math_with_ids(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
const original_tablecell = renderer.tablecell
|
|
||||||
renderer.tablecell = function (content, flags) {
|
|
||||||
return original_tablecell(replace_math_with_ids(content), flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.code = function (code, infostring, escaped) {
|
|
||||||
const lang = (infostring || '').match(/\S*/)[0];
|
|
||||||
if (!lang) {
|
|
||||||
return '<pre class="prettyprint linenums"><code>'
|
|
||||||
+ (escaped ? code : escape(code, true))
|
|
||||||
+ '</code></pre>';
|
|
||||||
}
|
}
|
||||||
|
, paragraph(text) {
|
||||||
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
|
let match = text.match(latexRegex)
|
||||||
return `<p class='editormd-tex'>${code}</p>`
|
if (match) {
|
||||||
} else {
|
text = text.replace(latexRegex, (_, $1) => {
|
||||||
return `<pre class="prettyprint linenums"><code class="language-${infostring}">${escaped ? code : escape(code, true)}</code></pre>\n`
|
return renderToString(unescape($1))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return '<p>' + text + '</p>\n';
|
||||||
|
},
|
||||||
|
heading(text, level, raw, slugger) {
|
||||||
|
let anchor = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w\\u4e00-\\u9fa5]]+/g, '-');
|
||||||
|
toc.push({
|
||||||
|
anchor: anchor,
|
||||||
|
level: level,
|
||||||
|
text: text
|
||||||
|
})
|
||||||
|
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.heading = function (text, level, raw) {
|
|
||||||
let anchor = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w\\u4e00-\\u9fa5]]+/g, '-');
|
|
||||||
toc.push({
|
|
||||||
anchor: anchor,
|
|
||||||
level: level,
|
|
||||||
text: text
|
|
||||||
})
|
|
||||||
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
|
|
||||||
}
|
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
silent: true,
|
silent: true,
|
||||||
smartypants: true,
|
smartypants: true,
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,30 @@
|
||||||
import React, { useEffect, useRef, useMemo } from 'react'
|
import React, { useEffect, useRef } from "react";
|
||||||
import 'katex/dist/katex.min.css'
|
import "katex/dist/katex.min.css";
|
||||||
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
|
|
||||||
import 'code-prettify'
|
|
||||||
import dompurify from 'dompurify';
|
|
||||||
|
|
||||||
import { renderToString } from 'katex'
|
import marked, { getTocContent, cleanToc } from "../common/marked";
|
||||||
|
|
||||||
const preRegex = /<pre[^>]*>/g
|
let preRegex = /<pre[^>]*>/g;
|
||||||
function _unescape(str) {
|
|
||||||
let div = document.createElement('div')
|
|
||||||
div.innerHTML = str
|
|
||||||
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export default ({ value = "", is_md = true, className, style = {} }) => {
|
||||||
|
|
||||||
export default ({
|
|
||||||
value = '',
|
|
||||||
className,
|
|
||||||
style = {},
|
|
||||||
url
|
|
||||||
}) => {
|
|
||||||
let str = String(value);
|
let str = String(value);
|
||||||
const html = useMemo(() => {
|
let html = is_md ? marked(str) : value;
|
||||||
let rs = marked(str);
|
if (str.match(/\[TOC\]/)) {
|
||||||
const math_expressions = getMathExpressions();
|
html = html.replace("<p>[TOC]</p>", getTocContent());
|
||||||
if (str.match(/\[TOC\]/)) {
|
cleanToc();
|
||||||
rs = rs.replace("<p>[TOC]</p>", getTocContent())
|
}
|
||||||
cleanToc()
|
|
||||||
}
|
|
||||||
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
|
|
||||||
const { type, expression } = math_expressions[capture]
|
|
||||||
return renderToString(_unescape(expression) || '', { displayMode: type === 'block', throwOnError: false, output: 'html' })
|
|
||||||
})
|
|
||||||
rs = rs.replace(/▁/g, "▁▁▁")
|
|
||||||
resetMathExpressions()
|
|
||||||
return dompurify.sanitize(rs)
|
|
||||||
}, [str]);
|
|
||||||
|
|
||||||
// 锚点跳转,链接地址里含#对应的id
|
|
||||||
useEffect(()=>{
|
|
||||||
if(url && url.hash && html){
|
|
||||||
let u = url.hash;
|
|
||||||
if(u){
|
|
||||||
let id = decodeURIComponent(u.split("#")[1]);
|
|
||||||
let ele = document.getElementById(id);
|
|
||||||
if(ele){
|
|
||||||
window.scrollTo(0, ele.offsetTop + 220);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},[url])
|
|
||||||
|
|
||||||
|
html = html.replace(/▁/g, "▁▁▁");
|
||||||
const el = useRef();
|
const el = useRef();
|
||||||
|
|
||||||
function onAncherHandler(e) {
|
function onAncherHandler(e) {
|
||||||
let target = e.target
|
let target = e.target;
|
||||||
if (target.tagName.toUpperCase() === 'A') {
|
if (target.tagName.toUpperCase() === "A") {
|
||||||
let ancher = target.getAttribute('href')
|
let ancher = target.getAttribute("href");
|
||||||
if (ancher.startsWith('#')) {
|
if (ancher.startsWith("#")) {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
let viewEl = document.getElementById(ancher.replace('#', ''))
|
let viewEl = document.getElementById(ancher.replace("#", ""));
|
||||||
if (viewEl) {
|
if (viewEl) {
|
||||||
viewEl.scrollIntoView(true)
|
viewEl.parentNode.scrollTop = viewEl.offsetTop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -70,24 +33,23 @@ export default ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (el.current && html) {
|
if (el.current && html) {
|
||||||
if (html.match(preRegex)) {
|
if (html.match(preRegex)) {
|
||||||
window.PR.prettyPrint()
|
window.PR.prettyPrint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (el.current) {
|
if (el.current) {
|
||||||
el.current.addEventListener('click', onAncherHandler)
|
el.current.addEventListener("click", onAncherHandler);
|
||||||
return () => {
|
return () => {
|
||||||
el.current.removeEventListener('click', onAncherHandler)
|
el.current.removeEventListener("click", onAncherHandler);
|
||||||
resetMathExpressions()
|
};
|
||||||
cleanToc()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [html, el.current, onAncherHandler])
|
}, [html, el.current, onAncherHandler]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={el}
|
ref={el}
|
||||||
style={style}
|
style={style}
|
||||||
className={`${className ? className : ''} markdown-body`}
|
className={`${className ? className : ""} markdown-body`}
|
||||||
dangerouslySetInnerHTML={{ __html: html }}
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
></div>
|
></div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
import React , { useEffect , useState } from 'react';
|
|
||||||
import './index.scss';
|
|
||||||
import { Button } from 'antd';
|
|
||||||
import Upload from "../Upload/Index";
|
|
||||||
import UploadImg from "../Images/upload.png";
|
|
||||||
import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
|
|
||||||
import { AlignCenterBetween } from '../Component/layout';
|
|
||||||
import axios from 'axios';
|
|
||||||
import RenderHtml from '../../components/render-html';
|
|
||||||
import Attachments from "../Upload/attachment";
|
|
||||||
|
|
||||||
function Index(props){
|
|
||||||
const [ attachments , setAttachments ] = useState(undefined);
|
|
||||||
const [ content , setContent ] = useState(undefined);
|
|
||||||
const [ edit , setEdit ] = useState(false);
|
|
||||||
const [ fileList , setFileList ] = useState(undefined);
|
|
||||||
const [ editOpration , setEditOpration ] = useState(false);
|
|
||||||
const { owner , projectsId } = props.match.params;
|
|
||||||
|
|
||||||
const { isManager , isDeveloper , current_user } = props;
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(owner && projectsId){
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
},[owner,projectsId])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if( (current_user && current_user.login) && (isManager === true || isDeveloper === true)){
|
|
||||||
setEditOpration(true);
|
|
||||||
}
|
|
||||||
},[isManager , isDeveloper])
|
|
||||||
function Init(){
|
|
||||||
const url = `/${owner}/${projectsId}/about.json`;
|
|
||||||
axios.get(url).then(result=>{
|
|
||||||
if(result){
|
|
||||||
setContent(result.data.content);
|
|
||||||
setAttachments(result.data.attachments);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
function editContent(){
|
|
||||||
setEdit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function onContentChange(value){
|
|
||||||
setContent(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
function UploadFunc(fileList){
|
|
||||||
setFileList(fileList);
|
|
||||||
};
|
|
||||||
|
|
||||||
function sureSubmit(){
|
|
||||||
const url = `/${owner}/${projectsId}/about.json`;
|
|
||||||
axios.post(url,{
|
|
||||||
content,attachment_ids:fileList
|
|
||||||
}).then(result=>{
|
|
||||||
if(result){
|
|
||||||
props.showNotification("项目主页编辑成功!");
|
|
||||||
Init();
|
|
||||||
setEdit(false);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
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>
|
|
||||||
{
|
|
||||||
edit ?
|
|
||||||
<div className="padding20">
|
|
||||||
<MDEditor
|
|
||||||
placeholder={"请输入描述信息"}
|
|
||||||
height={500}
|
|
||||||
mdID={"order-new-description"}
|
|
||||||
initValue={content}
|
|
||||||
onChange={onContentChange}
|
|
||||||
className="mt20"
|
|
||||||
></MDEditor>
|
|
||||||
<div className="pb20">
|
|
||||||
<Upload
|
|
||||||
className="commentStyle mt20"
|
|
||||||
isComplete={true}
|
|
||||||
load={UploadFunc}
|
|
||||||
icon={
|
|
||||||
<img
|
|
||||||
src={UploadImg}
|
|
||||||
width="58"
|
|
||||||
alt=""
|
|
||||||
style={{ marginBottom: 15 }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
size={100}
|
|
||||||
showNotification={props.showNotification}
|
|
||||||
/>
|
|
||||||
{attachments && attachments.length > 0 &&
|
|
||||||
<Attachments
|
|
||||||
attachments={attachments}
|
|
||||||
showNotification={props.showNotification}
|
|
||||||
canDelete={true}
|
|
||||||
></Attachments>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className="pb30">
|
|
||||||
<Button type="primary" onClick={sureSubmit}>确定</Button>
|
|
||||||
<Button className="ml30" onClick={()=>setEdit(false)}>取消</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
<div className="padding20">
|
|
||||||
{content ?
|
|
||||||
<RenderHtml className="break_word_comments imageLayerParent" value={content} url={props.history.location}/>
|
|
||||||
:
|
|
||||||
<div>暂无概览~</div>
|
|
||||||
}
|
|
||||||
{attachments && attachments.length > 0 &&
|
|
||||||
<Attachments
|
|
||||||
attachments={attachments}
|
|
||||||
showNotification={props.showNotification}
|
|
||||||
></Attachments>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Index;
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
.aboutPanels{
|
|
||||||
max-width: 1200px;
|
|
||||||
margin:0px auto;
|
|
||||||
.aboutContent{
|
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid #EEEEEE;
|
|
||||||
width:100%;
|
|
||||||
background-color: #fff;
|
|
||||||
margin-top:20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -27,6 +27,7 @@ const ARRAY = [
|
||||||
name:'1个月'
|
name:'1个月'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
// const dataformat="YYYY-MM-DD HH:mm";
|
||||||
|
|
||||||
class Activity extends Component{
|
class Activity extends Component{
|
||||||
constructor(props){
|
constructor(props){
|
||||||
|
|
@ -52,8 +53,8 @@ class Activity extends Component{
|
||||||
}
|
}
|
||||||
|
|
||||||
getInfo =(time,type,status,page)=>{
|
getInfo =(time,type,status,page)=>{
|
||||||
const { projectsId , owner } = this.props.match.params;
|
const { projectsId } = this.props.match.params;
|
||||||
const url = `/${owner}/${projectsId}/activity.json`;
|
const url = `/projects/${projectsId}/project_trends.json`;
|
||||||
axios.get(url,{
|
axios.get(url,{
|
||||||
params:{
|
params:{
|
||||||
time,type,status,page
|
time,type,status,page
|
||||||
|
|
@ -96,22 +97,27 @@ class Activity extends Component{
|
||||||
const { time,type,status } = this.state;
|
const { time,type,status } = this.state;
|
||||||
this.getInfo(time,type,status,page);
|
this.getInfo(time,type,status,page);
|
||||||
}
|
}
|
||||||
menu =()=> (
|
|
||||||
<Menu>
|
|
||||||
{
|
|
||||||
ARRAY && ARRAY.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<Menu.Item key={item.id} onClick={this.changeTime}>{item.name}</Menu.Item>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</Menu>
|
|
||||||
)
|
|
||||||
render(){
|
render(){
|
||||||
const { time , data , page , project_trends , isSpin } = this.state;
|
const { time , data , page , project_trends , isSpin } = this.state;
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu>
|
||||||
|
{
|
||||||
|
ARRAY && ARRAY.map((item,key)=>{
|
||||||
|
return(
|
||||||
|
<Menu.Item key={item.id} onClick={this.changeTime}>{item.name}</Menu.Item>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
let name = time && ARRAY.filter(item=>item.id === parseInt(time)) ;
|
let name = time && ARRAY.filter(item=>item.id === parseInt(time)) ;
|
||||||
|
// console.log(name);
|
||||||
|
|
||||||
|
|
||||||
const second_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%';
|
const second_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%';
|
||||||
|
|
||||||
const third_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%';
|
const third_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%';
|
||||||
const fourth_per = (parseInt(data && data.open_issues_count)/parseInt(data && data.issues_count)*100)+'%';
|
const fourth_per = (parseInt(data && data.open_issues_count)/parseInt(data && data.issues_count)*100)+'%';
|
||||||
return(
|
return(
|
||||||
|
|
@ -156,7 +162,7 @@ class Activity extends Component{
|
||||||
</div>
|
</div>
|
||||||
<div className="df trendsTop mt20">
|
<div className="df trendsTop mt20">
|
||||||
<div className="branchDropdown f-wrap-alignCenter">
|
<div className="branchDropdown f-wrap-alignCenter">
|
||||||
<Dropdown overlay={this.menu()} trigger={['click']} placement="bottomLeft">
|
<Dropdown overlay={menu} trigger={['click']} placement="bottomLeft">
|
||||||
<a className="ant-dropdown-link">
|
<a className="ant-dropdown-link">
|
||||||
<span className="color-grey-9 mr3">周期:</span>
|
<span className="color-grey-9 mr3">周期:</span>
|
||||||
{name && name.length>0 && name[0].name} <Icon type="down" />
|
{name && name.length>0 && name[0].name} <Icon type="down" />
|
||||||
|
|
@ -177,7 +183,7 @@ class Activity extends Component{
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<NoneData _html="暂时还没有相关数据!" />
|
<NoneData _html="暂时还没有相关数据哦!" />
|
||||||
}
|
}
|
||||||
</Spin>
|
</Spin>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,46 @@
|
||||||
import React, { Component } from 'react';
|
import React , { Component } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
import './activity.css';
|
import './activity.css';
|
||||||
import { getImageUrl } from 'educoder';
|
import { getImageUrl } from 'educoder';
|
||||||
|
|
||||||
|
|
||||||
class ActivityItem extends Component {
|
class ActivityItem extends Component{
|
||||||
render() {
|
render(){
|
||||||
const { projectsId ,owner } = this.props.match.params;
|
const { projectsId } = this.props.match.params;
|
||||||
const { item } = this.props;
|
const { item } = this.props;
|
||||||
return (
|
return(
|
||||||
<div className="activity_item">
|
<div className="activity_item">
|
||||||
<div className="flex1">
|
<div className="flex1">
|
||||||
{/* 如果是版本发布 */}
|
{/* 如果是版本发布 */}
|
||||||
{item.trend_type === "VersionRelease" ?
|
{ item.trend_type==="VersionRelease"?
|
||||||
<p className="itemLine">
|
<p className="itemLine">
|
||||||
<Link to={`/projects/${owner}/${projectsId}/version`} className="color-blue font-16">{item.name}</Link>
|
<Link to={`/projects/${projectsId}/version`} className="color-blue font-16">{item.name}</Link>
|
||||||
<span className="activity_type">{item.trend_type}</span>
|
<span className="activity_type">{item.trend_type}</span>
|
||||||
</p >
|
</p >
|
||||||
:
|
:
|
||||||
// 如果是任务
|
// 如果是任务
|
||||||
item.trend_type === "Issue" ?
|
item.trend_type==="Issue"?
|
||||||
<p className="itemLine">
|
<p className="itemLine">
|
||||||
<Link to={`/projects/${owner}/${projectsId}/issues/${item.trend_id}/detail`} className="color-blue font-16">{item.name}</Link>
|
<Link to={`/projects/${projectsId}/orders/${item.trend_id}/detail`} className="color-blue font-16">{item.name}</Link>
|
||||||
<span className="activity_type">{item.trend_type}</span>
|
<span className="activity_type">{item.trend_type}</span>
|
||||||
</p >
|
|
||||||
:
|
|
||||||
// 如果是合并请求
|
|
||||||
<p className="itemLine">
|
|
||||||
<Link to={`/projects/${owner}/${projectsId}/pulls/${item.trend_id}/Messagecount`} className="color-blue font-16">{item.name}</Link>
|
|
||||||
<span className="activity_type">{item.trend_type}</span>
|
|
||||||
</p >
|
|
||||||
}
|
|
||||||
<p className="itemLine mt10">
|
|
||||||
<Link to={`/users/${item && item.user_login}`} className="show-user-link">
|
|
||||||
<img alt="" src={getImageUrl(`/${item.user_avatar}`)} className="createImage" />
|
|
||||||
<span className="mr20">{item.user_name}</span>
|
|
||||||
</Link>
|
|
||||||
{item.created_at && <span className="color-grey-9">创建于<span className="ml2 color-grey-6">{item.created_at}</span></span>}
|
|
||||||
</p >
|
</p >
|
||||||
</div>
|
:
|
||||||
</div>
|
// 如果是合并请求
|
||||||
|
<p className="itemLine">
|
||||||
|
<Link to={`/projects/${projectsId}/merge/${item.trend_id}/Messagecount`} className="color-blue font-16">{item.name}</Link>
|
||||||
|
<span className="activity_type">{item.trend_type}</span>
|
||||||
|
</p >
|
||||||
|
}
|
||||||
|
<p className="itemLine mt15">
|
||||||
|
<Link to={`/users/${item && item.login}`} className="show-user-link">
|
||||||
|
<img alt="" src={getImageUrl(`images/${item.user_avatar}`)} className="createImage"/>
|
||||||
|
<span className="mr20">{item.user_name}</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{ item.created_at && <span className="color-grey-9">创建于<span className="ml2 color-grey-6">{item.created_at}</span></span>}
|
||||||
|
</p >
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
.activity_list .activity_item:last-child{
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
.prPercent > p{
|
.prPercent > p{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -101,7 +98,6 @@
|
||||||
height: 22px;
|
height: 22px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 22px;
|
|
||||||
}
|
}
|
||||||
.change{
|
.change{
|
||||||
color: black;
|
color: black;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ class CloneAddress extends Component {
|
||||||
const { http_url, downloadUrl } = this.props;
|
const { http_url, downloadUrl } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="gitAddressClone">
|
<div className="gitAddressClone">
|
||||||
{/* <p className="addressTips"><span>版本库地址已变更,请基于新地址提交代码</span></p> */}
|
|
||||||
{
|
{
|
||||||
http_url && <span>HTTP</span>
|
http_url && <span>HTTP</span>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import './branch.css';
|
||||||
import { getBranch , getTag } from '../GetData/getData';
|
import { getBranch , getTag } from '../GetData/getData';
|
||||||
|
|
||||||
|
|
||||||
export default (({ projectsId , branch , owner , changeBranch , branchList , tagflag = true })=>{
|
export default (({ projectsId , repo_id , changeBranch , branch })=>{
|
||||||
const [ showValue , setShowValue ] = useState(branch);
|
const [ showValue , setShowValue ] = useState(branch);
|
||||||
const [ inputValue , setInputValue] = useState(undefined);
|
const [ inputValue , setInputValue] = useState(undefined);
|
||||||
const [ nav , setNav ] = useState(0);
|
const [ nav , setNav ] = useState(0);
|
||||||
|
|
@ -17,11 +17,10 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
setShowValue(branch);
|
setShowValue(branch);
|
||||||
},[branch])
|
},[branch])
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
document.body.addEventListener('click', e => {
|
document.body.addEventListener('click', e => {
|
||||||
let name = e.target.className;
|
let name = e.target.className;
|
||||||
let turn = name === "ant-input OptionsInput" || name === "navli active"|| name === "navli" || name === "padding10 bor-bottom-greyE";
|
let turn = name == "ant-input OptionsInput" || name == "navli active"|| name == "navli" || name == "padding10 bor-bottom-greyE";
|
||||||
if(turn){
|
if(turn){
|
||||||
return;
|
return;
|
||||||
}else{
|
}else{
|
||||||
|
|
@ -31,22 +30,18 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
if(branchList){
|
getBranchs(projectsId);
|
||||||
setData(branchList);
|
},[projectsId])
|
||||||
setDatas(branchList);
|
|
||||||
setIsSpin(false);
|
|
||||||
}
|
|
||||||
},[branchList])
|
|
||||||
|
|
||||||
|
|
||||||
async function getBranchs(id,owner){
|
async function getBranchs(id){
|
||||||
let result = await getBranch(id,owner);
|
let result = await getBranch(id);
|
||||||
setData(result);
|
setData(result);
|
||||||
setDatas(result);
|
setDatas(result);
|
||||||
setIsSpin(false);
|
setIsSpin(false);
|
||||||
}
|
}
|
||||||
async function getTags(id,owner){
|
async function getTags(id){
|
||||||
let result = await getTag(id,owner);
|
let result = await getTag(id);
|
||||||
setData(result);
|
setData(result);
|
||||||
setDatas(result);
|
setDatas(result);
|
||||||
setIsSpin(false);
|
setIsSpin(false);
|
||||||
|
|
@ -62,13 +57,13 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
|
||||||
setNav(nav);
|
setNav(nav);
|
||||||
setIsSpin(true);
|
setIsSpin(true);
|
||||||
if(nav === 0){
|
if(nav === 0){
|
||||||
getBranchs(projectsId,owner);
|
getBranchs(projectsId);
|
||||||
}else{
|
}else{
|
||||||
getTags(projectsId,owner);
|
getTags(repo_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function chooseitem(value){
|
function chooseitem(value){
|
||||||
// setShowValue(value);
|
setShowValue(value);
|
||||||
changeBranch(value);
|
changeBranch(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,8 +77,8 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
|
||||||
onChange={changeInputValue} style={{width:"220px"}}
|
onChange={changeInputValue} style={{width:"220px"}}
|
||||||
/>
|
/>
|
||||||
<ul className="navUl">
|
<ul className="navUl">
|
||||||
<li className={nav === 0?"navli active":"navli"} onClick={()=>changeNav(0)}><i className="iconfont icon-fenzhi1 font-14 mr3"></i>分支列表</li>
|
<li className={nav==0?"navli active":"navli"} onClick={()=>changeNav(0)}><i className="iconfont icon-fenzhi1 font-14 mr3"></i>分支列表</li>
|
||||||
{ tagflag && <li className={nav === 1?"navli active":"navli"} onClick={()=>changeNav(1)}><i className="iconfont icon-biaoqian3 font-14 mr3"></i>标签列表</li> }
|
<li className={nav==1?"navli active":"navli"} onClick={()=>changeNav(1)}><i className="iconfont icon-biaoqian3 font-14 mr3"></i>标签列表</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<Spin spinning={isSpin}>
|
<Spin spinning={isSpin}>
|
||||||
|
|
@ -102,10 +97,9 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return(
|
return(
|
||||||
<Popover placement='bottomLeft' visible={flag} content={menu} onClick={()=>setFlag(!flag)} overlayClassName="branch-tagBox-list">
|
<Popover placement="bottom" visible={flag} content={menu} onClick={()=>setFlag(!flag)} overlayClassName="branch-tagBox-list">
|
||||||
<div className="branch-tagBox">
|
<div className="branch-tagBox">
|
||||||
{/* {nav === 0 ?"分支":"标签"} */}
|
<span className="color-grey-9 mr3 ml8">{nav === 0 ?"分支":"标签"}:</span>
|
||||||
<span className="color-grey-9 mr3 ml8"><i className="iconfont icon-fenzhi2 font-18"></i></span>
|
|
||||||
<a className="ant-dropdown-link">
|
<a className="ant-dropdown-link">
|
||||||
{showValue}
|
{showValue}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
min-width: 140px;
|
width: 240px;
|
||||||
}
|
}
|
||||||
.branch-tagBox-list .ant-popover-arrow{
|
.branch-tagBox-list .ant-popover-arrow{
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { AutoComplete , Button , Icon } from 'antd';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const { Option } = AutoComplete;
|
|
||||||
function AddGroup({organizeId,getGroupID}){
|
|
||||||
const [ id , setID ] = useState(undefined);
|
|
||||||
const [ source , setSource ] = useState(undefined);
|
|
||||||
const [ searchKey , setSearchKey ] = useState("");
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
getUserList();
|
|
||||||
},[searchKey])
|
|
||||||
|
|
||||||
function getUserList(e){
|
|
||||||
const url = `/organizations/${organizeId}/teams/search.json`;
|
|
||||||
axios.get(url, {
|
|
||||||
params: {
|
|
||||||
search: searchKey,
|
|
||||||
},
|
|
||||||
}).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
sourceOptions(result.data.teams);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function sourceOptions(userDataSource){
|
|
||||||
const s = userDataSource && userDataSource.map((item, key) => {
|
|
||||||
return (
|
|
||||||
<Option
|
|
||||||
key={key}
|
|
||||||
value={`${item.id}`}
|
|
||||||
name={item.name}
|
|
||||||
>
|
|
||||||
{item.name}
|
|
||||||
</Option>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
setSource(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeInputUser(e){
|
|
||||||
setSearchKey(e || "");
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选择用户
|
|
||||||
function selectInputUser(e, option){
|
|
||||||
setID(e);
|
|
||||||
setSearchKey(option.props.name);
|
|
||||||
};
|
|
||||||
|
|
||||||
function addCollaborator(){
|
|
||||||
getGroupID && getGroupID(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div className="addPanel">
|
|
||||||
<AutoComplete
|
|
||||||
dataSource={source}
|
|
||||||
value={searchKey}
|
|
||||||
style={{ width: 300 }}
|
|
||||||
onChange={changeInputUser}
|
|
||||||
onSelect={selectInputUser}
|
|
||||||
placeholder="搜索需要添加的团队..."
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
ghost
|
|
||||||
onClick={addCollaborator}
|
|
||||||
className="ml15"
|
|
||||||
>
|
|
||||||
<Icon type="plus" size="16"></Icon>
|
|
||||||
添加团队
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default AddGroup;
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { AutoComplete , Button , Icon } from 'antd';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { getImageUrl } from 'educoder';
|
|
||||||
|
|
||||||
const { Option } = AutoComplete;
|
|
||||||
function AddMember({getID,login}){
|
|
||||||
const [ id , setID ] = useState(undefined);
|
|
||||||
const [ source , setSource ] = useState(undefined);
|
|
||||||
const [ searchKey , setSearchKey ] = useState(undefined);
|
|
||||||
|
|
||||||
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 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={{ "vertical-align": "middle" }}>
|
|
||||||
{item.username}
|
|
||||||
<span className="color-grey ml10">({item.login})</span>
|
|
||||||
</span>
|
|
||||||
</Option>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
setSource(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeInputUser(e){
|
|
||||||
setSearchKey(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选择用户
|
|
||||||
function selectInputUser(e, option){
|
|
||||||
setID(login ? e : option.props.login);
|
|
||||||
setSearchKey(option.props.name);
|
|
||||||
};
|
|
||||||
|
|
||||||
function addCollaborator(){
|
|
||||||
getID && getID(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div className="addPanel">
|
|
||||||
<AutoComplete
|
|
||||||
dataSource={source}
|
|
||||||
value={searchKey}
|
|
||||||
style={{ width: 300 }}
|
|
||||||
onChange={changeInputUser}
|
|
||||||
onSelect={selectInputUser}
|
|
||||||
placeholder="搜索需要添加的用户..."
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
ghost
|
|
||||||
onClick={addCollaborator}
|
|
||||||
className="ml15"
|
|
||||||
>
|
|
||||||
<Icon type="plus" size="16"></Icon>
|
|
||||||
添加成员
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default AddMember;
|
|
||||||
|
|
@ -1,23 +1,19 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { getImageUrl } from 'educoder';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import './Component.scss';
|
import './Component.scss';
|
||||||
|
|
||||||
function Cards({img , title, desc , rightBtn , src , bottomInfos}){
|
export default (({img , title, desc , rightBtn})=>{
|
||||||
return(
|
return(
|
||||||
<div className="cards">
|
<div className="cards">
|
||||||
{img &&<div className="img"><img src={getImageUrl(`/${img}`)} alt=""/></div>}
|
<div className="img"><img src={img} alt=""/></div>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<p className="titles">
|
<p className="titles">
|
||||||
<Link to={src}>{title}</Link>
|
<span>{title}</span>
|
||||||
{rightBtn}
|
{rightBtn}
|
||||||
</p>
|
</p>
|
||||||
<div className="desc">
|
<div className="desc">
|
||||||
{desc}
|
{desc}
|
||||||
</div>
|
</div>
|
||||||
{bottomInfos}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
export default Cards;
|
|
||||||
|
|
@ -8,10 +8,6 @@ li.ant-menu-item{
|
||||||
margin:0px!important;
|
margin:0px!important;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
.flags{
|
|
||||||
border: 1px solid red;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
// Cards
|
// Cards
|
||||||
.cards{
|
.cards{
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -19,8 +15,6 @@ li.ant-menu-item{
|
||||||
padding:20px 34px;
|
padding:20px 34px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
margin-bottom:18px;
|
margin-bottom:18px;
|
||||||
min-height: 130px;
|
|
||||||
border:1px solid #eee;
|
|
||||||
.img{
|
.img{
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
width: 190px;
|
width: 190px;
|
||||||
|
|
@ -29,10 +23,8 @@ li.ant-menu-item{
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
|
||||||
img{
|
img{
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.content{
|
.content{
|
||||||
|
|
@ -43,9 +35,7 @@ li.ant-menu-item{
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 10px!important;
|
margin-bottom: 10px!important;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 22px;
|
&>span{
|
||||||
line-height: 22px;;
|
|
||||||
&>a{
|
|
||||||
font-size:18px ;
|
font-size:18px ;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
@ -56,13 +46,11 @@ li.ant-menu-item{
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
line-height: 20px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Tabs
|
// Tabs
|
||||||
.tabsStyle{
|
.tabsStyle{
|
||||||
border:1px solid #eee;
|
|
||||||
.ant-tabs-bar.ant-tabs-top-bar{
|
.ant-tabs-bar.ant-tabs-top-bar{
|
||||||
padding-left: 35px;
|
padding-left: 35px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
|
|
@ -75,121 +63,4 @@ li.ant-menu-item{
|
||||||
width:25px!important;
|
width:25px!important;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.statusColor{
|
|
||||||
display: inline-block;
|
|
||||||
padding:0px 10px;
|
|
||||||
height:20px;
|
|
||||||
line-height: 20px;
|
|
||||||
border-radius:11px;
|
|
||||||
color: #fff;
|
|
||||||
margin-left: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
&.running{
|
|
||||||
background:#5091FF;
|
|
||||||
color: #F1F8FF;
|
|
||||||
}
|
|
||||||
&.Preparing{
|
|
||||||
background:rgba(255,110,33,1);
|
|
||||||
color:rgba(255,248,244,1);
|
|
||||||
}
|
|
||||||
&.pass{
|
|
||||||
background:#28BD6C;
|
|
||||||
color: #EEFDF5;
|
|
||||||
}
|
|
||||||
&.failed{
|
|
||||||
background:#F73030;
|
|
||||||
color:#FCEEEE ;
|
|
||||||
}
|
|
||||||
&.killed{
|
|
||||||
background:#eee;
|
|
||||||
color:#999 ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.handleBox{
|
|
||||||
position: fixed;
|
|
||||||
top:45%;
|
|
||||||
right:240px;
|
|
||||||
z-index: 10000;
|
|
||||||
}
|
|
||||||
.laterest{
|
|
||||||
color: #05690d;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 1800px){
|
|
||||||
.handleBox{
|
|
||||||
right:190px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 1700px){
|
|
||||||
.handleBox{
|
|
||||||
right:140px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 1600px){
|
|
||||||
.handleBox{
|
|
||||||
right:90px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 1450px){
|
|
||||||
.handleBox{
|
|
||||||
right:10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 1380px){
|
|
||||||
.handleBox{
|
|
||||||
right:0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ant-drawer{
|
|
||||||
z-index: 10000!important;
|
|
||||||
}
|
|
||||||
.ant-drawer-body{
|
|
||||||
padding:0px!important;
|
|
||||||
.drawerHead{
|
|
||||||
background-color: #333;
|
|
||||||
color: #fff;
|
|
||||||
padding:15px 20px;
|
|
||||||
}
|
|
||||||
.ant-tree{
|
|
||||||
margin:0px 20px!important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuPanels{
|
|
||||||
width: 240px;
|
|
||||||
height: 180px;
|
|
||||||
.ant-popover-content,.ant-popover-inner{
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.halfs{
|
|
||||||
margin-top: 24px;
|
|
||||||
padding:24px 0px 0px 0px;
|
|
||||||
border-top: 1px solid #e8e8e8;
|
|
||||||
.attrPerson{
|
|
||||||
padding-bottom: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.menuinfos{
|
|
||||||
padding:15px 0px;
|
|
||||||
&>a{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
border-right: 1px solid #eee;
|
|
||||||
flex: 1;
|
|
||||||
& >span:first-child{
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
& >span:last-child{
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
&:last-child{
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { AlignCenter , FlexAJ } from '../Component/layout';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { Popover , Spin } from 'antd';
|
|
||||||
import { getImageUrl } from 'educoder';
|
|
||||||
import './Component.scss';
|
|
||||||
import { getUser } from '../GetData/getData';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
function Contributors({contributors,owner,projectsId}){
|
|
||||||
const [ menuList ,setMenuList ]= useState([]);
|
|
||||||
const [ list , setList ]= useState(undefined);
|
|
||||||
const [ total , setTotal ]= useState(0);
|
|
||||||
const [ menu , setMenu ] = useState("");
|
|
||||||
const [ login , setLogin ] = useState(undefined);
|
|
||||||
const [ isSpin , setIsSpin ] = useState(false);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(contributors && contributors.total_count>0){
|
|
||||||
setTotal(contributors.total_count);
|
|
||||||
setList(contributors.list);
|
|
||||||
}
|
|
||||||
},[contributors])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(login){
|
|
||||||
getUsers(login);
|
|
||||||
}else{
|
|
||||||
setMenu(undefined);
|
|
||||||
}
|
|
||||||
},[login])
|
|
||||||
|
|
||||||
async function getUsers(login){
|
|
||||||
setIsSpin(true);
|
|
||||||
let a = menuList && menuList.filter(i=>i.login === login);
|
|
||||||
if(a.length === 0){
|
|
||||||
let result = await getUser(login);
|
|
||||||
let arr = menuList;
|
|
||||||
arr.push({...result});
|
|
||||||
setMenuList(arr);
|
|
||||||
setMenusFunc(result);
|
|
||||||
setIsSpin(false);
|
|
||||||
}else{
|
|
||||||
setMenusFunc(a[0]);
|
|
||||||
setIsSpin(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMenusFunc(data){
|
|
||||||
if(data){
|
|
||||||
let ele = (
|
|
||||||
<Spin spinning={isSpin}>
|
|
||||||
<FlexAJ>
|
|
||||||
<AlignCenter>
|
|
||||||
<Link to={`/users/${data.login}`}><img src={getImageUrl(`/${data.image_url}`)} alt="" className="radius" width="38px" height="38px"/></Link>
|
|
||||||
<Link to={`/users/${data.login}`} className="ml10">{data.name}</Link>
|
|
||||||
</AlignCenter>
|
|
||||||
{
|
|
||||||
data.is_watch ? <a className="color-grey-9" onClick={()=>FocusFunc(false,data.login)}>取消关注</a>:<a className="color-blue" onClick={()=>FocusFunc(true,data.login)}>关注</a>
|
|
||||||
}
|
|
||||||
</FlexAJ>
|
|
||||||
<AlignCenter className="menuinfos">
|
|
||||||
<a href={data.projects_url}>
|
|
||||||
<span>{data.projects_count}</span>
|
|
||||||
<span>项目数</span>
|
|
||||||
</a>
|
|
||||||
<a href={data.followers_url}>
|
|
||||||
<span>{data.followers_count}</span>
|
|
||||||
<span>粉丝数</span>
|
|
||||||
</a>
|
|
||||||
<a href={data.following_url}>
|
|
||||||
<span>{data.following_count}</span>
|
|
||||||
<span>关注数</span>
|
|
||||||
</a>
|
|
||||||
</AlignCenter>
|
|
||||||
{
|
|
||||||
data.organizations && data.organizations.length > 0 ?
|
|
||||||
<AlignCenter className="font-12 pt4 pb4">
|
|
||||||
<span>所属组织:</span>
|
|
||||||
<div className="task-hide flex1">
|
|
||||||
{renderArray(data.organizations)}
|
|
||||||
</div>
|
|
||||||
</AlignCenter>
|
|
||||||
:""
|
|
||||||
}
|
|
||||||
{
|
|
||||||
data.location && <AlignCenter className="font-12 pt4 pb4"><span>所在地址:</span><span className="ml5">{data.location}</span></AlignCenter>
|
|
||||||
}
|
|
||||||
</Spin>
|
|
||||||
)
|
|
||||||
setMenu(ele);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function FocusFunc(flag,login){
|
|
||||||
axios({
|
|
||||||
method: flag ? 'post' : 'delete',
|
|
||||||
url: `/watchers/${flag ? 'follow' : 'unfollow'}.json`,
|
|
||||||
params: {target_type: "user",id:login}
|
|
||||||
}).then(result => {
|
|
||||||
if (result && (result.data.status === 0 || result.data.status === 2)) {
|
|
||||||
let a = menuList && menuList.filter(i=>i.login === login);
|
|
||||||
if(a){
|
|
||||||
a[0].is_watch = flag;
|
|
||||||
}
|
|
||||||
setMenusFunc(a[0]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderArray(array){
|
|
||||||
let str = "";
|
|
||||||
for(var i = 0;i<array.length;i++){
|
|
||||||
str += array[i].name +"、";
|
|
||||||
}
|
|
||||||
let substr = str.substr(0,str.length-1);
|
|
||||||
return (<span title={substr}>{substr}</span>)
|
|
||||||
}
|
|
||||||
|
|
||||||
function setVisibleFunc(flag,l,index){
|
|
||||||
if(l !== login){
|
|
||||||
setLogin(l);
|
|
||||||
}
|
|
||||||
var lx = list.concat();
|
|
||||||
lx.map(i=>i.visible =false);
|
|
||||||
if(flag){
|
|
||||||
lx[index].visible = flag;
|
|
||||||
}
|
|
||||||
lx.splice();
|
|
||||||
setList(lx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div className="halfs">
|
|
||||||
<FlexAJ>
|
|
||||||
<AlignCenter><span className="font-16 color-grey-6">贡献者</span>{ contributors && contributors.total_count > 0 && <span className="infoCount">{contributors.total_count}</span>}</AlignCenter>
|
|
||||||
<Link className="font-12 color-grey-9" to={`/projects/${owner}/${projectsId}/contribute`}>全部</Link>
|
|
||||||
</FlexAJ>
|
|
||||||
<div className="attrPerson" onMouseLeave={()=>setVisibleFunc(false)}>
|
|
||||||
{
|
|
||||||
total > 0 ?
|
|
||||||
list.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<Popover content={menu} visible={item.visible} overlayClassName="menuPanels" placement="top">
|
|
||||||
<Link key={key} to={`/users/${item.login}`}>
|
|
||||||
<img src={getImageUrl(`/${item.image_url}`)} alt="" onMouseOver={()=>setVisibleFunc(true,item.login,key)}/>
|
|
||||||
</Link>
|
|
||||||
</Popover>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
:""
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Contributors;
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { Drawer , Tree , Spin } from 'antd';
|
|
||||||
import './Component.scss';
|
|
||||||
import axios from 'axios';
|
|
||||||
const { TreeNode , DirectoryTree } = Tree;
|
|
||||||
|
|
||||||
function DrawerPanel({visible,onClose,branch,owner,projectsId,history, name , list}){
|
|
||||||
const [ treeData , setTreeData ] = useState(undefined);
|
|
||||||
const [ isSpin , setIsSpin ] = useState(true);
|
|
||||||
const [first , setFirst ] = useState(true);
|
|
||||||
useEffect(()=>{
|
|
||||||
if(visible && first){
|
|
||||||
if(list){
|
|
||||||
setTreeData(list);
|
|
||||||
setIsSpin(false);
|
|
||||||
}else{
|
|
||||||
getMenulist();
|
|
||||||
}
|
|
||||||
setFirst(false);
|
|
||||||
}
|
|
||||||
},[visible])
|
|
||||||
|
|
||||||
function getMenulist(){
|
|
||||||
const url = `/${owner}/${projectsId}/entries.json`;
|
|
||||||
axios.get(url,{ params: { ref: branch } }).then(result=>{
|
|
||||||
if(result){
|
|
||||||
setTreeData(result.data.entries);
|
|
||||||
}
|
|
||||||
setIsSpin(false);
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderTreeNodes(data) {
|
|
||||||
return data && data.length > 0 && data.map((item) => {
|
|
||||||
return (
|
|
||||||
<TreeNode title={item.name} key={item.key} dataRef={item} isLeaf={item.type === "file"}>
|
|
||||||
{renderTreeNodes(item.children)}
|
|
||||||
</TreeNode>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLoadData(tr){
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
if (tr.props.children) {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let en = [];
|
|
||||||
const url = `/${owner}/${projectsId}/sub_entries.json`;
|
|
||||||
axios.get(url, {
|
|
||||||
params:{
|
|
||||||
filepath:tr.props.dataRef.path,
|
|
||||||
ref:branch,
|
|
||||||
type:"dir"
|
|
||||||
}
|
|
||||||
}).then((result) => {
|
|
||||||
if(result){
|
|
||||||
en = result.data.entries;
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
setTimeout(() => {
|
|
||||||
tr.props.dataRef.children = en;
|
|
||||||
setTreeData([...treeData]);
|
|
||||||
resolve();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectTree(keys,event){
|
|
||||||
let dataref = event.node.props.dataRef;
|
|
||||||
if(dataref.type==="file"){
|
|
||||||
onClose();
|
|
||||||
history.push(`/projects/${owner}/${projectsId}/tree/${branch}/${dataref.path}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<Drawer
|
|
||||||
placement="left"
|
|
||||||
visible={visible}
|
|
||||||
closable={false}
|
|
||||||
onClose={onClose}
|
|
||||||
width={"320px"}
|
|
||||||
maskStyle={{backgroundColor:'rgba(0,0,0,0.09)'}}
|
|
||||||
>
|
|
||||||
<Spin spinning={isSpin}>
|
|
||||||
<div className="drawerHead">
|
|
||||||
<p className="font-20">{name}</p>
|
|
||||||
<p><i class="iconfont icon-fenzhi2 font-18 color-grey-9 mr3"></i>{branch}</p>
|
|
||||||
</div>
|
|
||||||
<DirectoryTree loadData={onLoadData} onSelect={selectTree}>
|
|
||||||
{treeData && renderTreeNodes(treeData)}
|
|
||||||
</DirectoryTree>
|
|
||||||
</Spin>
|
|
||||||
</Drawer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default DrawerPanel;
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
.ant-modal-mask{
|
|
||||||
z-index: 1001;
|
|
||||||
}
|
|
||||||
.ant-modal-wrap{
|
|
||||||
z-index: 1002;
|
|
||||||
.ant-form-explain{
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
import React , {forwardRef, useEffect} from 'react';
|
|
||||||
import { Modal , Form , Input , Button } from 'antd';
|
|
||||||
import './EAccount.scss';
|
|
||||||
|
|
||||||
function EducoderAccount({form , visible , onOk , email}){
|
|
||||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(email){
|
|
||||||
setFieldsValue({email})
|
|
||||||
}
|
|
||||||
},[email])
|
|
||||||
|
|
||||||
function onSure(){
|
|
||||||
validateFields((error,values)=>{
|
|
||||||
if(!error){
|
|
||||||
onOk(values);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const layout = {
|
|
||||||
labelCol: { span: 5 },
|
|
||||||
wrapperCol: { span: 18 },
|
|
||||||
};
|
|
||||||
return(
|
|
||||||
<Modal
|
|
||||||
visible={visible}
|
|
||||||
title="提示"
|
|
||||||
width="500px"
|
|
||||||
closable={false}
|
|
||||||
footer={
|
|
||||||
<Button type="primary" onClick={onSure}>确定</Button>
|
|
||||||
}
|
|
||||||
centered
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<p className="mb15 edu-txt-center" style={{maxWidth:"350px",margin:"0px auto"}}>
|
|
||||||
为确保您能正常使用平台功能,请确认以下信息:
|
|
||||||
</p>
|
|
||||||
<Form {...layout}>
|
|
||||||
<Form.Item label="邮箱">
|
|
||||||
{getFieldDecorator("email",{
|
|
||||||
rules:[{required:true,message:"请输入邮箱账号"}]
|
|
||||||
})(
|
|
||||||
<Input placeholder="请输入您的邮箱账号" width="220px"/>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label="密码">
|
|
||||||
{getFieldDecorator("password",{
|
|
||||||
rules:[{required:true,message:"请输入邮箱密码"}]
|
|
||||||
})(
|
|
||||||
<Input.Password placeholder="请输入您的邮箱密码" width="220px"/>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Form.create()(forwardRef(EducoderAccount));
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
import React , { useState , useEffect } from 'react';
|
|
||||||
import { Select } from 'antd';
|
|
||||||
import axios from 'axios';
|
|
||||||
const Option = Select.Option;
|
|
||||||
const LANGUAGE = [
|
|
||||||
"apex",
|
|
||||||
"azcli",
|
|
||||||
"bat",
|
|
||||||
"clojure",
|
|
||||||
"coffee",
|
|
||||||
"cpp",
|
|
||||||
"csharp",
|
|
||||||
"csp",
|
|
||||||
"css",
|
|
||||||
"dockerfile",
|
|
||||||
"fsharp",
|
|
||||||
"go",
|
|
||||||
"handlebars",
|
|
||||||
"html",
|
|
||||||
"ini",
|
|
||||||
"java",
|
|
||||||
"javascript",
|
|
||||||
"json",
|
|
||||||
"less",
|
|
||||||
"lua",
|
|
||||||
"markdown",
|
|
||||||
"msdax",
|
|
||||||
"mysql",
|
|
||||||
"objective",
|
|
||||||
"perl",
|
|
||||||
"pgsql",
|
|
||||||
"php",
|
|
||||||
"postiats",
|
|
||||||
"powerquery",
|
|
||||||
"powershell",
|
|
||||||
"pug",
|
|
||||||
"python",
|
|
||||||
"r",
|
|
||||||
"razor",
|
|
||||||
"redis",
|
|
||||||
"redshift",
|
|
||||||
"ruby",
|
|
||||||
"rust",
|
|
||||||
"sb",
|
|
||||||
"scheme",
|
|
||||||
"scss",
|
|
||||||
"shell",
|
|
||||||
"solidity",
|
|
||||||
"sql",
|
|
||||||
"st",
|
|
||||||
"swift",
|
|
||||||
"typescript",
|
|
||||||
"vb",
|
|
||||||
"xml",
|
|
||||||
"yaml",
|
|
||||||
];
|
|
||||||
|
|
||||||
export default (({ language , select_language })=>{
|
|
||||||
const [ languages , setLanguage ] = useState(undefined);
|
|
||||||
|
|
||||||
// useEffect(()=>{
|
|
||||||
// const url = '/dev_ops/languages.json';
|
|
||||||
// axios.get(url).then(result=>{
|
|
||||||
// if(result){
|
|
||||||
// setLanguage(result.data);
|
|
||||||
// }
|
|
||||||
// }).catch(error=>{
|
|
||||||
// console.log(error);
|
|
||||||
// })
|
|
||||||
// },[])
|
|
||||||
|
|
||||||
function changelanguage(value){
|
|
||||||
// let array = value ? languages.filter(item=>item.name === value) :undefined;
|
|
||||||
select_language(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<Select showSearch={true} placeholder={"请选择文本语言"} style={{ width: 200 }} value={language} onChange={changelanguage}>
|
|
||||||
<Option value={undefined}>请选择文本语言</Option>
|
|
||||||
{LANGUAGE && LANGUAGE.map((item, key) => {
|
|
||||||
return <Option value={item}>{item}</Option>;
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import Handbook from '../Images/handbook.png';
|
|
||||||
import './Component.scss';
|
|
||||||
|
|
||||||
export default (()=>{
|
|
||||||
return(
|
|
||||||
<div className="handleBox">
|
|
||||||
<a href="https://forum.trustie.net/forums/3075/detail" target="_blank" >
|
|
||||||
<img src={Handbook} alt=""/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default ({title , value , className})=>{
|
export default ({title , value , className})=>{
|
||||||
const Keys = styled.span`
|
const Keys = styled.span`
|
||||||
display:flex;
|
display:flex;
|
||||||
align-items:center;
|
align-item:center;
|
||||||
& span{
|
& span{
|
||||||
display:block;
|
display:block;
|
||||||
height:20px;
|
height:20px;
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { FlexAJ } from '../Component/layout';
|
|
||||||
|
|
||||||
function LanguagePower({languages}){
|
|
||||||
const [ array , setArray ] = useState(undefined);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(languages){
|
|
||||||
let arr = [];
|
|
||||||
Object.keys(languages).map((item,key)=>{
|
|
||||||
arr.push({name:item,percent:languages[item],color:getColor()});
|
|
||||||
})
|
|
||||||
setArray(arr);
|
|
||||||
}
|
|
||||||
},[languages])
|
|
||||||
|
|
||||||
function getColor(){
|
|
||||||
let str = "#";
|
|
||||||
let arr = ["1","2","3","4","4","5","6","7","8","9","a","b","c","d","e","f"];
|
|
||||||
for(var i=0;i<6;i++){
|
|
||||||
let num = parseInt(Math.random() * 16);
|
|
||||||
str+=arr[num];
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<p className="font-16 color-grey-6">开发语言</p>
|
|
||||||
<div className="progress">
|
|
||||||
{
|
|
||||||
array && array.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<span style={{width:item.percent,backgroundColor:item.color}}></span>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
array && array.length > 0 &&
|
|
||||||
<FlexAJ className="progresstip">
|
|
||||||
{
|
|
||||||
array.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<span><i className="zero" style={{backgroundColor:`${item.color}`}}></i><span>{item.name}</span><span>{item.percent}</span></span>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</FlexAJ>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default LanguagePower;
|
|
||||||
|
|
@ -19,7 +19,7 @@ export default (({fork,parise})=>{
|
||||||
}`;
|
}`;
|
||||||
const SpanStyleparise = styled.span`{
|
const SpanStyleparise = styled.span`{
|
||||||
display:flex;
|
display:flex;
|
||||||
align-items:center;
|
align-item:center;
|
||||||
margin-left:30px;
|
margin-left:30px;
|
||||||
padding:0px 12px;
|
padding:0px 12px;
|
||||||
border-radius:13px;
|
border-radius:13px;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './Component.scss';
|
import './Component.scss';
|
||||||
import { Button } from 'antd';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import FocusButton from "../UsersList/focus_button";
|
|
||||||
import { getImageUrl } from 'educoder';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
const Img = styled.img`{
|
const Img = styled.img`{
|
||||||
border-radius:50%;
|
border-radius:50%;
|
||||||
|
|
@ -32,8 +28,26 @@ const I = styled.i`{
|
||||||
font-size:13px!important;
|
font-size:13px!important;
|
||||||
color:#60B25E;
|
color:#60B25E;
|
||||||
margin-right:2px;
|
margin-right:2px;
|
||||||
height:17px;
|
}`
|
||||||
line-height:17px;
|
const FocusBtn = styled.a`{
|
||||||
|
display:inline-block;
|
||||||
|
height:30px;
|
||||||
|
line-height:26px;
|
||||||
|
padding:0px 12px;
|
||||||
|
background-color:#fafafa;
|
||||||
|
border:1px solid #eee;
|
||||||
|
border-radius:2px;
|
||||||
|
color:#888!important;
|
||||||
|
}`
|
||||||
|
const Ifocused = styled.i`{
|
||||||
|
font-size:16px!important;
|
||||||
|
color:#FFA802;
|
||||||
|
margin-right:4px;
|
||||||
|
}`
|
||||||
|
const Ifocus = styled.i`{
|
||||||
|
font-size:16px!important;
|
||||||
|
color:#BBBBBB;
|
||||||
|
margin-right:4px;
|
||||||
}`
|
}`
|
||||||
const Div = styled.div`{
|
const Div = styled.div`{
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
|
|
@ -42,18 +56,17 @@ const Div = styled.div`{
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border:1px solid #eee;
|
border:1px solid #eee;
|
||||||
}`
|
}`
|
||||||
export default (({ user , img, name, time, focusStatus, is_current_user, login , successFunc }) => {
|
export default (({img,name,time, focusStatus})=>{
|
||||||
return (
|
return(
|
||||||
<Div>
|
<Div>
|
||||||
<Link to={`/users/${user && user.login}`}><Img src={getImageUrl(`/${img}`)} /></Link>
|
<Img src={img}/>
|
||||||
<div className="m-infos">
|
<div className="m-infos">
|
||||||
<Link to={`/users/${user && user.login}`}><Name>{name}</Name></Link>
|
<Name>{name}</Name>
|
||||||
<Time><I className="iconfont icon-shijian"></I>加入时间:{time}</Time>
|
<Time><I className="iconfont icon-shijian"></I>加入时间:{time}</Time>
|
||||||
{
|
{
|
||||||
is_current_user ?
|
focusStatus ?
|
||||||
<Button type="default">当前用户</Button>
|
<FocusBtn><Ifocused className="iconfont icon-shixing"></Ifocused>已关注</FocusBtn> :
|
||||||
:
|
<FocusBtn><Ifocus className="iconfont icon-kongxing"></Ifocus>关注</FocusBtn>
|
||||||
<FocusButton is_watch={focusStatus} id={login} successFunc={successFunc}/>
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Div>
|
</Div>
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { Modal , Button } from 'antd';
|
|
||||||
import './Component.scss';
|
|
||||||
|
|
||||||
function Modals({visible,title,content,onOk,onCancel}){
|
|
||||||
return(
|
|
||||||
<Modal
|
|
||||||
className="modalsStyle"
|
|
||||||
visible={visible}
|
|
||||||
title={title}
|
|
||||||
onCancel={onCancel}
|
|
||||||
closable={true}
|
|
||||||
footer={
|
|
||||||
<div>
|
|
||||||
<Button onClick={onCancel}>取消</Button>
|
|
||||||
<Button type={"primary"} style={{marginLeft:"20px"}} onClick={onOk}>确定</Button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div style={{fontSize:"16px"}}>{content}</div>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Modals;
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import React , { useState , useEffect } from 'react';
|
|
||||||
import { Select } from 'antd';
|
|
||||||
import { getUrl } from 'educoder';
|
|
||||||
|
|
||||||
import axios from 'axios';
|
|
||||||
const Option = Select.Option;
|
|
||||||
|
|
||||||
export default (({ language , select_language })=>{
|
|
||||||
const [ six , setSix ] = useState(undefined);
|
|
||||||
const [ languages , setLanguage ] = useState(undefined);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
const url = '/ci/languages.json';
|
|
||||||
axios.get(url).then(result=>{
|
|
||||||
if(result){
|
|
||||||
setLanguage(result.data);
|
|
||||||
}
|
|
||||||
}).catch(error=>{
|
|
||||||
console.log(error);
|
|
||||||
})
|
|
||||||
},[])
|
|
||||||
|
|
||||||
function changelanguage(value){
|
|
||||||
let array = value ? languages.filter(item=>item.name === value) :undefined;
|
|
||||||
select_language(value,array && array[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
const url = '/ci/languages/common.json';
|
|
||||||
axios.get(url).then(result=>{
|
|
||||||
if(result){
|
|
||||||
setSix(result.data);
|
|
||||||
}
|
|
||||||
}).catch(error=>{
|
|
||||||
console.log(error);
|
|
||||||
})
|
|
||||||
},[])
|
|
||||||
|
|
||||||
return(
|
|
||||||
<React.Fragment>
|
|
||||||
{
|
|
||||||
six &&
|
|
||||||
<ul className="language">
|
|
||||||
{
|
|
||||||
six.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
key < 6 ? <li className={language ===item.name ? "active":""} onClick={()=>changelanguage(item.name)}><img alt="" src={item.cover_url && getUrl(item.cover_url)} /></li> : ""
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
<Select showSearch={true} placeholder={"请选择文本语言"} style={{ width: 200 }} value={language} onChange={changelanguage}>
|
|
||||||
<Option value={undefined}>请选择文本语言</Option>
|
|
||||||
{languages && languages.map((item, key) => {
|
|
||||||
return <Option value={item.name}>{item.name}</Option>;
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import './Component.scss';
|
|
||||||
|
|
||||||
export const Tags = (status)=>{
|
|
||||||
switch(status){
|
|
||||||
case "running":
|
|
||||||
return(
|
|
||||||
<span className="statusColor running">运行中</span>
|
|
||||||
);
|
|
||||||
case "failure":case"error":
|
|
||||||
return (
|
|
||||||
<span className="statusColor failed">未通过</span>
|
|
||||||
);
|
|
||||||
case "success":
|
|
||||||
return (
|
|
||||||
<span className="statusColor pass">已通过</span>
|
|
||||||
);
|
|
||||||
case "pending":
|
|
||||||
return (
|
|
||||||
<span className="statusColor Preparing">准备中</span>
|
|
||||||
);
|
|
||||||
case 'killed':
|
|
||||||
return (
|
|
||||||
<span className="statusColor killed">已撤销</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TagsLine = (status)=>{
|
|
||||||
switch(status){
|
|
||||||
case "running":
|
|
||||||
return(
|
|
||||||
<span className="statuslineColor running">运行中</span>
|
|
||||||
);
|
|
||||||
case "failure":case "error":
|
|
||||||
return (
|
|
||||||
<span className="statuslineColor failed">未通过</span>
|
|
||||||
);
|
|
||||||
case "success":
|
|
||||||
return (
|
|
||||||
<span className="statuslineColor pass">已通过</span>
|
|
||||||
);
|
|
||||||
case "pending":
|
|
||||||
return (
|
|
||||||
<span className="statuslineColor Preparing">准备中</span>
|
|
||||||
);
|
|
||||||
case 'killed':
|
|
||||||
return (
|
|
||||||
<span className="statuslineColor killed">已撤销</span>
|
|
||||||
);
|
|
||||||
case 'skipped':
|
|
||||||
return (
|
|
||||||
<span className="statuslineColor skipped">已跳过</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import React ,{ useState } from 'react';
|
|
||||||
import { Modal , Input , Spin } from 'antd';
|
|
||||||
import { AlignCenter } from "./layout";
|
|
||||||
import axios from 'axios';
|
|
||||||
import './Component.scss';
|
|
||||||
|
|
||||||
function PasswordAuthority({ authorityValBox , successFunc , cancelFunc }){
|
|
||||||
const [ authorityVal , setAuthorityVal ] = useState(undefined);
|
|
||||||
const [ authorityValFlag , setAuthorityValFlag ] = useState(false);
|
|
||||||
const [ isSpin , setIsSpin ] = useState(false);
|
|
||||||
|
|
||||||
// 取消授权-登录密码的输入
|
|
||||||
function cancelAuthority(){
|
|
||||||
setAuthorityVal(undefined);
|
|
||||||
cancelFunc();
|
|
||||||
}
|
|
||||||
// 确认授权
|
|
||||||
function okAuthority(){
|
|
||||||
if(!authorityVal){
|
|
||||||
setAuthorityValFlag(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setIsSpin(true);
|
|
||||||
const url = `/users/ci/oauth_grant.json`;
|
|
||||||
axios.get(url,{
|
|
||||||
params:{password:authorityVal}
|
|
||||||
}).then(result=>{
|
|
||||||
setIsSpin(false);
|
|
||||||
if(result){
|
|
||||||
successFunc(result.data.step);
|
|
||||||
}
|
|
||||||
}).catch(error=>{setIsSpin(false);});
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<Modal visible={authorityValBox} centered={true} title="授权" onCancel={cancelAuthority} onOk={okAuthority}>
|
|
||||||
<Spin spinning={isSpin}>
|
|
||||||
<p style={{textAlign:"center"}}>请输入您的登录密码,确认授权DevOps应用</p>
|
|
||||||
<AlignCenter style={{justifyContent:"center",marginTop:"20px"}}>
|
|
||||||
<span>密码:</span>
|
|
||||||
<Input.Password value={authorityVal} className={authorityValFlag===true && "flags"} onChange={(e)=>setAuthorityVal(e.target.value)} style={{width:"220px"}}></Input.Password>
|
|
||||||
</AlignCenter>
|
|
||||||
</Spin>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default PasswordAuthority;
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { AlignCenter , AlignTop , FlexAJ } from '../Component/layout';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
function Releases({owner,projectsId,releaseVersions}){
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<FlexAJ>
|
|
||||||
<AlignCenter><span className="font-16 color-grey-6">发行版</span>
|
|
||||||
{ releaseVersions && releaseVersions.total_count > 0 && <span className="infoCount">{releaseVersions.total_count}</span>}
|
|
||||||
</AlignCenter>
|
|
||||||
{ releaseVersions && releaseVersions.total_count > 0 ?
|
|
||||||
<Link className="font-12 color-grey-9" to={`/projects/${owner}/${projectsId}/releases`}>全部</Link>
|
|
||||||
:
|
|
||||||
<Link className="font-12 color-blue" to={`/projects/${owner}/${projectsId}/releases/new`}>新建</Link>
|
|
||||||
}
|
|
||||||
</FlexAJ>
|
|
||||||
{
|
|
||||||
releaseVersions && releaseVersions.total_count>0 ?
|
|
||||||
releaseVersions.list.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
key === 0 &&<AlignTop className="mt10">
|
|
||||||
<i className="iconfont icon-biaoqian3 color-grey-6 font-18 mr10"></i>
|
|
||||||
<div>
|
|
||||||
<p className="font-16 color-grey-6">
|
|
||||||
<Link to={`/projects/${owner}/${projectsId}/releases`}>{item.name}</Link>
|
|
||||||
<span className="font-12 laterest ml5">最新</span>
|
|
||||||
</p>
|
|
||||||
<p className="color-grey-9 font-13">{item.created_at}</p>
|
|
||||||
</div>
|
|
||||||
</AlignTop>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
:""
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Releases;
|
|
||||||
|
|
@ -2,15 +2,13 @@ import React from "react";
|
||||||
import { Input } from "antd";
|
import { Input } from "antd";
|
||||||
|
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
export default ({ placeholder , onSearch , onChange }) => {
|
export default ({ placeholder , onSearch }) => {
|
||||||
return (
|
return (
|
||||||
<Search
|
<Search
|
||||||
allowClear
|
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
enterButton={'搜索'}
|
enterButton={'搜索'}
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
width="300px"
|
width="300px"
|
||||||
onChange={onChange}
|
|
||||||
></Search>
|
></Search>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,53 +2,52 @@ import React , { useState , useEffect } from 'react';
|
||||||
import { AutoComplete } from 'antd';
|
import { AutoComplete } from 'antd';
|
||||||
import { getImageUrl } from "educoder";
|
import { getImageUrl } from "educoder";
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const Option = AutoComplete.Option;
|
const Option = AutoComplete.Option;
|
||||||
|
|
||||||
export default ({ getUser , placeholder, width ,value })=>{
|
export default ({ getUser })=>{
|
||||||
const [ source , setSource ] = useState(undefined);
|
|
||||||
const [ searchKey , setSearchKey ] = useState(undefined);
|
const [ searchKey , setSearchKey ] = useState(undefined);
|
||||||
|
const [ userDataSource , setUserDataSource ] = useState(undefined);
|
||||||
useEffect(()=>{
|
|
||||||
if(!value){
|
|
||||||
setSearchKey(undefined);
|
|
||||||
}
|
|
||||||
},[value])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
getUserList();
|
|
||||||
},[searchKey])
|
|
||||||
|
|
||||||
function getUserList(e){
|
function getUserList(e){
|
||||||
const url = `/users/list.json`;
|
const url = `/users/list.json`;
|
||||||
axios.get(url, {
|
axios.get(url, {
|
||||||
params: {
|
params: {
|
||||||
search: searchKey,
|
search: e,
|
||||||
},
|
},
|
||||||
}).then((result) => {
|
})
|
||||||
|
.then((result) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
sourceOptions(result.data.users);
|
setUserDataSource(result.data.users);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function changeInputUser(value){
|
||||||
|
setSearchKey(value);
|
||||||
|
getUserList(value);
|
||||||
|
}
|
||||||
|
|
||||||
function sourceOptions(userDataSource){
|
function selectInputUser(id, option){
|
||||||
const s = userDataSource && userDataSource.map((item, key) => {
|
setSearchKey(option.props.searchValue);
|
||||||
|
getUserList(option.props.searchValue);
|
||||||
|
getUser && getUser(id);
|
||||||
|
}
|
||||||
|
const source =
|
||||||
|
userDataSource && userDataSource.map((item, key) => {
|
||||||
return (
|
return (
|
||||||
<Option
|
<Option
|
||||||
key={key}
|
key={key}
|
||||||
value={`${item.user_id}`}
|
value={`${item.user_id}`}
|
||||||
login={`${item.login}`}
|
searchValue={`${item.username}`}
|
||||||
name={item.username}
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="user_img radius"
|
className="user_img radius"
|
||||||
width="28"
|
width="28"
|
||||||
height="28"
|
height="28"
|
||||||
src={getImageUrl(`/${item && item.image_url}`)}
|
src={getImageUrl(`images/${item && item.image_url}`)}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<span className="ml10" style={{ "vertical-align": "middle" }}>
|
<span className="ml10" style={{ "vertical-align": "middle" }}>
|
||||||
|
|
@ -58,31 +57,14 @@ export default ({ getUser , placeholder, width ,value })=>{
|
||||||
</Option>
|
</Option>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
setSource(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeInputUser(e){
|
|
||||||
setSearchKey(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选择用户
|
|
||||||
function selectInputUser(e, option){
|
|
||||||
setSearchKey(option.props.name);
|
|
||||||
getUser(option.props.login);
|
|
||||||
};
|
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div className="addPanel">
|
<AutoComplete
|
||||||
<AutoComplete
|
dataSource={source}
|
||||||
getPopupContainer={trigger => trigger.parentNode}
|
value={searchKey}
|
||||||
dataSource={source}
|
style={{ width: 300 }}
|
||||||
value={searchKey}
|
onChange={changeInputUser}
|
||||||
style={{ width: width || 300 }}
|
onSelect={selectInputUser}
|
||||||
onChange={changeInputUser}
|
placeholder="搜索需要添加的用户..."
|
||||||
onSelect={selectInputUser}
|
/>
|
||||||
placeholder={placeholder || "搜索需要添加的用户..."}
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
export default ({ url , name , column , id , login })=>{
|
|
||||||
|
export default ({ url , name , column })=>{
|
||||||
const Img = styled.span`
|
const Img = styled.span`
|
||||||
display:flex;
|
display:flex;
|
||||||
${column && "flex-direction: column;text-align:center;"}
|
${column && "flex-direction: column;text-align:center;"}
|
||||||
|
|
@ -19,16 +19,8 @@ export default ({ url , name , column , id , login })=>{
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
return(
|
return(
|
||||||
id?
|
|
||||||
<Link to={`/users/${login}`}>
|
|
||||||
<Img>
|
|
||||||
{ url && <img src={url} alt=""/> }
|
|
||||||
<span>{name}</span>
|
|
||||||
</Img>
|
|
||||||
</Link>
|
|
||||||
:
|
|
||||||
<Img>
|
<Img>
|
||||||
{ url && <img src={url} alt=""/> }
|
<img src={url} alt=""/>
|
||||||
<span>{name}</span>
|
<span>{name}</span>
|
||||||
</Img>
|
</Img>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,33 +24,18 @@ export const AlignCenter = styled.div`{
|
||||||
display:flex;
|
display:flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}`
|
}`
|
||||||
export const AlignTop = styled.div`{
|
|
||||||
display:flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
}`
|
|
||||||
// 左右结构
|
// 左右结构
|
||||||
export const Box = styled.div`{
|
export const Box = styled.div`{
|
||||||
display:flex;
|
display:flex;
|
||||||
align-items:flex-start;
|
align-item:flex-start;
|
||||||
}`
|
|
||||||
export const LongWidth = styled.div`{
|
|
||||||
flex:1;
|
|
||||||
width:0;
|
|
||||||
border-radius:5px;
|
|
||||||
margin-bottom:30px;
|
|
||||||
}`
|
}`
|
||||||
export const Long = styled.div`{
|
export const Long = styled.div`{
|
||||||
width:78%;
|
width:72%;
|
||||||
border-radius:5px;
|
|
||||||
margin-bottom:30px;
|
|
||||||
}`
|
|
||||||
export const ShortWidth = styled.div`{
|
|
||||||
width:300px;
|
|
||||||
border-radius:5px;
|
border-radius:5px;
|
||||||
margin-bottom:30px;
|
margin-bottom:30px;
|
||||||
}`
|
}`
|
||||||
export const Short = styled.div`{
|
export const Short = styled.div`{
|
||||||
flex:1;
|
width:28%;
|
||||||
border-radius:5px;
|
border-radius:5px;
|
||||||
margin-bottom:30px;
|
margin-bottom:30px;
|
||||||
}`
|
}`
|
||||||
|
|
@ -76,24 +61,22 @@ export const Redline = styled.a`{
|
||||||
line-height:28px;
|
line-height:28px;
|
||||||
border-radius:2px;
|
border-radius:2px;
|
||||||
border:1px solid #F73030;
|
border:1px solid #F73030;
|
||||||
color:${props => (props.bold ? "#fff" : "#F73030")} !important;
|
color:#F73030;
|
||||||
padding:0px 12px;
|
padding:0px 12px;
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
min-width:80px;
|
min-width:80px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
background:${props => (props.bold ? "#F73030" : "#fff")};
|
|
||||||
}`
|
}`
|
||||||
export const Greenline = styled.a`{
|
export const Greenline = styled.a`{
|
||||||
height:30px;
|
height:30px;
|
||||||
line-height:28px;
|
line-height:28px;
|
||||||
border-radius:2px;
|
border-radius:2px;
|
||||||
border:1px solid #28BD6C;
|
border:1px solid #28BD6C;
|
||||||
color:${props => (props.bold ? "#fff" : "#28BD6C")} !important;
|
color:#28BD6C;
|
||||||
padding:0px 12px;
|
padding:0px 12px;
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
min-width:80px;
|
min-width:80px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
background:${props => (props.bold ? "#28BD6C" : "#fff")};
|
|
||||||
}`
|
}`
|
||||||
export const Greenback = styled.a`{
|
export const Greenback = styled.a`{
|
||||||
height:30px;
|
height:30px;
|
||||||
|
|
@ -106,17 +89,6 @@ export const Greenback = styled.a`{
|
||||||
min-width:80px;
|
min-width:80px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}`
|
}`
|
||||||
export const Blueback = styled.a`{
|
|
||||||
height:30px;
|
|
||||||
line-height:30px;
|
|
||||||
border-radius:2px;
|
|
||||||
background-color:rgba(80,145,255,1);
|
|
||||||
color:#fff!important;
|
|
||||||
padding:0px 12px;
|
|
||||||
display:inline-block;
|
|
||||||
min-width:80px;
|
|
||||||
text-align:center;
|
|
||||||
}`
|
|
||||||
export const Redback = styled.a`{
|
export const Redback = styled.a`{
|
||||||
height:30px;
|
height:30px;
|
||||||
line-height:30px;
|
line-height:30px;
|
||||||
|
|
@ -171,9 +143,3 @@ export const Content = styled.div`{
|
||||||
background-color:#fff;
|
background-color:#fff;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}`
|
}`
|
||||||
export const GroupProjectBackgroup = styled.div`{
|
|
||||||
background:#fafafa;
|
|
||||||
padding:20px 30px;
|
|
||||||
width:100%;
|
|
||||||
}`
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,253 +0,0 @@
|
||||||
import React, { forwardRef, useCallback, useState , useEffect } from "react";
|
|
||||||
import activate from "../Images/activate.png";
|
|
||||||
import { AlignCenter, Blueback } from "../Component/layout";
|
|
||||||
import PasswordAuthority from "../Component/PasswordAuthority";
|
|
||||||
import styled from "styled-components";
|
|
||||||
import { Form, Input , Spin , Button } from "antd";
|
|
||||||
import ServiceModal from './ServiceModal';
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
const P = styled.p`
|
|
||||||
{
|
|
||||||
width: 230px;
|
|
||||||
line-height: 30px;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 30px;
|
|
||||||
margin-bottom: 30px !important;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
function About(props, ref) {
|
|
||||||
const { form: { getFieldDecorator, validateFields , setFieldsValue } } = props;
|
|
||||||
const [isSpining, setIsSpining] = useState(true);
|
|
||||||
|
|
||||||
// 认证密码
|
|
||||||
const [ authorityVal , setAuthorityVal ] = useState(undefined);
|
|
||||||
const [ authorityValFlag , setAuthorityValFlag ] = useState(false);
|
|
||||||
|
|
||||||
//0: 标识未开启devops
|
|
||||||
//1: 标识用户已填写了云服务器相关信息,但并未开启认证
|
|
||||||
const [step, setStep] = useState(0);
|
|
||||||
|
|
||||||
const owner = props.match.params.owner;
|
|
||||||
const { user } = props;
|
|
||||||
const projectsId = props.match.params.projectsId;
|
|
||||||
const [ disabled, setDisabled ] = useState(false);
|
|
||||||
const [ typeFlag, setTypeFlag] = useState(false);
|
|
||||||
|
|
||||||
const AuthorLogin = props.projectDetail && props.projectDetail.author && props.projectDetail.author.login;
|
|
||||||
const CurrentLogin = props.current_user && props.current_user.login;
|
|
||||||
useEffect(()=>{
|
|
||||||
if(CurrentLogin === AuthorLogin){
|
|
||||||
auth('get');
|
|
||||||
}else{
|
|
||||||
setIsSpining(false);
|
|
||||||
}
|
|
||||||
},[AuthorLogin,CurrentLogin])
|
|
||||||
|
|
||||||
function auth(type){
|
|
||||||
const url = `/${owner}/${projectsId}/ci_authorize.json`;
|
|
||||||
axios({
|
|
||||||
method:`${type}`,
|
|
||||||
url
|
|
||||||
}).then(result=>{
|
|
||||||
setIsSpining(false);
|
|
||||||
if(result && result.data ){
|
|
||||||
setStep(result.data.step);
|
|
||||||
}
|
|
||||||
}).catch(error=>{
|
|
||||||
setIsSpining(false);
|
|
||||||
console.log(error);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const helper = useCallback(
|
|
||||||
(label, name, rules, widget, isRequired) => (
|
|
||||||
<React.Fragment>
|
|
||||||
<span className={isRequired ? "required" : ""}>{label}</span>
|
|
||||||
<Form.Item>
|
|
||||||
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
|
|
||||||
</Form.Item>
|
|
||||||
</React.Fragment>
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
// 下一步
|
|
||||||
function goStep() {
|
|
||||||
validateFields((error, values) => {
|
|
||||||
if (!error) {
|
|
||||||
if(disabled){
|
|
||||||
setStep(1);
|
|
||||||
}else{
|
|
||||||
setIsSpining(true);
|
|
||||||
const url = `/${owner}/${projectsId}/cloud_accounts.json`;
|
|
||||||
axios.post(url, {...values,ip_num:values.ip}).then((result) => {
|
|
||||||
setIsSpining(false);
|
|
||||||
if (result && result.data.redirect_url) {
|
|
||||||
props.showNotification("服务器信息配置完成!");
|
|
||||||
setStep(1);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
setIsSpining(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 选择服务器后调用接口,点击下一步(1是自有服务器,2是trustie服务器)
|
|
||||||
function sureModal(type){
|
|
||||||
if(type === 2){
|
|
||||||
setTypeFlag(false);
|
|
||||||
setIsSpining(true);
|
|
||||||
const url = `/users/ci/cloud_account/trustie_bind.json`;
|
|
||||||
axios.post(url,{
|
|
||||||
account:user && user.login
|
|
||||||
}).then(result=>{
|
|
||||||
setIsSpining(false);
|
|
||||||
if(result && result.data){
|
|
||||||
setStep(result.data.step);
|
|
||||||
}
|
|
||||||
}).catch(error=>{setIsSpining(false)})
|
|
||||||
}else{
|
|
||||||
setTypeFlag(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上一步
|
|
||||||
function goUpStep(step){
|
|
||||||
setTypeFlag(false);
|
|
||||||
setStep(step);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 输入用户密码后下一步
|
|
||||||
function authStep(){
|
|
||||||
if(!authorityVal){
|
|
||||||
setAuthorityValFlag(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setAuthorityValFlag(false);
|
|
||||||
setIsSpining(true);
|
|
||||||
const url = `/users/ci/oauth_grant.json`;
|
|
||||||
axios.get(url,{
|
|
||||||
params:{password:authorityVal}
|
|
||||||
}).then(result=>{
|
|
||||||
setIsSpining(false);
|
|
||||||
if(result){
|
|
||||||
setStep(result.data.step);
|
|
||||||
}
|
|
||||||
}).catch(error=>{setIsSpining(false)});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始激活
|
|
||||||
function startActive(){
|
|
||||||
setIsSpining(true);
|
|
||||||
const url = `/${owner}/${projectsId}/activate.json`;
|
|
||||||
axios.post(url).then(result=>{
|
|
||||||
setIsSpining(false);
|
|
||||||
if(result && result.data.status === 0){
|
|
||||||
props.history.push(`/projects/${owner}/${projectsId}/devops/dispose`);
|
|
||||||
// 需要将顶部的open_devops修改
|
|
||||||
let { changeOpenDevops } = props;
|
|
||||||
changeOpenDevops && changeOpenDevops(true);
|
|
||||||
}
|
|
||||||
}).catch(error=>{
|
|
||||||
console.log(error);
|
|
||||||
setIsSpining(false);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Spin spinning={isSpining}>
|
|
||||||
{/* <PasswordAuthority authorityValBox={authorityValBox} successFunc={okAuthority} cancelFunc={cancelAuthority}></PasswordAuthority> */}
|
|
||||||
<div className="activatePanel">
|
|
||||||
<img src={activate} alt="" width="250px" />
|
|
||||||
<P>定义DevOps工作流,帮助您检测bug、发布代码…</P>
|
|
||||||
{
|
|
||||||
CurrentLogin !== AuthorLogin ?
|
|
||||||
<div className="noOperation">DevOps开启功能暂未对项目创建者以外的角色开放,可以联系项目创建者进行开启,开启后便可查看构建信息。</div>:""
|
|
||||||
}
|
|
||||||
<a href={"https://forum.trustie.net/forums/3110/detail"} target="_blank" style={{ color: "#5091FF"}}>
|
|
||||||
了解什么是DevOps?
|
|
||||||
</a>
|
|
||||||
<a href={"https://forum.trustie.net/forums/3080/detail"} target="_blank" style={{ color: "#5091FF"}}>
|
|
||||||
如何使用DevOps?
|
|
||||||
</a>
|
|
||||||
{
|
|
||||||
AuthorLogin === CurrentLogin ?
|
|
||||||
<React.Fragment>
|
|
||||||
{
|
|
||||||
step === 0 && !typeFlag ?
|
|
||||||
<ServiceModal sureModal={sureModal}></ServiceModal>
|
|
||||||
:""
|
|
||||||
}
|
|
||||||
{ step === 0 && typeFlag ?
|
|
||||||
<React.Fragment>
|
|
||||||
<Input.Password style={{display:'none'}} size="large" />
|
|
||||||
<Form style={{marginTop:"20px"}}>
|
|
||||||
<p className="mb20" style={{width:"370px"}}>请仔细核对您的服务器信息,一旦确认提交将无法修改</p>
|
|
||||||
{helper(
|
|
||||||
"服务器IP地址:",
|
|
||||||
"ip",
|
|
||||||
[{ required: true, message: "请输入服务器IP地址" }],
|
|
||||||
<Input
|
|
||||||
placeholder="请输入服务器IP地址"
|
|
||||||
style={{ width: "368px" }}
|
|
||||||
size="large"
|
|
||||||
disabled={disabled}
|
|
||||||
/>,
|
|
||||||
true
|
|
||||||
)}
|
|
||||||
{helper(
|
|
||||||
"服务器用户名:",
|
|
||||||
"account",
|
|
||||||
[{ required: true, message: "请输入服务器用户名" }],
|
|
||||||
<Input placeholder="请输入服务器用户名" size="large" disabled={disabled}/>,
|
|
||||||
true
|
|
||||||
)}
|
|
||||||
{helper(
|
|
||||||
"服务器密码:",
|
|
||||||
"secret",
|
|
||||||
[{ required: true, message: "请输入服务器密码" }],
|
|
||||||
<Input.Password placeholder="请输入服务器密码" size="large" disabled={disabled}/>,
|
|
||||||
true
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
<AlignCenter>
|
|
||||||
{ !disabled && <Button className="mr20" onClick={()=>goUpStep(0)}>上一步</Button>}
|
|
||||||
<Blueback onClick={goStep}>下一步</Blueback>
|
|
||||||
</AlignCenter>
|
|
||||||
</React.Fragment>
|
|
||||||
:""
|
|
||||||
}
|
|
||||||
{
|
|
||||||
step === 1 ?
|
|
||||||
<div>
|
|
||||||
<AlignCenter style={{justifyContent:"center",marginTop:"20px"}}>
|
|
||||||
<span style={{marginBottom:"42px"}}>密码:</span>
|
|
||||||
<div>
|
|
||||||
<Input.Password value={authorityVal} className={authorityValFlag===true && "flags"} onChange={(e)=>setAuthorityVal(e.target.value)} style={{width:"220px"}}></Input.Password>
|
|
||||||
<p className="color-grey-9" style={{textAlign:"left",lineHeight:'21px'}}>您已保存相关服务器信息,请输入密码,<br/>确认授权DevOps应用</p>
|
|
||||||
</div>
|
|
||||||
</AlignCenter>
|
|
||||||
<AlignCenter style={{justifyContent:'center'}}>
|
|
||||||
<Blueback onClick={authStep} className="mt20">下一步</Blueback>
|
|
||||||
</AlignCenter>
|
|
||||||
</div>:""
|
|
||||||
}
|
|
||||||
{ step === 2 ?
|
|
||||||
<div style={{textAlign:'center',marginTop:"20px"}}>
|
|
||||||
<Blueback onClick={startActive} className="mt20">开始激活</Blueback>
|
|
||||||
</div>:""
|
|
||||||
}
|
|
||||||
</React.Fragment>
|
|
||||||
:""
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default Form.create()(forwardRef(About));
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
import React , { useState , useEffect } from 'react';
|
|
||||||
import { Spin , Pagination } from 'antd';
|
|
||||||
import { Blueback } from '../Component/layout';
|
|
||||||
import List from './Dispose/List';
|
|
||||||
import Head from './Dispose/head';
|
|
||||||
import axios from 'axios';
|
|
||||||
import PipelineName from './Dispose/PipelineName';
|
|
||||||
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const Div = styled.div`{
|
|
||||||
padding:24px 30px;
|
|
||||||
}`;
|
|
||||||
const limit = 15;
|
|
||||||
function Dispose(props){
|
|
||||||
const [ spining , setSpining ] = useState(true);
|
|
||||||
const [ updateInfo , setUpdateInfo ] = useState(undefined);
|
|
||||||
const [ list , setList ] = useState(undefined);
|
|
||||||
const [ permission , setPermission ] = useState(undefined);
|
|
||||||
const [ visible , setVisible ] = useState(false);
|
|
||||||
const [ page , setPage ] = useState(1);
|
|
||||||
const [ totalCount , setTotalCount ] = useState(0);
|
|
||||||
const [ branchList , setBranchList ] =useState(undefined);
|
|
||||||
|
|
||||||
|
|
||||||
const projectDetail = props.projectDetail;
|
|
||||||
const current_user = props.current_user;
|
|
||||||
let projectsId = props.match.params.projectsId;
|
|
||||||
let owner = props.match.params.owner;
|
|
||||||
|
|
||||||
// 获取用户的身份角色
|
|
||||||
useEffect(()=>{
|
|
||||||
if(projectDetail){
|
|
||||||
setPermission(props.projectDetail.permission);
|
|
||||||
}
|
|
||||||
},[projectDetail])
|
|
||||||
|
|
||||||
function Init(){
|
|
||||||
const url = `/ci/pipelines/list.json`;
|
|
||||||
axios.get(url,{
|
|
||||||
params:{
|
|
||||||
identifier:projectsId,owner,
|
|
||||||
page,limit
|
|
||||||
}
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setList(result.data.pipelines);
|
|
||||||
}
|
|
||||||
setSpining(false);
|
|
||||||
}).catch(error=>{setSpining(false);})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
Init();
|
|
||||||
},[page])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(owner && projectsId){
|
|
||||||
const url = `/${owner}/${projectsId}/branches.json`;
|
|
||||||
axios.get(url).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setBranchList(result.data);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
},[owner,projectsId])
|
|
||||||
|
|
||||||
// 新增/编辑流水线
|
|
||||||
function addNew(pipeline_name,id,branch,event){
|
|
||||||
setVisible(true);
|
|
||||||
setUpdateInfo(undefined);
|
|
||||||
if(pipeline_name){
|
|
||||||
let eventA = event.split(",");
|
|
||||||
let l = {pipeline_name,id,branch,event:eventA}
|
|
||||||
setUpdateInfo(l);
|
|
||||||
}else{
|
|
||||||
setUpdateInfo(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOk(pipeline_name,updateId,branch,event){
|
|
||||||
if(pipeline_name){
|
|
||||||
let eventStr = "";
|
|
||||||
for(var i = 0;i<event.length;i++){
|
|
||||||
eventStr +=event[i]+",";
|
|
||||||
}
|
|
||||||
eventStr = eventStr.substring(0,eventStr.length-1);
|
|
||||||
if(!updateId){
|
|
||||||
// 新增
|
|
||||||
const url = `/ci/pipelines.json`;
|
|
||||||
axios.post(url,{
|
|
||||||
pipeline_name,
|
|
||||||
file_name:".trustie-pipeline.yml",
|
|
||||||
repo:projectsId,branch,event:eventStr,owner
|
|
||||||
}).then(result=>{
|
|
||||||
setVisible(false);
|
|
||||||
if(result && result.data){
|
|
||||||
props.showNotification("流水线新增成功,请进行工作流配置!");
|
|
||||||
props.history.push(`/projects/${owner}/${projectsId}/devops/dispose/${result.data.id}`);
|
|
||||||
}else{
|
|
||||||
props.showNotification("流水线新增失败,请稍后再试!");
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}else{
|
|
||||||
// 修改
|
|
||||||
const url = `/ci/pipelines/${updateId}.json`;
|
|
||||||
axios.put(url,{
|
|
||||||
pipeline_name,repo:projectsId,branch,event:eventStr,owner
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setVisible(false);
|
|
||||||
Init();
|
|
||||||
props.showNotification("流水线名称更新成功!");
|
|
||||||
}else{
|
|
||||||
props.showNotification("流水线名称更新失败,请稍后再试!");
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
props.showNotification("请输入流水线名称!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
function deleteFunc(id){
|
|
||||||
const url = `/ci/pipelines/${id}.json`;
|
|
||||||
axios.delete(url).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
props.showNotification("流水线删除成功!");
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模板管理
|
|
||||||
function toModalManage(){
|
|
||||||
props.history.push(`/projects/${owner}/${projectsId}/devops/mould`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 参数管理
|
|
||||||
function toparameter(){
|
|
||||||
props.history.push(`/projects/${owner}/${projectsId}/devops/params`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const operate = current_user && (permission && permission !== "Reporter");
|
|
||||||
return(
|
|
||||||
<Spin spinning={spining}>
|
|
||||||
<PipelineName branchList={branchList} visible={visible} value={updateInfo} onCancel={()=>setVisible(false)} onOk={onOk}/>
|
|
||||||
<div className="disposePanel">
|
|
||||||
<Head manager={ operate ? toModalManage : undefined} parameter={operate ? toparameter :undefined}/>
|
|
||||||
<Div>
|
|
||||||
{ operate && <Blueback onClick={()=>addNew(undefined,undefined)}>新增流水线</Blueback> }
|
|
||||||
<div className="mt20 disposeList">
|
|
||||||
<List list={list} operate={operate} projectsId={projectsId} owner={owner} showModal={addNew} deleteFunc={deleteFunc}/>
|
|
||||||
{
|
|
||||||
totalCount > limit &&
|
|
||||||
<div className="mt20 pb20" style={{textAlign:'center'}}>
|
|
||||||
<Pagination simple current={page} pageSize={limit} total={totalCount} onChange={(page)=>setPage(page)}/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Div>
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Dispose;
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
import React , { useEffect , useState } from 'react';
|
|
||||||
|
|
||||||
function Choosen({ chooseFunc, temp , templateId , category }){
|
|
||||||
const [ tempId, setTemId ] = useState(undefined);
|
|
||||||
const [ cate , setCate ]= useState(undefined);
|
|
||||||
const [ templates , setTemplates ] = useState(undefined);
|
|
||||||
const [ categories , setCategories ] = useState(undefined);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(templateId){
|
|
||||||
setTemId(templateId);
|
|
||||||
}
|
|
||||||
},[templateId])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(category){
|
|
||||||
setCate(category);
|
|
||||||
}
|
|
||||||
},[category])
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(temp && temp.length > 0){
|
|
||||||
if(temp[0].category !== "初始化"){
|
|
||||||
setCategories(temp);
|
|
||||||
}else{
|
|
||||||
setCategories(undefined);
|
|
||||||
}
|
|
||||||
if(category && temp[0].category !== "初始化" && category !== "初始化"){
|
|
||||||
let c = temp.filter(item=>item.category === category);
|
|
||||||
let t = c && c.length > 0 && c[0].templates;
|
|
||||||
setTemplates(t);
|
|
||||||
setCate(category);
|
|
||||||
}else{
|
|
||||||
setTemplates(temp[0].templates);
|
|
||||||
setCate(temp[0].category);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
setTemplates(undefined);
|
|
||||||
setCate(undefined);
|
|
||||||
setCategories(undefined);
|
|
||||||
}
|
|
||||||
},[temp,category])
|
|
||||||
|
|
||||||
// 选择类别
|
|
||||||
function changeCate(cate){
|
|
||||||
setCate(cate);
|
|
||||||
let c = categories && categories.filter(item=>item.category === cate);
|
|
||||||
let t = c && c[0].templates;
|
|
||||||
setTemplates(t);
|
|
||||||
let m_t_id = t && t.length>0 && t[0].id;
|
|
||||||
let m_t_content = t && t.length>0 && t[0].content;
|
|
||||||
setTemId(m_t_id);
|
|
||||||
chooseFunc && chooseFunc(m_t_content,m_t_id,cate);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 选择模板
|
|
||||||
function chooseOption(id){
|
|
||||||
let item = templates.filter(item=>item.id === id);
|
|
||||||
let content = item && item.length >0 && item[0].content;
|
|
||||||
chooseFunc && chooseFunc(content,id,cate);
|
|
||||||
setTemId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<React.Fragment>
|
|
||||||
{
|
|
||||||
categories && categories.length > 0 &&
|
|
||||||
<div className="choosenList">
|
|
||||||
<span>模板类别:</span>
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
categories.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<li className={cate === item.category ?"active":""} onClick={()=>changeCate(item.category)}>{item.category}</li>)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
templates && templates.length> 0 &&
|
|
||||||
<div className="choosenList">
|
|
||||||
<span>模板选择:</span>
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
templates.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<li className={tempId === item.id ? "active":""} onClick={()=>chooseOption(item.id)}>{item.template_name}</li>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Choosen;
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import Editor from 'react-monaco-editor';
|
|
||||||
|
|
||||||
function Editors({value,onChange,theme,height,visible,width="100%",Numbers="on"}){
|
|
||||||
const editor_options = {
|
|
||||||
lineNumbers: Numbers,
|
|
||||||
wordWrap: true, //强制换行
|
|
||||||
selectOnLineNumbers: true,
|
|
||||||
lineHeight: 24,
|
|
||||||
renderLineHighlight: "line",
|
|
||||||
revealHorizontalRightPadding: 5,
|
|
||||||
placeholder:"请输入内容",
|
|
||||||
readOnly: visible,
|
|
||||||
cursorStyle: visible ? "underline-thin" : "line",
|
|
||||||
folding: true,
|
|
||||||
foldingStrategy: "indentation", // 代码可分小段折叠
|
|
||||||
automaticLayout: true, // 自适应布局
|
|
||||||
minimap: {
|
|
||||||
// 不要小地图
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<Editor
|
|
||||||
height={height}
|
|
||||||
width={width}
|
|
||||||
language={"yaml"}
|
|
||||||
theme={theme}
|
|
||||||
placeholder="请输入内容"
|
|
||||||
value={value}
|
|
||||||
options={editor_options}
|
|
||||||
onChange={(value)=>onChange(value)}
|
|
||||||
disabled={true}
|
|
||||||
></Editor>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Editors;
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import React , { useEffect , useState } from 'react';
|
|
||||||
import { Button } from 'antd';
|
|
||||||
import Choosen from './Choosen';
|
|
||||||
import Editors from './Editors';
|
|
||||||
|
|
||||||
function Init({ datas , templates , saveFunc , saveDatas}){
|
|
||||||
const [ templateId , setTemplateId ] = useState(undefined);
|
|
||||||
const [ ymlValue , setYmlValue ] = useState(undefined);
|
|
||||||
const [ temp , setTemp ] = useState(undefined);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(templates && templates.length > 0){
|
|
||||||
setTemp(templates)
|
|
||||||
}
|
|
||||||
},[templates])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(datas && datas.length > 0){
|
|
||||||
setTemplateId(datas[0].template_id);
|
|
||||||
setYmlValue(datas[0].content);
|
|
||||||
}
|
|
||||||
},[datas])
|
|
||||||
|
|
||||||
// 选择模板
|
|
||||||
function chooseFunc(content,id,cate){
|
|
||||||
setTemplateId(id);
|
|
||||||
setYmlValue(content);
|
|
||||||
recieveData(id,content);
|
|
||||||
}
|
|
||||||
|
|
||||||
function recieveData(id,content){
|
|
||||||
let steps = datas;
|
|
||||||
if(datas && datas.length>0){
|
|
||||||
steps[0].content = content || ymlValue;
|
|
||||||
steps[0].template_id = id || templateId ;
|
|
||||||
}else{
|
|
||||||
steps =
|
|
||||||
[{
|
|
||||||
step_name:"初始化",
|
|
||||||
show_index:1,
|
|
||||||
content:content || ymlValue,
|
|
||||||
template_id:id || templateId
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
saveDatas(steps);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击下一步时
|
|
||||||
function nextStep(){
|
|
||||||
recieveData();
|
|
||||||
saveFunc(undefined,undefined,undefined,undefined,"next");
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<Choosen chooseFunc={chooseFunc} templateId={templateId} temp={temp}/>
|
|
||||||
<div className="mt15">
|
|
||||||
<Editors value={ymlValue} onChange={setYmlValue} theme={"vs-dark"} height={"400px"}/>
|
|
||||||
</div>
|
|
||||||
<div className="mt20">
|
|
||||||
<Button type={"primary"} onClick={nextStep}>下一步</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Init;
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { Table , Popconfirm } from 'antd';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
// const STATUS = {
|
|
||||||
// running:"运行中",
|
|
||||||
// failure:"未通过",
|
|
||||||
// error:"未通过",
|
|
||||||
// success:"已通过",
|
|
||||||
// killed:"已撤销",
|
|
||||||
// pending:"准备中"
|
|
||||||
// }
|
|
||||||
function renderTableStatus(status) {
|
|
||||||
switch (status) {
|
|
||||||
case "running":
|
|
||||||
return (
|
|
||||||
<span className="statusTag running">
|
|
||||||
<i className="iconfont icon-yunhangzhong"></i>运行中
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case "failure": case 'error':
|
|
||||||
return (
|
|
||||||
<span className="statusTag failed">
|
|
||||||
<i className="iconfont icon-weitongguo"></i>未通过
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case "success":
|
|
||||||
return (
|
|
||||||
<span className="statusTag pass">
|
|
||||||
<i className="iconfont icon-yitongguo"></i>已通过
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case 'killed':
|
|
||||||
return (
|
|
||||||
<span className="statusTag killed">
|
|
||||||
<i className="iconfont icon-weitongguo"></i>已撤销
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
default :
|
|
||||||
return (
|
|
||||||
<span className="statusTag Preparing">
|
|
||||||
<i className="iconfont icon-zhunbeizhong"></i>准备中
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function List({ list, operate , projectsId , owner , showModal , deleteFunc }){
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title:"流水线名称",
|
|
||||||
dataIndex:"pipeline_name",
|
|
||||||
key:1,
|
|
||||||
ellipsis:true,
|
|
||||||
render:(txt,item)=>{
|
|
||||||
return(
|
|
||||||
<span onDoubleClick={()=>showModal(txt,item.id,item.branch,item.event)} style={{display:"block",cursor:"pointer"}}>{txt}</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"文件名称",
|
|
||||||
dataIndex:"file_name",
|
|
||||||
key:1,
|
|
||||||
width:"15%",
|
|
||||||
ellipsis:true,
|
|
||||||
render:(value,item)=>{
|
|
||||||
return(
|
|
||||||
<Link to={`/projects/${owner}/${projectsId}/tree/${item.branch}/${value}`} className="color-blue">{value}</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"触发分支",
|
|
||||||
dataIndex:"branch",
|
|
||||||
key:1,
|
|
||||||
width:"10%",
|
|
||||||
ellipsis:true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"触发事件",
|
|
||||||
dataIndex:"event",
|
|
||||||
key:1,
|
|
||||||
width:"10%",
|
|
||||||
ellipsis:true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"最近构建时间",
|
|
||||||
dataIndex:"last_build_time",
|
|
||||||
key:1,
|
|
||||||
width:"15%",
|
|
||||||
ellipsis:true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"最近构建状态",
|
|
||||||
dataIndex:"pipeline_status",
|
|
||||||
key:1,
|
|
||||||
width:"12%",
|
|
||||||
ellipsis:true,
|
|
||||||
render:(txt)=>{
|
|
||||||
return renderTableStatus(txt)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"操作",
|
|
||||||
dataIndex:"operation",
|
|
||||||
key:1,
|
|
||||||
width:"21%",
|
|
||||||
render:(txt,item)=>{
|
|
||||||
return(
|
|
||||||
<span>
|
|
||||||
{ operate ?
|
|
||||||
<Link to={`/projects/${owner}/${projectsId}/devops/dispose/${item.id}`} className="mr10 color-grey-6">
|
|
||||||
<i className="iconfont icon-zaibianji font-13 mr3"></i>编辑</Link> :""
|
|
||||||
}
|
|
||||||
{ operate ?
|
|
||||||
<Popconfirm title={"确定要删除此流水线?"} onConfirm={()=>deleteFunc(item.id)} okText="确定" cancelText={"取消"}>
|
|
||||||
<a className="mr10 color-grey-6"><i className="iconfont icon-lajitong font-13 mr3"></i>删除</a>
|
|
||||||
</Popconfirm>:""
|
|
||||||
}
|
|
||||||
<Link to={`/projects/${owner}/${projectsId}/devops/list/${item.branch}`} className="color-grey-6"><i className="iconfont icon-yunhang font-13 mr3"></i>查看运行记录</Link>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
return(
|
|
||||||
<Table size="small" columns={columns} dataSource={list} rowKey={(row)=>row.id} pagination={false}></Table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default List;
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
import React , { useEffect , useState } from 'react';
|
|
||||||
import { Modal , Input , Select } from 'antd';
|
|
||||||
const { Option }= Select;
|
|
||||||
|
|
||||||
const EVENT = ["push","pull_request","tag","cron","custom","promote","rollback"]
|
|
||||||
function PipelineName({visible,onCancel,onOk,value ,branchList}){
|
|
||||||
const [ name , setName ] = useState(undefined);
|
|
||||||
const [ branchValue , setBranchValue ] = useState(undefined);
|
|
||||||
const [ eventValue , setEventValue ] = useState([EVENT[0]]);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(branchList && branchList.length>0){
|
|
||||||
setBranchValue(branchList[0].name);
|
|
||||||
}
|
|
||||||
},[branchList])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(value){
|
|
||||||
setName(value.pipeline_name);
|
|
||||||
setBranchValue(value.branch);
|
|
||||||
setEventValue(value.event);
|
|
||||||
}else{
|
|
||||||
setName(undefined);
|
|
||||||
}
|
|
||||||
},[value])
|
|
||||||
|
|
||||||
function onSure(){
|
|
||||||
onOk(name,value && value.id,branchValue,eventValue);
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<Modal
|
|
||||||
visible={visible}
|
|
||||||
title="流水线名称"
|
|
||||||
width={"500px"}
|
|
||||||
onCancel={onCancel}
|
|
||||||
onOk={onSure}
|
|
||||||
centered={true}
|
|
||||||
>
|
|
||||||
<div className="choosenList">
|
|
||||||
<span>流水线名称:</span>
|
|
||||||
<Input value={name} onChange={(e)=>setName(e.target.value)} placeholder="请输入名称" style={{width:"340px",margin:"6px 0px"}}/>
|
|
||||||
</div>
|
|
||||||
<div className="choosenList mt20">
|
|
||||||
<span>触发条件:</span>
|
|
||||||
<Select value={branchValue} style={{width:"150px"}} onChange={(e)=>setBranchValue(e)}>
|
|
||||||
{
|
|
||||||
branchList && branchList.length>0 && branchList.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<Option value={item.name} key={key}>{item.name}</Option>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</Select>
|
|
||||||
<Select mode="multiple" allowClear value={eventValue} style={{width:"180px",marginLeft:"10px"}} onChange={(e)=>{console.log(e);setEventValue(e)}}>
|
|
||||||
{
|
|
||||||
EVENT.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<Option value={item} key={key}>{item}</Option>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default PipelineName;
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
import React , { useEffect , useState } from 'react';
|
|
||||||
import { Button , Popconfirm } from 'antd';
|
|
||||||
import { Cancel } from '../../Component/layout';
|
|
||||||
import Item from './StageItem';
|
|
||||||
|
|
||||||
function Stage({
|
|
||||||
templates ,
|
|
||||||
datas ,
|
|
||||||
saveDatas ,
|
|
||||||
saveFunc ,
|
|
||||||
stepName ,
|
|
||||||
deleteStep ,
|
|
||||||
deleteFunc ,
|
|
||||||
deleteFlag
|
|
||||||
}){
|
|
||||||
const [ stepList , setStepList ] = useState(undefined);
|
|
||||||
const [ temp , setTemp ] = useState(undefined);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(templates && templates.length > 0){
|
|
||||||
setTemp(templates);
|
|
||||||
}
|
|
||||||
},[templates])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(datas){
|
|
||||||
if(datas.length > 0 && stepList !== datas){
|
|
||||||
setStepList(datas);
|
|
||||||
}else if(datas.length === 0){
|
|
||||||
let list = [];
|
|
||||||
setStepList(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},[datas])
|
|
||||||
|
|
||||||
// 添加步骤
|
|
||||||
function addFunc(){
|
|
||||||
let list = stepList;
|
|
||||||
let length = list ? list.length : 0;
|
|
||||||
let pre = temp && temp.length > 0 && temp[0];
|
|
||||||
let c = pre && pre.category;
|
|
||||||
let child = pre && pre.templates && pre.templates.length > 0 && pre.templates[0];
|
|
||||||
let step =
|
|
||||||
{
|
|
||||||
"category":c,
|
|
||||||
"step_name": stepName+`${length + 1}`,
|
|
||||||
"show_index": length + 1,
|
|
||||||
"content":child.content,
|
|
||||||
"template_id":child.id,
|
|
||||||
"hide":false
|
|
||||||
}
|
|
||||||
list.push(step);
|
|
||||||
saveDatas(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将修改的对应项保存在数组中
|
|
||||||
function saveItems(content,id,cate,key){
|
|
||||||
let item = stepList;
|
|
||||||
item[key].content = content;
|
|
||||||
item[key].template_id = id;
|
|
||||||
item[key].category = cate;
|
|
||||||
saveDatas([...item]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function slideItems(key,hide){
|
|
||||||
let item = stepList;
|
|
||||||
item[key].hide = !hide;
|
|
||||||
setStepList([...item]);
|
|
||||||
saveDatas(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteItem(id,key){
|
|
||||||
deleteStep(id,key);
|
|
||||||
}
|
|
||||||
// 点击下一步时
|
|
||||||
function nextStep(btn){
|
|
||||||
saveFunc(undefined,undefined,undefined,undefined,btn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
stepList && stepList.length > 0 && stepList.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<Item item={item} templates={temp} k={key} saveItems={saveItems} slideItems={slideItems} deleteStep={deleteItem}/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
<a className="addStageBtn" onClick={addFunc}>+ 添加步骤</a>
|
|
||||||
<div className="mt20">
|
|
||||||
<Button type="primary" onClick={()=>nextStep("last")}>上一步</Button>
|
|
||||||
<Button className="ml20" type="primary" onClick={()=>nextStep("next")}>下一步</Button>
|
|
||||||
{!deleteFlag &&
|
|
||||||
<Popconfirm title={'确定要删除当前阶段吗'} okText="是" cancelText="否" onConfirm={deleteFunc}>
|
|
||||||
<Cancel className="ml20">删除</Cancel>
|
|
||||||
</Popconfirm>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Stage;
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { FlexAJ } from '../../Component/layout';
|
|
||||||
import Editors from './Editors';
|
|
||||||
import Choosen from './Choosen';
|
|
||||||
import { Popconfirm } from 'antd';
|
|
||||||
|
|
||||||
|
|
||||||
function StageItem({item, templates,saveItems,k, slideItems , deleteStep}){
|
|
||||||
|
|
||||||
function onChangevalue(value){
|
|
||||||
saveItems(value,item.template_id,item.category,k);
|
|
||||||
}
|
|
||||||
// 选择模板
|
|
||||||
function chooseFunc(content,id,cate){
|
|
||||||
saveItems(content,id,cate,k);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div className="stepsItem">
|
|
||||||
<FlexAJ className="stepsHead">
|
|
||||||
<span>{item.step_name}</span>
|
|
||||||
<span className="color-grey-9">
|
|
||||||
<Popconfirm
|
|
||||||
title={"确定要删除这个步骤吗?"}
|
|
||||||
okText="是"
|
|
||||||
cancelText="否"
|
|
||||||
onConfirm={() => deleteStep(item.id,k)}
|
|
||||||
><a>
|
|
||||||
<i className="iconfont icon-lajitong1 font-14"></i></a>
|
|
||||||
</Popconfirm>
|
|
||||||
<a onClick={()=>slideItems(k,item.hide)}><i className={ (!item.hide || item.hide === false) ? "iconfont icon-sanjiaoxing-down font-14" :"iconfont icon-triangle font-14"}></i></a>
|
|
||||||
</span>
|
|
||||||
</FlexAJ>
|
|
||||||
<div className={(!item.hide || item.hide === false) ? "stepsBody active" : "stepsBody"}>
|
|
||||||
<Choosen chooseFunc={chooseFunc} category={item.category} templateId={item.template_id} temp={templates}/>
|
|
||||||
<Editors value={item.content} onChange={onChangevalue} theme="vs-dark" height="270px" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default StageItem;
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
import React ,{useEffect , useState} from 'react';
|
|
||||||
import { Button } from 'antd';
|
|
||||||
import Editors from './Editors';
|
|
||||||
|
|
||||||
function Sure({datas , name , saveFunc , sureSubmit , loading}){
|
|
||||||
const [ value , setValue ] = useState(undefined);
|
|
||||||
useEffect(()=>{
|
|
||||||
if(datas && datas.content){
|
|
||||||
setValue(datas.content)
|
|
||||||
}
|
|
||||||
},[datas]);
|
|
||||||
|
|
||||||
function sure(){
|
|
||||||
sureSubmit();
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<div style={{padding:"0px 15px 15px 15px"}}>
|
|
||||||
工作流名称:{name}
|
|
||||||
</div>
|
|
||||||
<div className="editorBody" style={{marginTop:"0px"}}>
|
|
||||||
<Editors value={value} theme={"vs-grey"} height={"600px"} visible/>
|
|
||||||
</div>
|
|
||||||
<div className="mt20">
|
|
||||||
<Button type={"primary"} onClick={()=>saveFunc(undefined,undefined,undefined,undefined,"last")}>上一步</Button>
|
|
||||||
{ value && <Button type={"primary"} loading={loading} className="ml20" onClick={sure}>确定提交</Button> }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Sure;
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { AlignCenterBetween , Blueline , FlexAJ } from '../../Component/layout';
|
|
||||||
|
|
||||||
|
|
||||||
function head({manager , parameter}){
|
|
||||||
return(
|
|
||||||
<AlignCenterBetween>
|
|
||||||
<span className="font-20">工作流配置</span>
|
|
||||||
<FlexAJ>
|
|
||||||
{
|
|
||||||
parameter && <Blueline onClick={parameter}>参数管理</Blueline>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
manager && <Blueline style={{marginLeft:"20px"}} onClick={manager}>模板管理</Blueline>
|
|
||||||
}
|
|
||||||
<a href={`https://forum.trustie.net/forums/3111/detail`} target="_blank" className="color-grey-6 ml20"><i className="iconfont icon-tishi1 font-14 mr3"></i>模板使用说明</a>
|
|
||||||
</FlexAJ>
|
|
||||||
</AlignCenterBetween>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default head;
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import MenusRename from './menusRename';
|
|
||||||
import MenusAdd from './menusAdd';
|
|
||||||
|
|
||||||
const typeIcon = {
|
|
||||||
init:"icon-initialize",build:"icon-structure",deploy:"icon-arrange",customize:"icon-newStage",confirm:'icon-sure'
|
|
||||||
}
|
|
||||||
|
|
||||||
function Menus({step,changeStep, menuList , renameFunc , checkDatas , addFunc }){
|
|
||||||
|
|
||||||
function InitActive(key,stage_type,stage_id,stage_name){
|
|
||||||
changeStep(key,stage_type,stage_id,stage_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增阶段
|
|
||||||
function getName(name,index){
|
|
||||||
addFunc && addFunc(name,index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<ul className="menus">
|
|
||||||
{
|
|
||||||
menuList && menuList.length > 0 && menuList.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<React.Fragment key={item.id} >
|
|
||||||
<li onClick={()=>InitActive(item.show_index,item.stage_type,item.id,item.stage_name)} className={item.show_index === step ?"active":""}>
|
|
||||||
<i className={`iconfont ${typeIcon[`${item.stage_type}`]}`}></i>
|
|
||||||
<MenusRename renameFunc={renameFunc} id={item.id} name={item.stage_name} edit={item.stage_type !== "init" && item.stage_type !== "confirm"}/>
|
|
||||||
</li>
|
|
||||||
{ key !== (menuList.length-1) && menuList.length < 7 ?
|
|
||||||
<MenusAdd checkDatas={checkDatas} k={key+2} getName={getName}/>:""
|
|
||||||
}
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Menus;
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import React , { useState , useEffect } from 'react';
|
|
||||||
import { Input } from 'antd';
|
|
||||||
|
|
||||||
function menusAdd ({ getName , checkDatas , k }){
|
|
||||||
const [ show, setShow ] = useState(false);
|
|
||||||
const [ value , setValue ] = useState(undefined);
|
|
||||||
const [ index , setIndex ] = useState(undefined);
|
|
||||||
const [ref, setRef ] = useState(undefined);
|
|
||||||
const [put, setPut ] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (put && ref) {
|
|
||||||
ref.focus();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
useEffect(() => {
|
|
||||||
if (k) {
|
|
||||||
setIndex(k);
|
|
||||||
}
|
|
||||||
},[k])
|
|
||||||
|
|
||||||
|
|
||||||
function blurInput(){
|
|
||||||
if(value){
|
|
||||||
getName(value , index);
|
|
||||||
}
|
|
||||||
setValue(undefined);
|
|
||||||
setShow(false);
|
|
||||||
setPut(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showInput(){
|
|
||||||
let c = checkDatas();
|
|
||||||
if(c || c === "" ){
|
|
||||||
setShow(true);
|
|
||||||
setPut(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<li className="menuAdd">
|
|
||||||
{ !show && <i className="iconfont icon-tianjia" onClick={showInput}></i> }
|
|
||||||
<Input
|
|
||||||
ref={(el) => setRef(el)}
|
|
||||||
size={"small"}
|
|
||||||
maxLength={8}
|
|
||||||
style={{width:"75px",display : `${show?"block":'none'}`}}
|
|
||||||
placeholder="新阶段名称"
|
|
||||||
value={value}
|
|
||||||
onChange={(e)=>setValue(e.target.value)} onBlur={blurInput}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default menusAdd;
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
import React , { useEffect , useState } from 'react';
|
|
||||||
import { Input } from 'antd';
|
|
||||||
|
|
||||||
function menusRename({ name , edit , id , renameFunc }){
|
|
||||||
const [ n , setN ] = useState(undefined);
|
|
||||||
const [ show , setShow ] = useState(false);
|
|
||||||
const [ref, setRef ] = useState(undefined);
|
|
||||||
const [put, setPut ] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (edit && ref) {
|
|
||||||
ref.focus();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(name){
|
|
||||||
setN(name)
|
|
||||||
}
|
|
||||||
},[name])
|
|
||||||
|
|
||||||
// 显示input框编辑
|
|
||||||
function changeShow(e){
|
|
||||||
e.stopPropagation();
|
|
||||||
setShow(true);
|
|
||||||
setPut(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function blurInput(e){
|
|
||||||
renameFunc(e.target.value,id);
|
|
||||||
setPut(false);
|
|
||||||
setShow(false);
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<div className="aboutEdit">
|
|
||||||
<span className="operateName">
|
|
||||||
{ !show && n }
|
|
||||||
<Input
|
|
||||||
ref={(el) => setRef(el)}
|
|
||||||
value={n}
|
|
||||||
size="small"
|
|
||||||
maxLength={8}
|
|
||||||
onClick={(e)=>e.stopPropagation()}
|
|
||||||
onBlur={blurInput}
|
|
||||||
style={{width:"75px",display : `${show?"block":'none'}`}}
|
|
||||||
onChange={(e)=>setN(e.target.value)}
|
|
||||||
/>
|
|
||||||
{ !show && edit && <i className="iconfont icon-editUnder font-16 color-grey-9" onClick={changeShow}></i> }
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default menusRename
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Modal ,Button } from 'antd';
|
|
||||||
|
|
||||||
const Div = styled.div`{
|
|
||||||
height:130px;
|
|
||||||
line-height:130px;
|
|
||||||
text-align:center;
|
|
||||||
color:#333;
|
|
||||||
}`
|
|
||||||
export default (({visible , closeFunc , sureFunc})=>{
|
|
||||||
return(
|
|
||||||
<Modal
|
|
||||||
title="提示"
|
|
||||||
visible={visible}
|
|
||||||
closable={true}
|
|
||||||
footer={
|
|
||||||
<div>
|
|
||||||
<Button onClick={()=>closeFunc(false)}>取消</Button>
|
|
||||||
<Button type={"primary"} onClick={()=>sureFunc()} style={{marginLeft:"20px"}}>确定</Button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
onCancel={()=>closeFunc(false)}
|
|
||||||
>
|
|
||||||
<Div>工作流配置完成,您的DevOps之旅即刻开启!</Div>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
import React , { useEffect } from 'react';
|
|
||||||
import { WhiteBack } from '../Component/layout';
|
|
||||||
import './ops.scss';
|
|
||||||
|
|
||||||
import { Route, Switch } from 'react-router-dom';
|
|
||||||
import Loadable from 'react-loadable';
|
|
||||||
import Loading from '../../Loading';
|
|
||||||
|
|
||||||
const About = Loadable({
|
|
||||||
loader: () => import('./About'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
const New = Loadable({
|
|
||||||
loader: () => import('./disposePipeline'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
const Dispose = Loadable({
|
|
||||||
loader: () => import('./Dispose'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
const Stucture = Loadable({
|
|
||||||
loader: () => import('./Structure'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
const Mould = Loadable({
|
|
||||||
loader: () => import('./Mould'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
const Params = Loadable({
|
|
||||||
loader: () => import('./Manage/Params'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
|
|
||||||
export default ((props)=>{
|
|
||||||
|
|
||||||
return(
|
|
||||||
<WhiteBack className="opsPanel">
|
|
||||||
<Switch {...props}>
|
|
||||||
<Route path="/projects/:owner/:projectsId/devops/dispose/:disposeId"
|
|
||||||
render={
|
|
||||||
(p) => (<New {...props} {...p}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
<Route path="/projects/:owner/:projectsId/devops/params"
|
|
||||||
render={
|
|
||||||
(p) => (<Params {...props} {...p}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
<Route path="/projects/:owner/:projectsId/devops/mould"
|
|
||||||
render={
|
|
||||||
(p) => (<Mould {...props} {...p}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
<Route path="/projects/:owner/:projectsId/devops/dispose/new"
|
|
||||||
render={
|
|
||||||
(p) => (<New {...props} {...p}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
<Route path="/projects/:owner/:projectsId/devops/dispose"
|
|
||||||
render={
|
|
||||||
(p) => (<Dispose {...props} {...p}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
<Route path="/projects/:owner/:projectsId/devops/list/:branch"
|
|
||||||
render={
|
|
||||||
(p) => (<Stucture {...props} {...p}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
<Route path="/projects/:owner/:projectsId/devops"
|
|
||||||
render={
|
|
||||||
(p) => (<About {...props} {...p}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
</Switch>
|
|
||||||
</WhiteBack>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
import React , { useEffect , useState } from 'react';
|
|
||||||
import { Banner } from '../Component/layout';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import Dispost from './Dispose';
|
|
||||||
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const Div = styled.div`{
|
|
||||||
padding:24px 30px;
|
|
||||||
}`;
|
|
||||||
export default ((props)=>{
|
|
||||||
const [ permission , setPermission ] = useState("");
|
|
||||||
|
|
||||||
const owner = props.match.params.owner;
|
|
||||||
|
|
||||||
const projectDetail = props.projectDetail;
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(projectDetail){
|
|
||||||
setPermission(props.projectDetail.permission);
|
|
||||||
}
|
|
||||||
},[projectDetail])
|
|
||||||
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div className="disposePanel">
|
|
||||||
<Banner>
|
|
||||||
{ permission !=="Reporter" && <Link to={`/projects/${owner}/${props.match.params.projectsId}/devops/dispose`}>工作流配置</Link>}
|
|
||||||
</Banner>
|
|
||||||
<Div>
|
|
||||||
<Dispost {...props}/>
|
|
||||||
</Div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
import React , { useEffect , useState , useRef } from 'react';
|
|
||||||
import { Banner , Blueback , FlexAJ , AlignCenter } from '../../Component/layout';
|
|
||||||
import { Input , Table , Popconfirm , Pagination } from 'antd';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import New from './ParamsNew';
|
|
||||||
import "./manage.scss";
|
|
||||||
import axios from 'axios';
|
|
||||||
import { result } from 'lodash';
|
|
||||||
|
|
||||||
const Div = styled.div`{
|
|
||||||
padding:24px 30px;
|
|
||||||
min-height:420px;
|
|
||||||
}`;
|
|
||||||
function Params(props){
|
|
||||||
const [ list ,setList ] = useState(undefined);
|
|
||||||
const [ editList ,setEditList ] = useState(undefined);
|
|
||||||
const [ visible ,setVisible ] = useState(false);
|
|
||||||
|
|
||||||
let projectsId = props.match.params.projectsId;
|
|
||||||
let owner = props.match.params.owner;
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
Init()
|
|
||||||
},[])
|
|
||||||
|
|
||||||
function Init(){
|
|
||||||
const url = `/ci/secrets.json`;
|
|
||||||
axios.get(url,{
|
|
||||||
params:{
|
|
||||||
owner,repo:projectsId
|
|
||||||
}
|
|
||||||
}).then(result=>{
|
|
||||||
if(result){
|
|
||||||
setList(result.data);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns=[
|
|
||||||
{
|
|
||||||
title:"参数名",
|
|
||||||
dataIndex:"name",
|
|
||||||
key:1,
|
|
||||||
ellipsis:true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"操作",
|
|
||||||
dataIndex:"operation",
|
|
||||||
key:4,
|
|
||||||
render:(txt,item)=>{
|
|
||||||
return(
|
|
||||||
<React.Fragment>
|
|
||||||
<a className="mr10 color-grey-6" onClick={()=>editMouldFunc(item)}><i className="iconfont icon-zaibianji font-13 mr3"></i>编辑</a>
|
|
||||||
<Popconfirm title={"确定要删除此模板?"} onConfirm={()=>deleteMouldFunc(item.id,item.name)} okText="确定" cancelText={"取消"}>
|
|
||||||
<a className="mr10 color-grey-6"><i className="iconfont icon-lajitong font-13 mr3"></i>删除</a>
|
|
||||||
</Popconfirm>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// 编辑
|
|
||||||
function editMouldFunc(item){
|
|
||||||
setEditList(item);
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
function deleteMouldFunc(id,name){
|
|
||||||
if(id && name){
|
|
||||||
const url = `/ci/secrets/${id}.json`;
|
|
||||||
axios.delete(url,{
|
|
||||||
params:{owner,repo:projectsId,name}
|
|
||||||
}).then(result=>{
|
|
||||||
if(result){
|
|
||||||
Init();
|
|
||||||
props.showNotification(`参数删除成功!`);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function successFunc(values,id){
|
|
||||||
const url = `/ci/secrets.json?owner=${owner}&repo=${projectsId}`;
|
|
||||||
axios.post(url,{
|
|
||||||
...values,id
|
|
||||||
}).then(result=>{
|
|
||||||
if(result){
|
|
||||||
props.showNotification(`${id ? '参数编辑':"新增参数"}成功!`);
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
function CancelFunc(){
|
|
||||||
setVisible(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<New visble={visible} successFunc={successFunc} CancelFunc={CancelFunc} editList={editList}/>
|
|
||||||
<Banner>
|
|
||||||
<FlexAJ>
|
|
||||||
<span className="font-18">工作流 - 参数管理</span>
|
|
||||||
<Link to={`/projects/${owner}/${projectsId}/devops/dispose`} className="font-14 color-grey-9 ml20">返回</Link>
|
|
||||||
</FlexAJ>
|
|
||||||
</Banner>
|
|
||||||
<Div className="disposeList">
|
|
||||||
<div style={{textAlign:"right"}}>
|
|
||||||
<Blueback onClick={()=>setVisible(true)}>新建</Blueback>
|
|
||||||
</div>
|
|
||||||
<Table className="mt20" size="small" columns={columns} dataSource={list} rowKey={(row)=>row.id} pagination={false}></Table>
|
|
||||||
</Div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Params;
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
import React , { useEffect , useState , useRef , forwardRef } from 'react';
|
|
||||||
import { Modal , Input , Form } from 'antd';
|
|
||||||
|
|
||||||
const { TextArea } = Input;
|
|
||||||
|
|
||||||
function ParamsNew({ form , visble,successFunc,CancelFunc ,editList }){
|
|
||||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
|
||||||
const layout = {
|
|
||||||
labelCol: { span: 5 },
|
|
||||||
wrapperCol: { span: 18 },
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(editList && editList.id){
|
|
||||||
setFieldsValue({
|
|
||||||
name:editList.name,
|
|
||||||
data:editList.data
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
setFieldsValue({
|
|
||||||
name:undefined,
|
|
||||||
data:undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},[editList])
|
|
||||||
|
|
||||||
// 确定
|
|
||||||
function onConfirmFunc(){
|
|
||||||
validateFields((error,values)=>{
|
|
||||||
if(!error){
|
|
||||||
successFunc(values,editList && editList.id);
|
|
||||||
onCancelFunc();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function onCancelFunc(){
|
|
||||||
setFieldsValue({
|
|
||||||
name:undefined,
|
|
||||||
data:undefined
|
|
||||||
})
|
|
||||||
CancelFunc();
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<Modal
|
|
||||||
visible={visble}
|
|
||||||
okText={"确定"}
|
|
||||||
cancelText={"取消"}
|
|
||||||
onCancel={onCancelFunc}
|
|
||||||
onOk={onConfirmFunc}
|
|
||||||
title={"新建"}
|
|
||||||
closable={false}
|
|
||||||
width="500px"
|
|
||||||
>
|
|
||||||
<Form {...layout}>
|
|
||||||
<Form.Item label="参数名称">
|
|
||||||
{getFieldDecorator("name",{
|
|
||||||
rules:[{required:true,message:"请输入参数名称"}]
|
|
||||||
})(
|
|
||||||
<Input placeholder="请输入参数名称" width="220px"/>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item label="参数值">
|
|
||||||
{getFieldDecorator("data",{
|
|
||||||
rules:[{required:true,message:"请输入参数值"}]
|
|
||||||
})(
|
|
||||||
<TextArea placeholder="请输入参数值" width="220px" autoSize={{ minRows: 4, maxRows: 4 }}/>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Form.create()(forwardRef(ParamsNew));
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
.ant-form-explain{
|
|
||||||
position: absolute;
|
|
||||||
bottom: -15px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
import React , { useEffect , useState , useRef } from 'react';
|
|
||||||
import { Banner , Blueback , FlexAJ } from '../Component/layout';
|
|
||||||
import { Input , Table ,Pagination , Select , Popconfirm } from 'antd';
|
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import axios from 'axios';
|
|
||||||
import New from './MouldNew';
|
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
const Div = styled.div`{
|
|
||||||
padding:24px 30px;
|
|
||||||
min-height:420px;
|
|
||||||
}`;
|
|
||||||
const STAGE = [
|
|
||||||
{stage_name:"所有",stage_type:"all"},
|
|
||||||
{stage_name:"初始化",stage_type:"init"},
|
|
||||||
{stage_name:"编译构建",stage_type:"build"},
|
|
||||||
{stage_name:"部署",stage_type:"deploy"},
|
|
||||||
{stage_name:"其他",stage_type:"customize"}
|
|
||||||
]
|
|
||||||
const limit = 15;
|
|
||||||
function Mould(props){
|
|
||||||
const [ visible ,setVisible ] = useState(false);
|
|
||||||
const [ list ,setList ] = useState(undefined);
|
|
||||||
const [ page ,setPage ] = useState(1);
|
|
||||||
const [ totalCount ,setTotalCount ] = useState(0);
|
|
||||||
const [ stageType ,setStageType ] = useState("all");
|
|
||||||
const [ search ,setSearch ] = useState(undefined);
|
|
||||||
const childRef = useRef();
|
|
||||||
let projectsId = props.match.params.projectsId;
|
|
||||||
let owner = props.match.params.owner;
|
|
||||||
useEffect(()=>{
|
|
||||||
Init(page,stageType);
|
|
||||||
},[page,stageType])
|
|
||||||
|
|
||||||
function Init(page,stageType,searchvalue){
|
|
||||||
const url = `/ci/templates/list.json`;
|
|
||||||
axios.get(url,{
|
|
||||||
params:{
|
|
||||||
page,limit,stage_type:stageType,name:searchvalue
|
|
||||||
}
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setList(result.data.templates);
|
|
||||||
setTotalCount(result.data.total_count);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns=[
|
|
||||||
{
|
|
||||||
title:"名称",
|
|
||||||
dataIndex:"template_name",
|
|
||||||
key:1,
|
|
||||||
ellipsis:true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"所属阶段",
|
|
||||||
dataIndex:"stage_type",
|
|
||||||
key:2,
|
|
||||||
ellipsis:true,
|
|
||||||
render:(txt,item)=>{
|
|
||||||
let i = STAGE.filter(item=>item.stage_type === txt);
|
|
||||||
return i && i.length>0 && i[0].stage_name
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"模板类型",
|
|
||||||
dataIndex:"category",
|
|
||||||
key:3,
|
|
||||||
ellipsis:true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title:"操作",
|
|
||||||
dataIndex:"operation",
|
|
||||||
key:4,
|
|
||||||
ellipsis:true,
|
|
||||||
render:(txt,item)=>{
|
|
||||||
return(
|
|
||||||
<span>
|
|
||||||
<a className="mr10 color-grey-6" onClick={()=>editMouldFunc(item)}><i className="iconfont icon-zaibianji font-13 mr3"></i>编辑</a>
|
|
||||||
<Popconfirm title={"确定要删除此模板?"} onConfirm={()=>deleteMouldFunc(item.id)} okText="确定" cancelText={"取消"}>
|
|
||||||
<a className="mr10 color-grey-6"><i className="iconfont icon-lajitong font-13 mr3"></i>删除</a>
|
|
||||||
</Popconfirm>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// 编辑模板
|
|
||||||
function editMouldFunc(item){
|
|
||||||
if (childRef.current) {
|
|
||||||
childRef.current.setEditInfo(item);
|
|
||||||
}
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除模板
|
|
||||||
function deleteMouldFunc(id){
|
|
||||||
const url = `/ci/templates/${id}.json`;
|
|
||||||
axios.delete(url).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
props.showNotification("模板删除成功!");
|
|
||||||
Init(page,stageType,search);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function searchValue(){
|
|
||||||
Init(page,stageType,search);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newMouldFunc(){
|
|
||||||
if (childRef.current) {
|
|
||||||
childRef.current.setEditInfo(undefined);
|
|
||||||
}
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOk(){
|
|
||||||
Init(page,stageType);
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<New wrappedComponentRef={(f) => childRef.current = f} ref={childRef} visible={visible} onCancel={()=>setVisible(false)} onOk={onOk}></New>
|
|
||||||
<Banner>
|
|
||||||
<FlexAJ><span>工作流 - 模板管理</span><Link to={`/projects/${owner}/${projectsId}/devops/dispose`} className="font-14 color-grey-9">返回</Link></FlexAJ>
|
|
||||||
</Banner>
|
|
||||||
<Div className="disposeList">
|
|
||||||
<FlexAJ>
|
|
||||||
<Blueback onClick={newMouldFunc}>新建模板</Blueback>
|
|
||||||
<FlexAJ>
|
|
||||||
<span className="mr10">阶段:</span>
|
|
||||||
<Select onChange={e=>setStageType(e)} value={stageType} style={{width:"180px"}}>
|
|
||||||
{
|
|
||||||
STAGE.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<Option value={item.stage_type}>{item.stage_name}</Option>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</Select>
|
|
||||||
<Input placeholder="请输入模板名称" value={search} onChange={(e)=>setSearch(e.target.value)} allowClear style={{width:"160px",marginLeft:"15px"}}/>
|
|
||||||
<Blueback className="ml15" onClick={searchValue}>搜索</Blueback>
|
|
||||||
</FlexAJ>
|
|
||||||
</FlexAJ>
|
|
||||||
<Table className="mt20" size="small" columns={columns} dataSource={list} rowKey={(row)=>row.id} pagination={false}></Table>
|
|
||||||
{
|
|
||||||
totalCount > limit &&
|
|
||||||
<div className="mt20 pb20" style={{textAlign:'center'}}>
|
|
||||||
<Pagination simple current={page} pageSize={limit} total={totalCount} onChange={(page)=>setPage(page)}/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Mould;
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
import React , { useImperativeHandle , useState , forwardRef , useCallback } from 'react';
|
|
||||||
import { Form , Modal , Input , Select , Spin } from 'antd';
|
|
||||||
import Editor from './Dispose/Editors';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
const TYPE = ["Java","C","C++","Python","Go","Ruby","R","PHP",
|
|
||||||
"Perl","Node","Docker","Rust","Swift","Erlang","Other"]
|
|
||||||
|
|
||||||
function MouldNew({ form , visible , onCancel , onOk }, ref){
|
|
||||||
const [value , setValue ] = useState(undefined);
|
|
||||||
const [isSpin , setIsSpin ] = useState(false);
|
|
||||||
const [valueFlag , setValueFlag ] = useState(false);
|
|
||||||
const [ id , setId ] = useState(false);
|
|
||||||
const [ buildFlag , setBuildFlag ] = useState(false);
|
|
||||||
|
|
||||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
|
||||||
setEditInfo: (info) => {
|
|
||||||
if(info){
|
|
||||||
setFieldsValue({
|
|
||||||
...info
|
|
||||||
})
|
|
||||||
if(info.stage_type === "build"){
|
|
||||||
setBuildFlag(true);
|
|
||||||
setFieldsValue({
|
|
||||||
category:TYPE[0]
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
removeCate();
|
|
||||||
}
|
|
||||||
setValue(info.content);
|
|
||||||
setId(info.id);
|
|
||||||
}else{
|
|
||||||
removeCate();
|
|
||||||
clear();
|
|
||||||
setId(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
const helper = useCallback(
|
|
||||||
(label, name, rules, widget, className, isRequired,flag) => (
|
|
||||||
<Form.Item label={label} className={className}>
|
|
||||||
{getFieldDecorator(name, { rules, validateFirst: true , valuePropName:flag ? "checked":"value" })(widget)}
|
|
||||||
</Form.Item>
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
function changeContent(v){
|
|
||||||
if(v){
|
|
||||||
setValue(v);
|
|
||||||
setValueFlag(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function cancel(){
|
|
||||||
clear();
|
|
||||||
onCancel();
|
|
||||||
}
|
|
||||||
function sure(){
|
|
||||||
if(!value){
|
|
||||||
setValueFlag(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
validateFields((error,values)=>{
|
|
||||||
if(!error){
|
|
||||||
setIsSpin(true);
|
|
||||||
const url = `/ci/templates.json`;
|
|
||||||
axios.post(url,{
|
|
||||||
...values,id,content:value,category:buildFlag ? values.category:""
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setIsSpin(false);
|
|
||||||
cancel();
|
|
||||||
onOk();
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function clear(){
|
|
||||||
setFieldsValue({
|
|
||||||
stage_type:"init",
|
|
||||||
template_name:undefined,
|
|
||||||
category:"Java",
|
|
||||||
})
|
|
||||||
setValue("");
|
|
||||||
setValueFlag(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeStage(e){
|
|
||||||
if(e === "build"){
|
|
||||||
setBuildFlag(true);
|
|
||||||
setFieldsValue({
|
|
||||||
category:TYPE[0]
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
removeCate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeCate(){
|
|
||||||
setBuildFlag(false);
|
|
||||||
setFieldsValue({
|
|
||||||
category:""
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<Modal
|
|
||||||
visible={visible}
|
|
||||||
width="500px"
|
|
||||||
title={"新建/编辑模板"}
|
|
||||||
onCancel={cancel}
|
|
||||||
onOk={sure}
|
|
||||||
centered={true}
|
|
||||||
>
|
|
||||||
<Spin spinning={isSpin}>
|
|
||||||
<Form layout={"inline"}>
|
|
||||||
{helper(
|
|
||||||
"所属阶段",
|
|
||||||
"stage_type",
|
|
||||||
[{required:true,message:"请选择所属阶段"}],
|
|
||||||
<Select placeholder="请选择所属阶段" style={{width:"350px"}} onChange={(e)=>{changeStage(e)}}>
|
|
||||||
<Option value="init">初始化</Option>
|
|
||||||
<Option value="build">编译构建</Option>
|
|
||||||
<Option value="deploy">部署</Option>
|
|
||||||
<Option value="customize">其他</Option>
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
{helper(
|
|
||||||
"模板名称",
|
|
||||||
"template_name",
|
|
||||||
[{required:true,message:"请输入模板名称"}],
|
|
||||||
<Input placeholder="请输入模板名称" style={{width:"350px"}}/>
|
|
||||||
)}
|
|
||||||
{helper(
|
|
||||||
"模板分类",
|
|
||||||
"category",
|
|
||||||
[{required:buildFlag,message:"请选择模板分类"}],
|
|
||||||
<Select placeholder="请选择模板分类" style={{width:"350px"}}>
|
|
||||||
{
|
|
||||||
TYPE.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<Option value={item}>{item}</Option>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</Select>,buildFlag===true ? "" :"hide"
|
|
||||||
)}
|
|
||||||
<div style={{display:'flex',justifyContent:"flex-start"}}>
|
|
||||||
<span><span className="color-red">* </span>模板内容:</span>
|
|
||||||
<div>
|
|
||||||
<div className="editorPanel">
|
|
||||||
<Editor Numbers={"off"} width={"350px"} value={value} height="200px" theme="vs-grey" onChange={changeContent}/>
|
|
||||||
</div>
|
|
||||||
{ valueFlag && <span className="color-red">请输入模板内容</span>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Spin>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Form.create()(forwardRef(MouldNew));
|
|
||||||
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { FlexAJ, Blueline, AlignCenter } from "../Component/layout";
|
|
||||||
import styled from "styled-components";
|
|
||||||
import { Menu, Popconfirm } from "antd";
|
|
||||||
import { TagsLine } from "../Component/OpsStatus";
|
|
||||||
import { Time } from "../Utils/Time";
|
|
||||||
import { truncateCommitId } from "../common/util";
|
|
||||||
import { getUrl } from 'educoder';
|
|
||||||
|
|
||||||
const SubMenu = Menu.SubMenu;
|
|
||||||
const Img = styled.img`
|
|
||||||
{
|
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export default ({ data, repeatSet , chooseSteps }) => {
|
|
||||||
const [tamp, setTamp] = useState(undefined);
|
|
||||||
const [sha, setSha] = useState(undefined);
|
|
||||||
useEffect(() => {
|
|
||||||
if (data && data.started) {
|
|
||||||
let t = parseInt(data.started) * 1000;
|
|
||||||
let time = Time(t);
|
|
||||||
setTamp(time);
|
|
||||||
}
|
|
||||||
if (data && data.after) {
|
|
||||||
setSha(truncateCommitId(data.after));
|
|
||||||
}
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
function renderStatusBtn() {
|
|
||||||
let status = data && data.status;
|
|
||||||
let number = data && data.number;
|
|
||||||
if (status === "failure" || status === "error" || status === "success") {
|
|
||||||
return "";
|
|
||||||
}else if(status === "killed"){
|
|
||||||
return(
|
|
||||||
<Popconfirm
|
|
||||||
title="确认重新构建?"
|
|
||||||
onConfirm={(e) => repeatSet(e,'repeat',number)}
|
|
||||||
onCancel={(e)=>{e.stopPropagation()}}
|
|
||||||
cancelText="取消"
|
|
||||||
okText="确定"
|
|
||||||
>
|
|
||||||
<Blueline onClick={(e)=>{e.stopPropagation()}}>重新构建</Blueline>
|
|
||||||
</Popconfirm>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Popconfirm
|
|
||||||
title="确认撤销构建?"
|
|
||||||
onConfirm={(e) => repeatSet(e,'cancel',number)}
|
|
||||||
onCancel={(e)=>{e.stopPropagation()}}
|
|
||||||
cancelText="取消"
|
|
||||||
okText="确定"
|
|
||||||
>
|
|
||||||
<Blueline onClick={(e)=>{e.stopPropagation()}}>撤销构建</Blueline>
|
|
||||||
</Popconfirm>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickSub(e,stageN,stepN){
|
|
||||||
chooseSteps(stageN,stepN);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<FlexAJ className="leftheader">
|
|
||||||
<AlignCenter>
|
|
||||||
<Img src={getUrl(`/images/${data && data.author && data.author.image_url}`)} />
|
|
||||||
{data && data.started &&
|
|
||||||
<span className="nest">
|
|
||||||
开始时间:<span> {data.started}</span>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
data && data.duration_time &&
|
|
||||||
<span className="nest">
|
|
||||||
运行时间:<span>{data.duration_time}</span>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</AlignCenter>
|
|
||||||
{renderStatusBtn()}
|
|
||||||
</FlexAJ>
|
|
||||||
<div className="leftMainContent">
|
|
||||||
<AlignCenter className="contentBranch">
|
|
||||||
<i className="iconfont icon-fenzhi1"></i>
|
|
||||||
<span>分支:</span>
|
|
||||||
<span className="branchname">{data && data.branch_target}</span>
|
|
||||||
<span className="branchsha">{data && truncateCommitId(data.build_after_sha)}</span>
|
|
||||||
</AlignCenter>
|
|
||||||
</div>
|
|
||||||
<Menu mode="inline" className="leftMenu" defaultOpenKeys={[`0`]} defaultSelectedKeys={[`0`]}>
|
|
||||||
{data && data.stages ? data.stages.map((item, key) => {
|
|
||||||
return item.steps && item.steps.length > 0 ?
|
|
||||||
<SubMenu
|
|
||||||
title={
|
|
||||||
<div>
|
|
||||||
<i className="iconfont icon-gongzuoliu font-14 mr4"></i>
|
|
||||||
<span>{item.name}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
key={`${key}`}
|
|
||||||
>
|
|
||||||
{item.steps.map((i, k) => {
|
|
||||||
return (
|
|
||||||
<Menu.Item key={`${k}`} onClick={(e)=>clickSub(e,item.number,i.id)}>
|
|
||||||
<FlexAJ>
|
|
||||||
<span>
|
|
||||||
{i.name} {i.status ? TagsLine(i.status) : ""}
|
|
||||||
</span>
|
|
||||||
<span>{i.duration_time}</span>
|
|
||||||
</FlexAJ>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</SubMenu>
|
|
||||||
: "";
|
|
||||||
})
|
|
||||||
: ""}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { Spin , Menu } from "antd";
|
|
||||||
import { FlexAJ, AlignCenter } from "../Component/layout";
|
|
||||||
import axios from "axios";
|
|
||||||
import CodeSSH from './ssh/Index';
|
|
||||||
|
|
||||||
export default ({
|
|
||||||
data,
|
|
||||||
stepN,
|
|
||||||
stageN,
|
|
||||||
projectId,
|
|
||||||
owner,
|
|
||||||
opsId,
|
|
||||||
rightSpin,
|
|
||||||
}) => {
|
|
||||||
const [coders, setCoders] = useState(undefined);
|
|
||||||
const [empty, setEmpty] = useState(false);
|
|
||||||
const [spining, setSpining] = useState(true);
|
|
||||||
const [stage, setStage] = useState(undefined);
|
|
||||||
const [step, setStep] = useState(undefined);
|
|
||||||
const [nav, setNav] = useState("0");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setSpining(rightSpin);
|
|
||||||
}, [rightSpin]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (data) {
|
|
||||||
let stages = data.stages;
|
|
||||||
if (stages && stages.length > 0) {
|
|
||||||
let pre = stageN
|
|
||||||
? stages.filter((item) => item.number === stageN)[0]
|
|
||||||
: stages[0];
|
|
||||||
setStage(pre);
|
|
||||||
let p = pre && pre.steps;
|
|
||||||
let sub = stepN
|
|
||||||
? p && p.length > 0 && p.filter((item) => item.id === stepN)[0]
|
|
||||||
: p[0];
|
|
||||||
|
|
||||||
setStep(sub);
|
|
||||||
setNav("0");
|
|
||||||
if (sub && sub.status !== "skipped") {
|
|
||||||
getStep(pre.number, sub.number);
|
|
||||||
}
|
|
||||||
// 如果状态是skipped就不用去调用接口查询对应的out信息了
|
|
||||||
if(sub.status === "skipped"){
|
|
||||||
setCoders(undefined);
|
|
||||||
setEmpty(true);
|
|
||||||
setSpining(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setSpining(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [data, stageN, stepN]);
|
|
||||||
|
|
||||||
function getStep(stageN, stepN) {
|
|
||||||
if (stageN && stepN) {
|
|
||||||
const url = `/${owner}/${projectId}/builds/${opsId}/logs/${stageN}/${stepN}.json`;
|
|
||||||
axios.get(url).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
setCoders(result.data);
|
|
||||||
setSpining(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
{/* <Menu className="devopsNav" onClick={(e)=>{setNav(e.key)}} selectedKeys={[nav]} mode="horizontal">
|
|
||||||
<Menu.Item key={'0'} value="0">开发流水线</Menu.Item>
|
|
||||||
<Menu.Item key={'1'} value="1">命令行</Menu.Item>
|
|
||||||
</Menu> */}
|
|
||||||
{
|
|
||||||
nav === "0" &&
|
|
||||||
<Spin spinning={spining}>
|
|
||||||
<div className="rightMainContent">
|
|
||||||
{data && data.status !== "error" ? (
|
|
||||||
<div>
|
|
||||||
<FlexAJ className="items">
|
|
||||||
<span>{step && step.name}</span>
|
|
||||||
<AlignCenter>
|
|
||||||
{step && step.duration_time}
|
|
||||||
<i className="iconfont icon-sanjiaoxing-down"></i>
|
|
||||||
</AlignCenter>
|
|
||||||
</FlexAJ>
|
|
||||||
<div>
|
|
||||||
{coders && coders.length > 0 ? (
|
|
||||||
coders.map((item, key) => {
|
|
||||||
return (
|
|
||||||
<div className="opsDetailOut">
|
|
||||||
<span>{key + 1}</span>
|
|
||||||
<p>{item.out}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : empty ? (
|
|
||||||
<div className="opsDetailOut">
|
|
||||||
<span>1</span>
|
|
||||||
<p>
|
|
||||||
{stage && stage.name} – {step && step.name}: Skipped
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
""
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div style={{ color: "red" }}>error:{data && data.error}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
nav === "1" && <CodeSSH />
|
|
||||||
}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { Radio , Button } from 'antd';
|
|
||||||
|
|
||||||
function ServiceModal({sureModal}){
|
|
||||||
const [ type , setType ] = useState(1);
|
|
||||||
|
|
||||||
function changeType(e){
|
|
||||||
setType(e.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div className="mt30" style={{textAlign:"center"}}>
|
|
||||||
<Radio.Group value={type} onChange={changeType}>
|
|
||||||
<Radio value={1}>自有服务器</Radio>
|
|
||||||
<Radio value={2}>Trustie服务器</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
<p className="mt30"><Button type="primary" onClick={()=>sureModal(type)}>下一步</Button></p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default ServiceModal;
|
|
||||||
|
|
@ -1,331 +0,0 @@
|
||||||
import React, { useState, useEffect , useImperativeHandle ,forwardRef } from "react";
|
|
||||||
import { FlexAJ, AlignCenter , Banner } from "../Component/layout";
|
|
||||||
import { Table, Pagination, Popconfirm } from "antd";
|
|
||||||
import { truncateCommitId } from "../common/util";
|
|
||||||
import {getUrl} from 'educoder';
|
|
||||||
import axios from "axios";
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const Div = styled.div`{
|
|
||||||
padding:24px 30px;
|
|
||||||
}`;
|
|
||||||
const STATUS = [
|
|
||||||
{ name: "所有"},
|
|
||||||
{ name: "运行中", value: "running" },
|
|
||||||
{ name: "已撤销", value: "killed" },
|
|
||||||
{ name: "构建失败", value: "failure" },
|
|
||||||
{ name: "已完成", value: "success" },
|
|
||||||
];
|
|
||||||
const LIMIT = 15;
|
|
||||||
function Structure(props,ref){
|
|
||||||
const [status, setStatus] = useState(undefined);
|
|
||||||
const [page, setPage] = useState(1);
|
|
||||||
const [total, setTotal] = useState(0);
|
|
||||||
const [data, setData] = useState(undefined);
|
|
||||||
const [tableLoading, setTableLoading] = useState(true);
|
|
||||||
|
|
||||||
let projectsId = props.match.params.projectsId;
|
|
||||||
let owner = props.match.params.owner;
|
|
||||||
let branch = props.match.params.branch;
|
|
||||||
const permission = props.projectDetail && props.projectDetail.permission;
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
|
||||||
changeVal: () => {
|
|
||||||
setTableLoading(true);
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (projectsId) {
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
}, [page]);
|
|
||||||
|
|
||||||
let current_user = props.current_user;
|
|
||||||
function Init(status) {
|
|
||||||
const url = `/${owner}/${projectsId}/builds.json`;
|
|
||||||
axios.get(url,{
|
|
||||||
params:{
|
|
||||||
search:status,
|
|
||||||
page,limit:LIMIT,branch
|
|
||||||
}
|
|
||||||
}).then((result) => {
|
|
||||||
if (result && result.data) {
|
|
||||||
let list = result.data.builds && result.data.builds.map((item, key) => {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
author:item.author && item.author.name,
|
|
||||||
image_url:item.author && item.author.image_url,
|
|
||||||
message: {
|
|
||||||
branch: item.branch_target,
|
|
||||||
message: item.message,
|
|
||||||
sha: truncateCommitId(item.build_after_sha),
|
|
||||||
},
|
|
||||||
started: item.started || "--"
|
|
||||||
};
|
|
||||||
});
|
|
||||||
setTotal(result.data.total_count);
|
|
||||||
setData(list);
|
|
||||||
setTableLoading(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function ChangeStatus(value) {
|
|
||||||
setStatus(value);
|
|
||||||
Init(value);
|
|
||||||
}
|
|
||||||
// 切换分页
|
|
||||||
function ChangePage(page) {
|
|
||||||
setPage(page);
|
|
||||||
}
|
|
||||||
function renderStatus() {
|
|
||||||
return (
|
|
||||||
<ul className="listNav">
|
|
||||||
{STATUS.map((item, key) => {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
onClick={() => ChangeStatus(item.value)}
|
|
||||||
className={status === item.value ? "active" : ""}
|
|
||||||
>
|
|
||||||
{item.name}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
function renderStatusBtn(status, number) {
|
|
||||||
if (status === "error" || status === "success") {
|
|
||||||
return "";
|
|
||||||
}else if(status === "killed" || status === "failure"){
|
|
||||||
return(
|
|
||||||
<Popconfirm
|
|
||||||
title="确认重新构建?"
|
|
||||||
onConfirm={(e) => repeatSet(e,number)}
|
|
||||||
onCancel={(e)=>{e.stopPropagation()}}
|
|
||||||
cancelText="取消"
|
|
||||||
okText="确定"
|
|
||||||
>
|
|
||||||
<a className="color-blue" onClick={(e)=>{e.stopPropagation()}}>重新构建</a>
|
|
||||||
</Popconfirm>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Popconfirm
|
|
||||||
title="确认撤销构建?"
|
|
||||||
onConfirm={(e) => cancelSet(e,number)}
|
|
||||||
onCancel={(e)=>{e.stopPropagation()}}
|
|
||||||
cancelText="取消"
|
|
||||||
okText="确定"
|
|
||||||
>
|
|
||||||
<a className="color-red" onClick={(e)=>{e.stopPropagation()}}>撤销构建</a>
|
|
||||||
</Popconfirm>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 重新构建
|
|
||||||
function repeatSet(e,number) {
|
|
||||||
e.stopPropagation();
|
|
||||||
setTableLoading(true);
|
|
||||||
const url = `/${owner}/${projectsId}/builds/${number}/restart.json`;
|
|
||||||
axios.post(url).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
props.showNotification("工作流正在重新构建!");
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 撤销构建
|
|
||||||
function cancelSet(e,number) {
|
|
||||||
e.stopPropagation();
|
|
||||||
setTableLoading(true);
|
|
||||||
const url = `/${owner}/${projectsId}/builds/${number}/stop.json`;
|
|
||||||
axios.delete(url).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
props.showNotification("撤销构建成功!");
|
|
||||||
Init(projectsId);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderTableStatus(status) {
|
|
||||||
switch (status) {
|
|
||||||
case "running":
|
|
||||||
return (
|
|
||||||
<span className="statusTag running">
|
|
||||||
<i className="iconfont icon-yunhangzhong"></i>运行中
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case "failure": case 'error':
|
|
||||||
return (
|
|
||||||
<span className="statusTag failed">
|
|
||||||
<i className="iconfont icon-weitongguo"></i>未通过
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case "success":
|
|
||||||
return (
|
|
||||||
<span className="statusTag pass">
|
|
||||||
<i className="iconfont icon-yitongguo"></i>已通过
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case 'killed':
|
|
||||||
return (
|
|
||||||
<span className="statusTag killed">
|
|
||||||
<i className="iconfont icon-weitongguo"></i>已撤销
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<span className="statusTag Preparing">
|
|
||||||
<i className="iconfont icon-zhunbeizhong"></i>准备中
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickRows(event,e){
|
|
||||||
props.history.push(`/projects/${owner}/${projectsId}/devops/${e.number}/detail`);
|
|
||||||
}
|
|
||||||
const column = [
|
|
||||||
{
|
|
||||||
title: "序号",
|
|
||||||
dataIndex: "number",
|
|
||||||
key: "number",
|
|
||||||
width: "8%",
|
|
||||||
render: ( value, item, key) => {
|
|
||||||
return <span>#{value}</span>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "状态",
|
|
||||||
dataIndex: "status",
|
|
||||||
key: "status",
|
|
||||||
width: "12%",
|
|
||||||
render: (value, item, key) => {
|
|
||||||
return renderTableStatus(value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "构建人",
|
|
||||||
dataIndex: "author",
|
|
||||||
key: "author",
|
|
||||||
width: "12%",
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "提交信息",
|
|
||||||
dataIndex: "message",
|
|
||||||
key: "message",
|
|
||||||
width: "30%",
|
|
||||||
render: (value, item, key) => {
|
|
||||||
let meg = item.message;
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div>
|
|
||||||
{meg.branch && (
|
|
||||||
<span className="mr10 color-grey-8">
|
|
||||||
<i className="iconfont icon-fenzhi1 font-16 mr5"></i>分支
|
|
||||||
{meg.branch}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{meg.sha && <span className="color-orange">{meg.sha}</span>}
|
|
||||||
</div>
|
|
||||||
<AlignCenter>
|
|
||||||
<img style={{borderRadius:"50%",marginRight:"10px",width:"25px",height:"25px"}} alt="" src={`${item.image_url && getUrl(`/images/${item.image_url}`)}`} />
|
|
||||||
<div className="task-hide ml5" style={{ maxWidth: "300px" }}>
|
|
||||||
{meg.message}
|
|
||||||
</div>
|
|
||||||
</AlignCenter>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "开始时间",
|
|
||||||
dataIndex: "started",
|
|
||||||
key: "started",
|
|
||||||
width: "15%",
|
|
||||||
render: (value, item, key) => {
|
|
||||||
return <span>{value || "--"}</span>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "运行时间",
|
|
||||||
dataIndex: "duration_time",
|
|
||||||
key: "duration_time",
|
|
||||||
width: "15%",
|
|
||||||
render: (value, item, key) => {
|
|
||||||
return <span>{value || "--"}</span>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "操作",
|
|
||||||
dataIndex: "operation",
|
|
||||||
key: "operation",
|
|
||||||
render: (value, item, key) => {
|
|
||||||
if(permission === "Admin" || permission === "Owner"){
|
|
||||||
return renderStatusBtn(item.status, item.number);
|
|
||||||
}else{
|
|
||||||
return "--";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<div className="disposePanel">
|
|
||||||
<Banner>
|
|
||||||
<FlexAJ>
|
|
||||||
<span>构建列表</span>
|
|
||||||
<Link to={`/projects/${owner}/${projectsId}/devops/dispose`} className="font-15 color-grey-9">返回</Link>
|
|
||||||
</FlexAJ>
|
|
||||||
</Banner>
|
|
||||||
<Div>
|
|
||||||
<div className="listPart">
|
|
||||||
<FlexAJ>
|
|
||||||
{renderStatus()}
|
|
||||||
<a onClick={()=>Init(status)} className="color-red font-16">刷新</a>
|
|
||||||
</FlexAJ>
|
|
||||||
<Table
|
|
||||||
onRow={(record,index)=>{
|
|
||||||
return{
|
|
||||||
onClick:(event)=>clickRows(event,record)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
columns={column}
|
|
||||||
className="normalTable"
|
|
||||||
dataSource={data}
|
|
||||||
pagination={false}
|
|
||||||
loading={tableLoading}
|
|
||||||
></Table>
|
|
||||||
{total > LIMIT ?
|
|
||||||
<div style={{ textAlign: "center", margin: "30px 50px" }}>
|
|
||||||
<Pagination
|
|
||||||
showQuickJumper
|
|
||||||
defaultCurrent={page}
|
|
||||||
total={total}
|
|
||||||
pageSize={LIMIT}
|
|
||||||
onChange={ChangePage}
|
|
||||||
></Pagination>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
"" }
|
|
||||||
</div>
|
|
||||||
</Div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default forwardRef(Structure);
|
|
||||||
|
|
@ -1,317 +0,0 @@
|
||||||
import React , { useEffect , useState } from 'react';
|
|
||||||
import { WhiteBack } from '../Component/layout';
|
|
||||||
import { Spin } from 'antd';
|
|
||||||
import Head from './Dispose/head';
|
|
||||||
import Menus from './Dispose/menus';
|
|
||||||
import Init from './Dispose/Init';
|
|
||||||
import Sure from './Dispose/Sure';
|
|
||||||
import Stage from './Dispose/Stage';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
function disposePipeline(props){
|
|
||||||
const [ spining , setSpining ] = useState(true);
|
|
||||||
const [ stage , setStage ] = useState(1);
|
|
||||||
const [ pipeLineName , setPipeLineName ] = useState(undefined);
|
|
||||||
const [ stepName , setStepName ] = useState(undefined);
|
|
||||||
const [ stageId , setStageId ] =useState(undefined);
|
|
||||||
const [ menuList , setMenuList ] = useState(undefined);
|
|
||||||
const [ stageType , setStageType ] = useState("init");
|
|
||||||
const [ datas , setDatas ] = useState(undefined);
|
|
||||||
const [ templates , setTemplates] = useState(undefined);
|
|
||||||
const [ datasUpdataFlag , setDatasUpdataFlag ] = useState(false);
|
|
||||||
const [ loading ,setLoading ] = useState(false);
|
|
||||||
|
|
||||||
const { disposeId } = props.match.params;
|
|
||||||
let projectsId = props.match.params.projectsId;
|
|
||||||
let owner = props.match.params.owner;
|
|
||||||
useEffect(()=>{
|
|
||||||
if(stageType && stageType !=="confirm"){
|
|
||||||
InitTemplates();
|
|
||||||
}
|
|
||||||
},[stageType])
|
|
||||||
|
|
||||||
// 获取模板
|
|
||||||
function InitTemplates(){
|
|
||||||
const url = `/ci/templates/templates_by_stage.json`;
|
|
||||||
axios.get(url,{
|
|
||||||
params:{ stage_type:stageType, id:disposeId }
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setTemplates(result.data);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(disposeId && (stageType && stageType !=="confirm")){
|
|
||||||
getData(0);
|
|
||||||
}
|
|
||||||
},[disposeId])
|
|
||||||
// 获取所有阶段默认第一个阶段的数据
|
|
||||||
function getData(index){
|
|
||||||
const url = `/ci/pipelines/${disposeId}/stages.json`;
|
|
||||||
axios.get(url).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setMenuList(result.data.stages);
|
|
||||||
if(index || index === 0){
|
|
||||||
let first = result.data.stages[index];
|
|
||||||
setStage(first.show_index);
|
|
||||||
setStageId(first.id);
|
|
||||||
setPipeLineName(`${first.pipeline_name}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(stageId){
|
|
||||||
getStageStep(stageId);
|
|
||||||
}
|
|
||||||
},[stageId])
|
|
||||||
// 切换阶段时获取阶段步骤数据(第一次默认查询”初始化“阶段的数据)
|
|
||||||
function getStageStep(stageId){
|
|
||||||
let url = "";
|
|
||||||
if(stageType && stageType === "confirm"){
|
|
||||||
url = `/ci/pipelines/${disposeId}/content.json`;
|
|
||||||
axios.get(url,{
|
|
||||||
params:{
|
|
||||||
owner,repo:projectsId
|
|
||||||
}
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setDatas(result.data);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}else{
|
|
||||||
url = `/ci/pipelines/${disposeId}/${stageId}/steps.json`;
|
|
||||||
axios.get(url).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
let step = result.data.steps;
|
|
||||||
setDatas(step);
|
|
||||||
let flag = !step || (step && step.length === 0);
|
|
||||||
setDatasUpdataFlag(flag);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
setSpining(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换阶段:s:show_index,stage_type:stage_type
|
|
||||||
function changestage(show_index,stage_type,stage_id,stage_name){
|
|
||||||
if(show_index !== stage){
|
|
||||||
saveFunc(show_index,stage_type,stage_id,stage_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取子组件传过来的数据
|
|
||||||
function saveDatas(steps){
|
|
||||||
setDatas([...steps]);
|
|
||||||
setDatasUpdataFlag(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查数组里面是否有空数据
|
|
||||||
function checkDatas(){
|
|
||||||
if(datas && datas.length > 0){
|
|
||||||
for(let i= 0;i< datas.length;i++){
|
|
||||||
if(datas[i] && (!datas[i].content || !datas[i].template_id)){
|
|
||||||
props.showNotification("请先选择模板!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(stageType === "init" ){
|
|
||||||
props.showNotification("请先选择模板!");
|
|
||||||
return false;
|
|
||||||
}else if(stageType === "confirm" ){
|
|
||||||
return true;
|
|
||||||
}else{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存步骤
|
|
||||||
function saveFunc(show_index,stage_type,stage_id,stageName,btn){
|
|
||||||
setSpining(true);
|
|
||||||
// 判断数据是否有过更新
|
|
||||||
if(datasUpdataFlag && stageType !== "confirm"){
|
|
||||||
// 先判断子组件传过来的数据是否有undefined --datas
|
|
||||||
let f = checkDatas();
|
|
||||||
if(f && (datas && datas.length !== 0)){
|
|
||||||
// 数据没问题后先保存数据再切换阶段
|
|
||||||
saveDataFunc(btn,show_index,stage_type,stage_id,stageName);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
setSpining(false);
|
|
||||||
f === "" && elseFunc(btn,show_index,stage_type,stage_id,stageName);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
elseFunc(btn,show_index,stage_type,stage_id,stageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveDataFunc(btn,show_index,stage_type,stage_id,stageName){
|
|
||||||
const url = `/ci/pipelines/${disposeId}/${stageId}/stage_step.json`;
|
|
||||||
axios.post(url,{
|
|
||||||
steps:datas
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setDatasUpdataFlag(false);
|
|
||||||
if(!btn){
|
|
||||||
setStage(show_index);
|
|
||||||
setStageType(stage_type);
|
|
||||||
setStageId(stage_id);
|
|
||||||
setStepName(pipeLineName+`-`+stageName);
|
|
||||||
}else{
|
|
||||||
enters(btn);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
props.showNotification("阶段更新失败,请稍微重试!");
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
function elseFunc(btn,show_index,stage_type,stage_id,stageName){
|
|
||||||
if(btn){
|
|
||||||
enters(btn);
|
|
||||||
}else{
|
|
||||||
setStage(show_index);
|
|
||||||
setStageType(stage_type);
|
|
||||||
setStageId(stage_id);
|
|
||||||
setStepName(pipeLineName+`-`+stageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上一步、下一步
|
|
||||||
function enters(btn){
|
|
||||||
let s = stage;
|
|
||||||
if(btn === "next"){
|
|
||||||
// 点击阶段里面的下一步
|
|
||||||
s = s+1;
|
|
||||||
}else{
|
|
||||||
// 上一步
|
|
||||||
s = s-1;
|
|
||||||
}
|
|
||||||
let item = menuList && menuList.filter(i=>i.show_index === (s));
|
|
||||||
setStage(s);
|
|
||||||
setStageType(item[0].stage_type);
|
|
||||||
setStageId(item[0].id);
|
|
||||||
setStepName(pipeLineName+`-`+item[0].stage_name);
|
|
||||||
}
|
|
||||||
// 删除步骤
|
|
||||||
function deleteStep(id,index){
|
|
||||||
if(id){
|
|
||||||
const url = `/ci/pipelines/${disposeId}/${stageId}/${id}/delete_step.json`;
|
|
||||||
axios.delete(url).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
getStageStep(stageId);
|
|
||||||
props.showNotification("阶段步骤删除成功!");
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}else{
|
|
||||||
let d = datas.filter(s=>s.show_index !== (index+1));
|
|
||||||
setDatas(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function renameFunc(value,id){
|
|
||||||
const url = `/ci/pipelines/${disposeId}/${id}/update_stage.json`;
|
|
||||||
axios.put(url,{
|
|
||||||
stage_name:value
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
getData();
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增阶段
|
|
||||||
function addMenuFunc(name,index){
|
|
||||||
const url = `/ci/pipelines/${disposeId}/create_stage.json`;
|
|
||||||
axios.post(url,{
|
|
||||||
show_index:index,
|
|
||||||
stage_name:name
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
getData(index-1);
|
|
||||||
setStageType("customize");
|
|
||||||
}else{
|
|
||||||
props.showNotification("阶段新增失败!");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除阶段
|
|
||||||
function deleteStageFunc(){
|
|
||||||
let next = menuList && menuList.filter(item=>item.show_index === (stage+1));
|
|
||||||
let nextStageType = next && next.length>0 && next[0].stage_type;
|
|
||||||
const url = `/ci/pipelines/${disposeId}/${stageId}/delete_stage.json`;
|
|
||||||
axios.delete(url,{
|
|
||||||
params:{show_index:stage}
|
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
getData(stage-1);
|
|
||||||
setStageType(nextStageType);
|
|
||||||
}else{
|
|
||||||
props.showNotification("阶段删除失败!");
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确认提交
|
|
||||||
function sureSubmit(){
|
|
||||||
setLoading(true);
|
|
||||||
let params = {
|
|
||||||
branch: datas.branch,
|
|
||||||
content:datas.content,
|
|
||||||
filepath:'.trustie-pipeline.yml',
|
|
||||||
message:'',
|
|
||||||
sha:datas.sha || undefined,
|
|
||||||
owner:owner,
|
|
||||||
repo:projectsId
|
|
||||||
}
|
|
||||||
let url = `/${owner}/${projectsId}/update_trustie_pipeline.json`;
|
|
||||||
axios.put(url,{
|
|
||||||
...params
|
|
||||||
}).then(result=>{
|
|
||||||
if(result){
|
|
||||||
props.history.push(`/projects/${owner}/${projectsId}/devops/dispose`);
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
}).catch(error=>{
|
|
||||||
console.log(error);
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div className="disposePanel">
|
|
||||||
<Head />
|
|
||||||
<WhiteBack style={{padding:"24px 30px"}}>
|
|
||||||
<Spin spinning={spining}>
|
|
||||||
<div style={{minHeight:"450px"}}>
|
|
||||||
<Menus step={stage} checkDatas={checkDatas} changeStep={changestage} menuList={menuList} renameFunc={renameFunc} addFunc={addMenuFunc}/>
|
|
||||||
{
|
|
||||||
stageType === "init" ? <Init stage_type={stageType} templates={templates} datas={datas} saveDatas={saveDatas} saveFunc={saveFunc}/>
|
|
||||||
:
|
|
||||||
stageType === "confirm" ? <Sure sureSubmit={sureSubmit} name={pipeLineName} datas={datas} saveFunc={saveFunc} loading={loading}/>
|
|
||||||
:
|
|
||||||
<Stage {...props}
|
|
||||||
stepName={stepName}
|
|
||||||
deleteStep={deleteStep}
|
|
||||||
stage_type={stageType}
|
|
||||||
templates={templates}
|
|
||||||
datas={datas}
|
|
||||||
deleteFunc={deleteStageFunc}
|
|
||||||
saveDatas={saveDatas}
|
|
||||||
saveFunc={saveFunc}
|
|
||||||
deleteFlag={menuList && menuList.length === 3}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
</WhiteBack>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default disposePipeline;
|
|
||||||
|
|
@ -1,521 +0,0 @@
|
||||||
.opsPanel
|
|
||||||
{
|
|
||||||
margin:20px auto;
|
|
||||||
width: 1200px;
|
|
||||||
// 开始激活页面
|
|
||||||
.activatePanel{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding:60px 0px;
|
|
||||||
}
|
|
||||||
.disposePanel{
|
|
||||||
border:1px solid #eee;
|
|
||||||
.language{
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
li{
|
|
||||||
margin-right: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
border:1px solid #fff;
|
|
||||||
&.active{
|
|
||||||
border:1px solid #5091FF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.editorBody{
|
|
||||||
border:1px solid #ddd;
|
|
||||||
margin:20px 0px;
|
|
||||||
.editorHead{
|
|
||||||
background-color: #F1F8FF;
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding:0px 20px;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
.monaco-editor{
|
|
||||||
width:1138px!important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ant-modal-close{
|
|
||||||
top:7px;
|
|
||||||
}
|
|
||||||
.listPart{
|
|
||||||
min-height: 400px;
|
|
||||||
.listNav{
|
|
||||||
display: flex;
|
|
||||||
li{
|
|
||||||
background-color: #fafafa;
|
|
||||||
border:1px solid #eee;
|
|
||||||
border-radius: 5px;
|
|
||||||
height: 38px;
|
|
||||||
line-height:38px;
|
|
||||||
padding:0px 20px;
|
|
||||||
color: #333;
|
|
||||||
margin-right: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
&:last-child{
|
|
||||||
margin-right: 0px;
|
|
||||||
}
|
|
||||||
&.active{
|
|
||||||
border:1px solid #5091FF;
|
|
||||||
color: #5091FF;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.normalTable{
|
|
||||||
margin-top: 20px;
|
|
||||||
.ant-table-thead > tr > th, .ant-table-tbody > tr > td{
|
|
||||||
padding:10px 5px;
|
|
||||||
color:#333;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.ant-table-thead{
|
|
||||||
border:1px solid rgba(238,238,238,1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ant-modal-close{
|
|
||||||
top:7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// ops详情
|
|
||||||
.opsDetailPanel{
|
|
||||||
height: 100vh;
|
|
||||||
background-color: #114879;
|
|
||||||
.opsInfos{
|
|
||||||
height: 60px;
|
|
||||||
background-color: #114879;
|
|
||||||
color:#fff;
|
|
||||||
padding:0px 20px;
|
|
||||||
}
|
|
||||||
.opsSection{
|
|
||||||
height:calc( 100% - 60px );
|
|
||||||
background-color: #fff;
|
|
||||||
display: flex;
|
|
||||||
section{
|
|
||||||
height: 100%;
|
|
||||||
float: left;
|
|
||||||
&.leftSection{
|
|
||||||
width:100%;
|
|
||||||
.leftheader{
|
|
||||||
background-color: #F1F8FF;
|
|
||||||
padding:10px 20px;
|
|
||||||
.nest{
|
|
||||||
color: #888888;
|
|
||||||
margin-right: 18px;
|
|
||||||
&>span{
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.leftMainContent{
|
|
||||||
padding:14px 20px;
|
|
||||||
.contentBranch{
|
|
||||||
border:1px solid #eee;
|
|
||||||
background-color: #fafafa;
|
|
||||||
padding:0px 18px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 3px;
|
|
||||||
color:#888;
|
|
||||||
i{
|
|
||||||
font-size: 16px;
|
|
||||||
color:#BBB;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.branchname{
|
|
||||||
color:#333;
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
.branchsha{
|
|
||||||
color:#FF6E21;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.leftMenu{
|
|
||||||
li.ant-menu-item,.ant-menu-submenu{
|
|
||||||
padding:0px 13px 0px 20px!important;
|
|
||||||
position: relative;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.ant-menu-submenu-title{
|
|
||||||
margin:0px;
|
|
||||||
padding:0px 0px 0px 20px!important
|
|
||||||
}
|
|
||||||
.ant-menu-submenu-arrow{
|
|
||||||
position: absolute;
|
|
||||||
left: 2px;
|
|
||||||
}
|
|
||||||
.ant-menu.ant-menu-sub{
|
|
||||||
.ant-menu-item:hover{
|
|
||||||
background-color: #F1F8FF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.statuslineColor{
|
|
||||||
display: inline-block;
|
|
||||||
height: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
padding:0px 10px;
|
|
||||||
border-radius: 11px;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-left: 6px;
|
|
||||||
&.pass{
|
|
||||||
border:1px solid rgba(40,189,108,1);
|
|
||||||
color:rgba(40,189,108,1);
|
|
||||||
}
|
|
||||||
&.failed{
|
|
||||||
border:1px solid #F73030;
|
|
||||||
color:#F73030;
|
|
||||||
}
|
|
||||||
&.running{
|
|
||||||
border:1px solid #5091FF;
|
|
||||||
color:#5091FF;
|
|
||||||
}
|
|
||||||
&.Preparing{
|
|
||||||
border:1px solid rgba(255,110,33,1);
|
|
||||||
color:rgba(255,110,33,1);
|
|
||||||
}
|
|
||||||
&.killed{
|
|
||||||
border:1px solid #999;
|
|
||||||
color:#999;
|
|
||||||
}
|
|
||||||
&.skipped{
|
|
||||||
border:1px solid #d4d9de;
|
|
||||||
color:#798390;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.rightSection{
|
|
||||||
width:100%;
|
|
||||||
background-color: #081930;
|
|
||||||
.devopsNav{
|
|
||||||
background-color: #111c24;
|
|
||||||
border-bottom: none;
|
|
||||||
.ant-menu-item{
|
|
||||||
color: #ccc;
|
|
||||||
padding:0px;
|
|
||||||
margin:0px 20px!important;
|
|
||||||
}
|
|
||||||
.ant-menu-item.ant-menu-item-selected{
|
|
||||||
color: #1890ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.rightMainContent{
|
|
||||||
padding:24px 30px;
|
|
||||||
height:100vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
& > div{
|
|
||||||
margin-bottom: 12px;
|
|
||||||
.items{
|
|
||||||
height: 45px;
|
|
||||||
line-height: 45px;
|
|
||||||
padding:0px 24px;
|
|
||||||
background:rgba(24,41,62,1);
|
|
||||||
border-radius:5px;
|
|
||||||
color:#fff;
|
|
||||||
cursor: pointer;
|
|
||||||
i{
|
|
||||||
font-size: 13px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.Resizer.vertical{
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.Resizer.vertical:hover:before{
|
|
||||||
background: #eee;
|
|
||||||
}
|
|
||||||
.Resizer.vertical:before {
|
|
||||||
content: "";
|
|
||||||
border-radius: 50%;
|
|
||||||
background: hsla(0,0%,92.2%,.3);
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 999;
|
|
||||||
top: 50%;
|
|
||||||
margin-top: -12px;
|
|
||||||
right: -12px;
|
|
||||||
}
|
|
||||||
.Resizer.vertical::after{
|
|
||||||
position: absolute;
|
|
||||||
content: "\e712";
|
|
||||||
font-family: iconfont;
|
|
||||||
font-size: 10px;
|
|
||||||
transform: scale(.8);
|
|
||||||
top: 50%;
|
|
||||||
margin-top: -10px;
|
|
||||||
color: #666;
|
|
||||||
right: -12px;
|
|
||||||
opacity: .3;
|
|
||||||
z-index: 1000;
|
|
||||||
cursor: col-resize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.opsDetailOut{
|
|
||||||
display: flex;
|
|
||||||
color: #fff;
|
|
||||||
line-height: 22px;
|
|
||||||
align-items: flex-start;
|
|
||||||
margin-top: 5px;
|
|
||||||
&>span{
|
|
||||||
margin-right: 10px;
|
|
||||||
min-width: 20px;
|
|
||||||
text-align: left;
|
|
||||||
padding-left: 3px;
|
|
||||||
}
|
|
||||||
&>p{
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.noOperation{
|
|
||||||
margin-bottom:20px;
|
|
||||||
width:380px;
|
|
||||||
text-align:center;
|
|
||||||
line-height:22px;
|
|
||||||
color:#666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disposeList{
|
|
||||||
min-height: 450px;
|
|
||||||
.ant-table-body{
|
|
||||||
margin:0px!important;
|
|
||||||
.ant-table-thead{
|
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.txtright{
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.menus{
|
|
||||||
display: flex;
|
|
||||||
box-shadow: 0px 0px 6px rgba(0,3,8,0.1);
|
|
||||||
padding:20px 0px 10px 0px;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
& > li{
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
align-items:center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
text-align: center;
|
|
||||||
color: #787878;
|
|
||||||
font-size: 16px;
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
max-width: 122px;
|
|
||||||
flex:2;
|
|
||||||
&>i{
|
|
||||||
font-size: 30px!important;
|
|
||||||
height: 30px;
|
|
||||||
width: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
}
|
|
||||||
&:hover,&.active{
|
|
||||||
& > i{
|
|
||||||
color: #1890FF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.active::after{
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -25px;
|
|
||||||
bottom:-10px;
|
|
||||||
width: 50px;
|
|
||||||
height: 2px;
|
|
||||||
background-color: #1890FF;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
.aboutEdit{
|
|
||||||
width: 100%;
|
|
||||||
.operateName{
|
|
||||||
position: relative;
|
|
||||||
line-height: 22px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 0px 15px;
|
|
||||||
min-height: 40px;
|
|
||||||
& > i{
|
|
||||||
position: absolute;
|
|
||||||
right:0px;
|
|
||||||
top:10px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover i{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& > li.menuAdd{
|
|
||||||
position: relative;
|
|
||||||
flex: 1;
|
|
||||||
&:hover{
|
|
||||||
& > i{
|
|
||||||
color: #787878;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& > i{
|
|
||||||
font-size: 18px!important;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top:0px;
|
|
||||||
}
|
|
||||||
& > input{
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top:3px;
|
|
||||||
}
|
|
||||||
&::before{
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
border-bottom: 1px dashed #eee;
|
|
||||||
content: "";
|
|
||||||
top:15px;
|
|
||||||
margin-top: -1px;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.choosenList{
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
line-height: 35px;
|
|
||||||
align-items: center;
|
|
||||||
& > span{
|
|
||||||
display: block;
|
|
||||||
min-width: 75px;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
height: 35px;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
& > ul{
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
li{
|
|
||||||
padding:0px 12px;
|
|
||||||
height: 35px;
|
|
||||||
line-height: 35px;
|
|
||||||
border:1px solid #e1e4e8;
|
|
||||||
background-color: #fff;
|
|
||||||
margin:6px 0px;
|
|
||||||
margin-right: 12px;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
li.active{
|
|
||||||
color: #fff;
|
|
||||||
background-color: #1890FF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ant-select-selection.ant-select-selection--multiple{
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.addStageBtn{
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
border:1px solid #e1e4e8;
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
color: #1890FF;
|
|
||||||
font-size: 16px;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 0px 20px;
|
|
||||||
}
|
|
||||||
.stepsItem{
|
|
||||||
border:1px solid #e1e4e8;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
.stepsHead{
|
|
||||||
padding:8px 15px;
|
|
||||||
span > a > i{
|
|
||||||
margin-left: 8px;
|
|
||||||
color: #666!important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stepsBody{
|
|
||||||
padding:10px 15px;
|
|
||||||
border-top: 1px solid #e1e4e8;
|
|
||||||
background-color: rgb(251, 251, 251);
|
|
||||||
display: none;
|
|
||||||
transition: 0.3s;
|
|
||||||
}
|
|
||||||
.stepsBody.active{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.editorPanel{
|
|
||||||
border:1px solid #eee;
|
|
||||||
.margin{
|
|
||||||
width: 0px;
|
|
||||||
}
|
|
||||||
.monaco-scrollable-element.editor-scrollable{
|
|
||||||
left: 0px!important;
|
|
||||||
width: 100%!important;
|
|
||||||
.view-lines{
|
|
||||||
width: 336px!important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.hide{
|
|
||||||
display: none!important;
|
|
||||||
}
|
|
||||||
.statusTag{
|
|
||||||
display: flex;
|
|
||||||
padding:0px 16px;
|
|
||||||
height: 32px;
|
|
||||||
line-height: 32px;
|
|
||||||
border-radius:20px;
|
|
||||||
float: left;
|
|
||||||
i{
|
|
||||||
font-size: 20px!important;
|
|
||||||
margin-right: 7px;
|
|
||||||
}
|
|
||||||
&.running{
|
|
||||||
background:#F1F8FF;
|
|
||||||
border:1px solid #5091FF;
|
|
||||||
color: #5091FF;
|
|
||||||
}
|
|
||||||
&.Preparing{
|
|
||||||
background:rgba(255,248,244,1);
|
|
||||||
border:1px solid rgba(255,110,33,1);
|
|
||||||
color:rgba(255,110,33,1) ;
|
|
||||||
}
|
|
||||||
&.pass{
|
|
||||||
background:#EEFDF5;
|
|
||||||
border:1px solid #28BD6C;
|
|
||||||
color:#28BD6C ;
|
|
||||||
}
|
|
||||||
&.failed{
|
|
||||||
background:#FCEEEE;
|
|
||||||
border:1px solid #F73030;
|
|
||||||
color:#F73030 ;
|
|
||||||
}
|
|
||||||
&.killed{
|
|
||||||
background:#eee;
|
|
||||||
border:1px solid #999;
|
|
||||||
color:#999 ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
import React, { useEffect, useState, useRef, useContext } from "react";
|
|
||||||
import "./ops.scss";
|
|
||||||
import { FlexAJ, AlignCenter } from "../Component/layout";
|
|
||||||
import { Tags } from "../Component/OpsStatus";
|
|
||||||
import SplitPane from "react-split-pane";
|
|
||||||
import LeftPanel from "./OpsDetailLeftpanel";
|
|
||||||
import RightPanel from "./OpsDetailRightpanel";
|
|
||||||
import axios from "axios";
|
|
||||||
import { Spin } from "antd";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
export default (props) => {
|
|
||||||
const [data, setData] = useState(undefined);
|
|
||||||
const [stageN, setStageN] = useState(undefined);
|
|
||||||
const [stepN, setStepN] = useState(undefined);
|
|
||||||
const [rightSpin, setRightSpin] = useState(false);
|
|
||||||
const [spinning, setSpinning] = useState(true);
|
|
||||||
|
|
||||||
let projectId = props.match.params.projectId;
|
|
||||||
let owner = props.match.params.owner;
|
|
||||||
let opsId = props.match.params.opsId;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (opsId && projectId) {
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
}, [opsId]);
|
|
||||||
|
|
||||||
function Init() {
|
|
||||||
const url = `/${owner}/${projectId}/builds/${opsId}.json`;
|
|
||||||
axios.get(url).then((result) => {
|
|
||||||
if (result && result.data) {
|
|
||||||
setSpinning(false);
|
|
||||||
setData(result.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
setSpinning(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新构建
|
|
||||||
function repeatSet(e,type,number) {
|
|
||||||
if(type==="repeat"){
|
|
||||||
// 重新构建
|
|
||||||
const url = `/${owner}/${projectId}/builds/${number}/restart.json`;
|
|
||||||
axios.post(url).then((result) => {
|
|
||||||
if (result && result.data) {
|
|
||||||
props.showNotification("工作流正在重新构建!");
|
|
||||||
props.history.push(`/projects/${owner}/${projectId}/devops/${result.data.number}/detail`);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
// 撤销构建
|
|
||||||
const url = `/${owner}/${projectId}/builds/${number}/stop.json`;
|
|
||||||
axios.delete(url).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
props.showNotification("撤销构建成功!");
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function chooseSteps(pre,sub){
|
|
||||||
if(pre && sub){
|
|
||||||
setStepN(sub);
|
|
||||||
setStageN(pre);
|
|
||||||
setRightSpin(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Spin spinning={spinning}>
|
|
||||||
<div className="opsDetailPanel">
|
|
||||||
<FlexAJ className="opsInfos">
|
|
||||||
<AlignCenter>
|
|
||||||
<span>#{data && data.number}</span>
|
|
||||||
<span className="ml10">{data && data.message}</span>
|
|
||||||
{Tags(`${data && data.status}`)}
|
|
||||||
</AlignCenter>
|
|
||||||
<Link
|
|
||||||
style={{ color: "#ddd" }}
|
|
||||||
to={`/projects/${owner}/${projectId}/devops/dispose`}
|
|
||||||
>
|
|
||||||
<i className="iconfont icon-yiguanbi font-15 mr5"></i>退出
|
|
||||||
</Link>
|
|
||||||
</FlexAJ>
|
|
||||||
<div className="opsSection">
|
|
||||||
<SplitPane
|
|
||||||
className="outer-split-pane"
|
|
||||||
split="vertical"
|
|
||||||
minSize={468}
|
|
||||||
maxSize={-350}
|
|
||||||
defaultSize="40%"
|
|
||||||
>
|
|
||||||
<section className="leftSection">
|
|
||||||
<LeftPanel data={data} repeatSet={repeatSet} chooseSteps={chooseSteps} />
|
|
||||||
</section>
|
|
||||||
<section className="rightSection">
|
|
||||||
<RightPanel data={data} rightSpin={rightSpin} stepN={stepN} stageN={stageN} owner={owner} projectId={projectId} opsId={opsId} />
|
|
||||||
</section>
|
|
||||||
</SplitPane>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import XmlPanel from "./XmlPanel";
|
|
||||||
import mediator from "./mediator";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
// const defaulturl = `http://47.111.130.18:48088`;
|
|
||||||
const defaultValue = {
|
|
||||||
host: "106.75.231.63",
|
|
||||||
port: "2021",
|
|
||||||
ws_url: "wss://pre-webssh.educoder.net/ws",
|
|
||||||
username: "root",
|
|
||||||
secret: "Dron_123123",
|
|
||||||
};
|
|
||||||
function Index() {
|
|
||||||
const [sshConfigData, setSshConfigData] = useState(undefined);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!sshConfigData) {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
mediator.publish("create-socket", 1);
|
|
||||||
}, 300);
|
|
||||||
}, [sshConfigData]);
|
|
||||||
|
|
||||||
// 获取服务器连接信息
|
|
||||||
function init() {
|
|
||||||
const url = `/api/ci/pipelines/ssh_server.json`;
|
|
||||||
axios.get(url).then(result=>{
|
|
||||||
if(result && result.data){
|
|
||||||
setSshConfigData({...result.data})
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<XmlPanel
|
|
||||||
sshConfigData={sshConfigData||{}}
|
|
||||||
sid={1}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default Index;
|
|
||||||
|
|
@ -1,219 +0,0 @@
|
||||||
import React, { useRef, useEffect, useState } from 'react';
|
|
||||||
import { Base64 } from 'js-base64';
|
|
||||||
|
|
||||||
import { Terminal } from 'xterm';
|
|
||||||
import 'xterm/css/xterm.css';
|
|
||||||
import mediator from './mediator';
|
|
||||||
import ResizeObserver from 'resize-observer-polyfill';
|
|
||||||
|
|
||||||
function getColsAndRows(width, height, term) {
|
|
||||||
let w = term._core._renderService.dimensions.actualCellWidth || 9.5;
|
|
||||||
let h = term._core._renderService.dimensions.actualCellHeight || 18;
|
|
||||||
const rows = Math.floor(height / h);
|
|
||||||
const cols = Math.floor(width / w);
|
|
||||||
return [cols, rows];
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLayout(term, el) {
|
|
||||||
const ro = new ResizeObserver(entries => {
|
|
||||||
console.log(entries);
|
|
||||||
for (let entry of entries) {
|
|
||||||
if (entry.target.offsetHeight > 0 || entry.target.offsetWidth > 0) {
|
|
||||||
const [cols, rows] = getColsAndRows(
|
|
||||||
entry.target.offsetWidth,
|
|
||||||
entry.target.offsetHeight,
|
|
||||||
term,
|
|
||||||
);
|
|
||||||
console.log('cols, rows', cols, rows);
|
|
||||||
term.resize(cols, rows);
|
|
||||||
mediator.publish('ssh-xterm-resize', {
|
|
||||||
columns: cols,
|
|
||||||
rows: rows,
|
|
||||||
width: entry.target.offsetWidth,
|
|
||||||
height: entry.target.offsetHeight,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ro.observe(el);
|
|
||||||
return ro;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TimeTicket = 30000;
|
|
||||||
|
|
||||||
//建立 websockt 来交互
|
|
||||||
//根据容器大小计算行数和列数并做到自适应
|
|
||||||
//socket 与 term 需要分开初始化 因为socket 可能重置连接
|
|
||||||
//mediator 监听消息,如果和id匹配,则建立连接,重置,或关闭连接
|
|
||||||
|
|
||||||
export default ({ sshConfigData, sid }) => {
|
|
||||||
const [term, setTerm] = useState(null);
|
|
||||||
|
|
||||||
const { ws_url, password, port, secret } = sshConfigData;
|
|
||||||
const el = useRef();
|
|
||||||
const socket = useRef();
|
|
||||||
const isFirstConnected = useRef(false);
|
|
||||||
|
|
||||||
//term init
|
|
||||||
useEffect(() => {
|
|
||||||
if (el.current && ws_url) {
|
|
||||||
const term = new Terminal({ fontSize: 16, rendererType: 'dom' });
|
|
||||||
term.open(el.current);
|
|
||||||
|
|
||||||
term.onData(data => {
|
|
||||||
if (socket.current) {
|
|
||||||
if (socket.current.readyState === 1) {
|
|
||||||
socket.current.send(JSON.stringify({ tp: 'client', data: data }));
|
|
||||||
mediator.publish('on-operating-ssh'); //有操作则自动延时
|
|
||||||
} else {
|
|
||||||
//断开连接后重连
|
|
||||||
// socket.current = null
|
|
||||||
// mediator.publish('create-socket', sid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
term.write('Connecting...');
|
|
||||||
setTerm(term);
|
|
||||||
const ro = onLayout(term, el.current);
|
|
||||||
return () => {
|
|
||||||
term.dispose();
|
|
||||||
ro.unobserve(el.current);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, [ws_url, el.current]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (term && ws_url) {
|
|
||||||
function createSocket() {
|
|
||||||
const socketInstance = new WebSocket(ws_url);
|
|
||||||
socket.current = socketInstance;
|
|
||||||
|
|
||||||
socketInstance.onopen = () => {
|
|
||||||
let container = term.element.parentElement;
|
|
||||||
if (container) {
|
|
||||||
let width = container.offsetWidth;
|
|
||||||
let height = container.offsetHeight;
|
|
||||||
console.log('init', {
|
|
||||||
tp: 'init',
|
|
||||||
data: {
|
|
||||||
...sshConfigData,
|
|
||||||
secret: secret,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
rows: term.rows,
|
|
||||||
columns: term.cols,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
socketInstance.send(
|
|
||||||
JSON.stringify({
|
|
||||||
tp: 'init',
|
|
||||||
data: {
|
|
||||||
...sshConfigData,
|
|
||||||
secret: secret,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
rows: term.rows,
|
|
||||||
columns: term.cols,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
term.focus();
|
|
||||||
};
|
|
||||||
socketInstance.onerror = error => {
|
|
||||||
console.log(
|
|
||||||
'------in socket error----',
|
|
||||||
error,
|
|
||||||
socketInstance,
|
|
||||||
ws_url,
|
|
||||||
);
|
|
||||||
//连接报错后,重新请求资源
|
|
||||||
// mediator.publish('on-recreate-socket')
|
|
||||||
};
|
|
||||||
socketInstance.onmessage = event => {
|
|
||||||
if (!isFirstConnected.current) {
|
|
||||||
term.write('\r');
|
|
||||||
// term.focus()
|
|
||||||
setTimeout(() => {
|
|
||||||
// term.clear();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
isFirstConnected.current = true;
|
|
||||||
console.log('event:', event);
|
|
||||||
|
|
||||||
const data = Base64.decode(event.data.toString());
|
|
||||||
let w = term._core._renderService.dimensions.actualCellWidth || 9.5;
|
|
||||||
|
|
||||||
console.log('data:', data, w, term);
|
|
||||||
term.write(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
socketInstance.onclose = evt => {
|
|
||||||
if (tid) {
|
|
||||||
clearInterval(tid);
|
|
||||||
}
|
|
||||||
term.write('\r\nconnection closed');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const tid = setInterval(() => {
|
|
||||||
if (socket.current) {
|
|
||||||
socket.current.send(JSON.stringify({ tp: 'h' }));
|
|
||||||
}
|
|
||||||
}, TimeTicket);
|
|
||||||
|
|
||||||
const unSubCreate = mediator.subscribe('create-socket', id => {
|
|
||||||
if (sid === id) {
|
|
||||||
if (socket.current && socket.current.readyState === 1) {
|
|
||||||
term.focus();
|
|
||||||
} else {
|
|
||||||
createSocket();
|
|
||||||
}
|
|
||||||
term.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const unSubClose = mediator.subscribe('close-socket', id => {
|
|
||||||
if (sid === id) {
|
|
||||||
if (socket.current) {
|
|
||||||
socket.current.close();
|
|
||||||
isFirstConnected.current = false;
|
|
||||||
term.clear();
|
|
||||||
}
|
|
||||||
socket.current = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const unSubResize = mediator.subscribe('ssh-xterm-resize', option => {
|
|
||||||
if (socket.current && socket.current.readyState === 1) {
|
|
||||||
socket.current.send(
|
|
||||||
JSON.stringify({ tp: 'resize', data: { ...option } }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const unSubAddTime = mediator.subscribe('ssh-add-connect-time', () => {
|
|
||||||
if (socket.current && socket.current.readyState === 1) {
|
|
||||||
socket.current.send(JSON.stringify({ tp: 'overtime' }));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
unSubClose();
|
|
||||||
unSubCreate();
|
|
||||||
unSubResize();
|
|
||||||
unSubAddTime();
|
|
||||||
if (socket.current) {
|
|
||||||
socket.current.close();
|
|
||||||
isFirstConnected.current = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, [term, ws_url, port]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={el} className="xterm-panel" style={{height:"100%"}}>
|
|
||||||
{!ws_url ? <p style={{ color: '#fff' }}>正在连接命令行服务...</p> : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
function Mediator(obj) {
|
|
||||||
const channels = {};
|
|
||||||
|
|
||||||
const mediator = {
|
|
||||||
subscribe: function(channel, cb) {
|
|
||||||
if (!channels[channel]) {
|
|
||||||
channels[channel] = [];
|
|
||||||
}
|
|
||||||
channels[channel].push(cb);
|
|
||||||
return this.unsubscribe.bind(null, channel, cb);
|
|
||||||
},
|
|
||||||
|
|
||||||
unsubscribe: function(channel, cb) {
|
|
||||||
let rs = channels[channel];
|
|
||||||
let index = -1;
|
|
||||||
if (rs) {
|
|
||||||
for (let i = 0; i < rs.length; i++) {
|
|
||||||
if (rs[i].name === cb.name) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index >= 0) {
|
|
||||||
channels[channel].splice(index, 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
publish: function(channel) {
|
|
||||||
if (!channels[channel]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
channels[channel].forEach(subscription => {
|
|
||||||
subscription.apply(null, args);
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return obj ? Object.assign(obj, mediator) : mediator;
|
|
||||||
}
|
|
||||||
const mediator = new Mediator();
|
|
||||||
export default mediator;
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
import React ,{ forwardRef, useEffect, useState } from 'react';
|
|
||||||
import { Modal , Form , Input , Radio , Select } from 'antd';
|
|
||||||
import SearchUser from '../Component/SearchUser';
|
|
||||||
import './Index.scss';
|
|
||||||
import Axios from 'axios';
|
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
function DivertModal({form , visible , onSuccess , onCancel,owner,repo}){
|
|
||||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
|
||||||
const [ cate , setCate ] = useState(0);
|
|
||||||
const [ value , setValue ] = useState(undefined);
|
|
||||||
|
|
||||||
const [ organizations , setOrganizations ] = useState(undefined);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
setFieldsValue({goal:cate})
|
|
||||||
},[])
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
if(owner && repo && visible===true){
|
|
||||||
getTeam();
|
|
||||||
}
|
|
||||||
if(!visible){
|
|
||||||
setFieldsValue({
|
|
||||||
owner_name:undefined,
|
|
||||||
identifier:undefined
|
|
||||||
})
|
|
||||||
setValue(undefined)
|
|
||||||
}
|
|
||||||
},[repo,owner,visible])
|
|
||||||
|
|
||||||
function getTeam(){
|
|
||||||
const url = `/${owner}/${repo}/applied_transfer_projects/organizations.json`;
|
|
||||||
Axios.get(url).then(result=>{
|
|
||||||
if(result){
|
|
||||||
setOrganizations(result.data.organizations);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确认转移
|
|
||||||
function onOk(){
|
|
||||||
validateFields((error,values)=>{
|
|
||||||
console.log(...values);
|
|
||||||
if(!error){
|
|
||||||
const url = `/${owner}/${repo}/applied_transfer_projects.json`;
|
|
||||||
Axios.post(url,{
|
|
||||||
...values
|
|
||||||
}).then(result=>{
|
|
||||||
if(result){
|
|
||||||
onSuccess(result.data && result.data.owner);
|
|
||||||
}
|
|
||||||
}).catch(error=>{})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
function changeType(e){
|
|
||||||
setCate(e.target.value);
|
|
||||||
setFieldsValue({
|
|
||||||
owner_name:undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkIdentifier(rule, value, callback){
|
|
||||||
if(!value){
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
if (repo && value !== repo) {
|
|
||||||
callback("请输入当前项目的标识!");
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
const layout = {
|
|
||||||
labelCol: { span: 5 },
|
|
||||||
wrapperCol: { span: 18 },
|
|
||||||
};
|
|
||||||
|
|
||||||
function getUser(id){
|
|
||||||
setValue(id);
|
|
||||||
setFieldsValue({
|
|
||||||
owner_name:id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return(
|
|
||||||
<Modal
|
|
||||||
width="620px"
|
|
||||||
visible={visible}
|
|
||||||
title="转移仓库"
|
|
||||||
onCancel={onCancel}
|
|
||||||
onOk={onOk}
|
|
||||||
okText="确认转移"
|
|
||||||
cancelText={"取消"}
|
|
||||||
centered
|
|
||||||
>
|
|
||||||
<div className="diverModal">
|
|
||||||
{
|
|
||||||
cate === 0 ?
|
|
||||||
<ul className="descUl">
|
|
||||||
<li>转移需对方确认接受,转移成功后你将被移出仓库,其他已有成员权限不变</li>
|
|
||||||
<li>转移成功后,仓库的地址将变更至目标用户的命名空间下</li>
|
|
||||||
<li>已有成员如需继续操作仓库,需更新本地仓库的remote,使之指向新的地址</li>
|
|
||||||
</ul>
|
|
||||||
:
|
|
||||||
<ul className="descUl">
|
|
||||||
<li>仓库仅可以转移到您已经加入的组织中,不可以转移到未加入的组织中</li>
|
|
||||||
<li>涉及到仓库改名操作,请提前做好仓库备份并且在转移后对本地仓库的remote进行修改</li>
|
|
||||||
<li>转移仓库到组织后,你和组织创建者/管理员同时拥有对该仓库的管理操作</li>
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
<Form {...layout} colon={false} layout={"horizontal"}>
|
|
||||||
<Form.Item label="转移给:" style={{marginBottom:"0px"}}>
|
|
||||||
{getFieldDecorator("goal",{
|
|
||||||
rules:[]
|
|
||||||
})(
|
|
||||||
<Radio.Group onChange={changeType}>
|
|
||||||
<Radio value={0}>个人</Radio>
|
|
||||||
<Radio value={1}>组织</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
{
|
|
||||||
cate === 0 &&
|
|
||||||
<Form.Item label=" ">
|
|
||||||
{getFieldDecorator("owner_name",{
|
|
||||||
rules:[{required:true,message:"请输入目标用户名"}]
|
|
||||||
})(
|
|
||||||
// <Input placeholder="请输入目标用户" autoComplete={"off"}/>
|
|
||||||
<SearchUser getUser={getUser} width={"100%"} placeholder="请输入目标用户" value={value}/>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
cate === 1 &&
|
|
||||||
<Form.Item label=" ">
|
|
||||||
{getFieldDecorator("owner_name",
|
|
||||||
{rules:[{required:true,message:"请选择目标组织"}]}
|
|
||||||
)(
|
|
||||||
<Select placeholder="请选择目标组织" getPopupContainer={trigger => trigger.parentNode}>
|
|
||||||
{
|
|
||||||
organizations && organizations.length > 0 ?
|
|
||||||
organizations.map((i,k)=>{
|
|
||||||
return(
|
|
||||||
<Option value={i.name}>{i.nickname}</Option>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
:""
|
|
||||||
}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Form.Item label="仓库名称:">
|
|
||||||
{getFieldDecorator("identifier",
|
|
||||||
{
|
|
||||||
rules:[
|
|
||||||
{required:true,message:"请输入仓库名称"},
|
|
||||||
{
|
|
||||||
validator:checkIdentifier
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)(
|
|
||||||
<Input placeholder="请输入仓库名称" autoComplete={"off"}/>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Form.create()(forwardRef(DivertModal));
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
.diverModal{
|
|
||||||
.descUl{
|
|
||||||
background-color: #fffae6;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding:10px 15px;
|
|
||||||
color: #efc16b;
|
|
||||||
border:1px solid #efc16b;
|
|
||||||
}
|
|
||||||
.ant-form-item-required::before{
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +1,13 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
// 获取分支列表
|
// 获取分支列表
|
||||||
export const getBranch = async (id,owner)=>{
|
export const getBranch = async (id)=>{
|
||||||
return (await axios.get(`/${owner}/${id}/branches.json`)).data;
|
return (await axios.get(`/projects/${id}/branches.json`)).data;
|
||||||
}
|
}
|
||||||
// 获取标签列表
|
// 获取标签列表
|
||||||
export const getTag = async (id,owner)=>{
|
export const getTag = async (id)=>{
|
||||||
return (await axios.get(`/${owner}/${id}/tags.json`)).data;
|
return (await axios.get(`/repositories/${id}/tags.json`)).data;
|
||||||
}
|
}
|
||||||
// 获取hooks(仓库设置-管理web钩子)列表
|
// 获取hooks(仓库设置-管理web钩子)列表
|
||||||
export const getHooks = async (id,params)=>{
|
export const getHooks = async (id,params)=>{
|
||||||
return (await axios.get(`/projects/${id}/hooks.json`,{params})).data;
|
return (await axios.get(`/projects/${id}/hooks.json`,{params})).data;
|
||||||
}
|
|
||||||
// 获取子目录列表
|
|
||||||
export const getSubEntries = async (owner,projectsId,params)=>{
|
|
||||||
return (await axios.get(`/${owner}/${projectsId}/sub_entries.json`,{params})).data;
|
|
||||||
}
|
|
||||||
// 获取用户信息
|
|
||||||
export const getUser = async (login)=>{
|
|
||||||
return (await axios.get(`/users/${login}/hovercard.json`)).data;
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import React, { useEffect , useState } from 'react';
|
|
||||||
import './header.scss';
|
|
||||||
|
|
||||||
function Footer(){
|
|
||||||
const [ value , setValue ] = useState(undefined);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
|
||||||
try {
|
|
||||||
var chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
|
|
||||||
setValue(chromesettingArray.footer);
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
},[])
|
|
||||||
|
|
||||||
function showhtml(htmlString){
|
|
||||||
var html = {__html:htmlString};
|
|
||||||
return <div dangerouslySetInnerHTML={html}></div> ;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<div style={{height:"483px"}}></div>
|
|
||||||
<div className="newFooter edu-txt-center">
|
|
||||||
{value && showhtml(value)}
|
|
||||||
{/* <div className="footerInfos">
|
|
||||||
<ul>
|
|
||||||
<li>社区</li>
|
|
||||||
<li><a href={`/`} target="_blank">网站首页</a></li>
|
|
||||||
<li><a href={`https://www.trustie.net/agreement`} target="_blank">服务协议</a></li>
|
|
||||||
<li><a href={`https://forum.trustie.net/forums/1168/detail`} target="_blank">帮助中心</a></li>
|
|
||||||
<li><a href={`https://forum.trustie.net/`} target="_blank">问吧交流</a></li>
|
|
||||||
<li><a href={`https://www.trustie.net/cooperation`} target="_blank">合作伙伴</a></li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>支持与服务</li>
|
|
||||||
<li><a href={`https://forgeplus.trustie.net/docs/api`} target="_blank">API文档</a></li>
|
|
||||||
<li><a href={`https://forum.trustie.net/forums/1168/detail`} target="_blank">帮助中心</a></li>
|
|
||||||
<li><a href={`https://git-scm.com`} target="_blank">Git常用命令</a></li>
|
|
||||||
<li><a href={`https://forum.trustie.net/forums/3080/detail`} target="_blank">DevOps使用文档</a></li>
|
|
||||||
<li><a href={`https://forgeplus.trustie.net/projects/jasder/forgeplus/tree/master/CHANGELOG.md`} target="_blank">日志更新</a></li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>合作伙伴</li>
|
|
||||||
<li><a href={`http://www.sei.pku.edu.cn`} target="_blank">北京大学</a></li>
|
|
||||||
<li><a href={`http://scse.buaa.edu.cn`} target="_blank">北京航空航天大学</a></li>
|
|
||||||
<li><a href={`https://www.nju.edu.cn`} target="_blank">南京大学</a></li>
|
|
||||||
<li><a href={`https://www.xtu.edu.cn`} target="_blank">湘潭大学</a></li>
|
|
||||||
<li><a href={`http://www.iscas.ac.cn`} target="_blank">ISCAS</a></li>
|
|
||||||
<li><a href={`https://www.ucloud.cn`} target="_blank">UCloud优刻得</a></li>
|
|
||||||
<li><a href={`http://www.inforbus.com`} target="_blank">中创软件</a></li>
|
|
||||||
<li><a href={`https://www.inspur.com`} target="_blank">浪潮集团</a></li>
|
|
||||||
<li><a href={`http://www.copu.org.cn`} target="_blank">中国开源软件推进联盟</a></li>
|
|
||||||
<li><a href={`https://www.sjtu.edu.cn`} target="_blank">上海交通大学</a></li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>合作伙伴</li>
|
|
||||||
<li><span>热线:</span></li>
|
|
||||||
<li><span>QQ群:1071514693</span></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p className="footerCopy">© Copyright 2007~2021 国防科技大学Trustie团队 & IntelliDE <a href="https://beian.miit.gov.cn">湘ICP备 17009477号</a></p> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default Footer;
|
|
||||||
|
|
@ -1,728 +0,0 @@
|
||||||
import React, { Component } from 'react';
|
|
||||||
import AccountProfile from "../../modules/user/AccountProfile";
|
|
||||||
import { getImageUrl } from 'educoder'
|
|
||||||
import axios from 'axios';
|
|
||||||
import { Modal, Input, message, notification , Dropdown , Menu } from 'antd';
|
|
||||||
|
|
||||||
import LoginDialog from '../../modules/login/LoginDialog';
|
|
||||||
import GotoQQgroup from '../../modal/GotoQQgroup'
|
|
||||||
|
|
||||||
import '../../modules/tpm/TPMIndex.css';
|
|
||||||
import logo from '../../modules/tpm/images/logo.png';
|
|
||||||
|
|
||||||
import './header.scss';
|
|
||||||
const $ = window.$
|
|
||||||
// TODO 这部分脚本从公共脚本中直接调用
|
|
||||||
const { Search } = Input;
|
|
||||||
let old_url;
|
|
||||||
|
|
||||||
window._header_componentHandler = null;
|
|
||||||
// 非trustie链接则新开页跳转
|
|
||||||
const str = ['www.trustie.net','forgeplus.trustie.net','forum.trustie.net','testforgeplus.trustie.net']
|
|
||||||
class NewHeader extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
this.state = {
|
|
||||||
Addcoursestypes: false,
|
|
||||||
tojoinitemtype: false,
|
|
||||||
tojoinclasstitle: undefined,
|
|
||||||
rolearr: ["", ""],
|
|
||||||
Checkboxteacherchecked: false,
|
|
||||||
Checkboxstudentchecked: false,
|
|
||||||
Checkboxteachingchecked: false,
|
|
||||||
Checkboxteachertype: false,
|
|
||||||
Checkboxteachingtype: false,
|
|
||||||
code_notice: false,
|
|
||||||
checked_notice: false,
|
|
||||||
RadioGroupvalue: undefined,
|
|
||||||
submitapplications: false,
|
|
||||||
isRender: false,
|
|
||||||
showSearchOpentype: false,
|
|
||||||
showTrial: false,
|
|
||||||
setevaluatinghides: false,
|
|
||||||
occupation: 0,
|
|
||||||
mydisplay: false,
|
|
||||||
headtypesonClickbool: false,
|
|
||||||
headtypess: "/",
|
|
||||||
settings: null,
|
|
||||||
goshowqqgtounp: false,
|
|
||||||
visiblemyss: false,
|
|
||||||
openSearch:false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
componentDidMount() {
|
|
||||||
// this.getAppdata();
|
|
||||||
this.geturlsdata();
|
|
||||||
window._header_componentHandler = this;
|
|
||||||
|
|
||||||
//下拉框的显示隐藏
|
|
||||||
var hoverTimeout;
|
|
||||||
var hoveredPanel;
|
|
||||||
$(".edu-menu-panel").hover(function () {
|
|
||||||
if (hoverTimeout) { // 一次只显示一个panel
|
|
||||||
if (hoveredPanel && hoveredPanel !== this) {
|
|
||||||
$(hoveredPanel).find(".edu-menu-list").hide()
|
|
||||||
}
|
|
||||||
clearTimeout(hoverTimeout);
|
|
||||||
hoverTimeout = null;
|
|
||||||
}
|
|
||||||
hoveredPanel = this;
|
|
||||||
$(this).find(".edu-menu-list").show();
|
|
||||||
}, function () {
|
|
||||||
var that = this;
|
|
||||||
// 延迟hide
|
|
||||||
hoverTimeout = setTimeout(function () {
|
|
||||||
$(that).find(".edu-menu-list").hide();
|
|
||||||
}, 800)
|
|
||||||
|
|
||||||
});
|
|
||||||
//获取游览器地址
|
|
||||||
try {
|
|
||||||
window.sessionStorage.setItem("yslgeturls", JSON.stringify(window.location.href))
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchInput = (open,item)=>{
|
|
||||||
if(open){
|
|
||||||
return(
|
|
||||||
<div
|
|
||||||
onBlur={() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.setState({
|
|
||||||
openSearch:false
|
|
||||||
})
|
|
||||||
}, 300)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Search placeholder="实践课程/教学课堂/实践项目/交流问答"
|
|
||||||
className={`search-input mr20`}
|
|
||||||
onSearch={(value)=>this.onGlobalSearch(value,item)}
|
|
||||||
autoFocus={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}else{
|
|
||||||
return <i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
|
|
||||||
this.setState({openSearch:true})
|
|
||||||
}} />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onGlobalSearch=(value,item)=>{
|
|
||||||
window.location.href=`${item}?value=` + value;
|
|
||||||
}
|
|
||||||
|
|
||||||
openNotification = (messge) => {
|
|
||||||
notification.open({
|
|
||||||
message: "提示",
|
|
||||||
description:
|
|
||||||
messge,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
componentWillReceiveProps(newProps, oldProps) {
|
|
||||||
this.setState({
|
|
||||||
user: newProps.user
|
|
||||||
})
|
|
||||||
if (newProps.Headertop !== undefined) {
|
|
||||||
old_url = newProps.Headertop.old_url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getCookie = (key) => {
|
|
||||||
var arr, reg = RegExp('(^| )' + key + '=([^;]+)(;|$)');
|
|
||||||
if (arr === document.cookie.match(reg))
|
|
||||||
return decodeURIComponent(arr[2]);
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
delCookie = (name) => {
|
|
||||||
var exp = new Date();
|
|
||||||
exp.setTime(exp.getTime() - 1);
|
|
||||||
var cval = this.getCookie(name);
|
|
||||||
if (cval != null) {
|
|
||||||
document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onLogout = () => {
|
|
||||||
const url = `/accounts/logout.json`
|
|
||||||
this.delCookie("autologin_trustie")
|
|
||||||
axios.get(url, {
|
|
||||||
}).then((response) => {
|
|
||||||
if (response.data.status === 1) {
|
|
||||||
this.setState({
|
|
||||||
user: undefined
|
|
||||||
})
|
|
||||||
window.location.href = "/login"
|
|
||||||
message.success('退出成功');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
tojoinclass = () => {
|
|
||||||
let { user } = this.state;
|
|
||||||
if (user === undefined) {
|
|
||||||
this.setState({
|
|
||||||
isRender: true
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (user && user.login === "") {
|
|
||||||
this.setState({
|
|
||||||
isRender: true
|
|
||||||
})
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (user && user.profile_completed === false) {
|
|
||||||
this.setState({
|
|
||||||
AccountProfiletype: true
|
|
||||||
})
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
Addcoursestypes: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
tojoinitem = () => {
|
|
||||||
if (this.props.user && this.props.user.email === undefined || this.props.user && this.props.user.email === null || this.props.user && this.props.user.email === "") {
|
|
||||||
this.openNotification("请先绑定邮箱,谢谢");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let { user } = this.state;
|
|
||||||
if (user === undefined) {
|
|
||||||
this.setState({
|
|
||||||
isRender: true
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (user && user.login === "") {
|
|
||||||
this.setState({
|
|
||||||
isRender: true
|
|
||||||
})
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user && user.profile_completed === false) {
|
|
||||||
this.setState({
|
|
||||||
AccountProfiletype: true
|
|
||||||
})
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
tojoinitemtype: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
submitstatevalue = (sum, value, data) => {
|
|
||||||
this.setState({
|
|
||||||
Addcoursestypes: false,
|
|
||||||
tojoinitemtype: false,
|
|
||||||
tojoinclasstitle: undefined,
|
|
||||||
rolearr: ["", ""],
|
|
||||||
Checkboxteacherchecked: false,
|
|
||||||
Checkboxstudentchecked: false,
|
|
||||||
Checkboxteachingchecked: false,
|
|
||||||
Checkboxteachertype: false,
|
|
||||||
Checkboxteachingtype: false,
|
|
||||||
code_notice: false,
|
|
||||||
checked_notice: false,
|
|
||||||
submitapplicationssum: sum,
|
|
||||||
submitapplications: true,
|
|
||||||
submitapplicationsvalue: value,
|
|
||||||
submitapplicationsvaluedata: data,
|
|
||||||
RadioGroupvalue: undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onChangeRadioGroup = (e) => {
|
|
||||||
this.setState({
|
|
||||||
RadioGroupvalue: e.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
submitsubmitapplications = () => {
|
|
||||||
let {
|
|
||||||
submitapplicationssum,
|
|
||||||
submitapplicationsvaluedata
|
|
||||||
} = this.state;
|
|
||||||
this.setState({
|
|
||||||
submitapplications: false,
|
|
||||||
RadioGroupvalue: undefined
|
|
||||||
})
|
|
||||||
if (submitapplicationssum === 0) {
|
|
||||||
if (submitapplicationsvaluedata !== undefined) {
|
|
||||||
window.location.href = "/courses/" + submitapplicationsvaluedata;
|
|
||||||
}
|
|
||||||
} else if (submitapplicationssum === 1) {
|
|
||||||
if (submitapplicationsvaluedata !== undefined) {
|
|
||||||
window.location.href = "/projects/" + submitapplicationsvaluedata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hidesubmitapplications = () => {
|
|
||||||
this.setState({
|
|
||||||
Addcoursestypes: false,
|
|
||||||
tojoinitemtype: false,
|
|
||||||
tojoinclasstitle: undefined,
|
|
||||||
rolearr: ["", ""],
|
|
||||||
Checkboxteacherchecked: false,
|
|
||||||
Checkboxstudentchecked: false,
|
|
||||||
Checkboxteachingchecked: false,
|
|
||||||
Checkboxteachertype: false,
|
|
||||||
Checkboxteachingtype: false,
|
|
||||||
code_notice: false,
|
|
||||||
checked_notice: false,
|
|
||||||
submitapplications: false,
|
|
||||||
RadioGroupvalue: undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
educoderlogin = () => {
|
|
||||||
//登录账号
|
|
||||||
this.setState({
|
|
||||||
isRender: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
educoderloginysl = () => {
|
|
||||||
//退出账号
|
|
||||||
var url = `/accounts/logout.json`;
|
|
||||||
axios.get((url)).then((result) => {
|
|
||||||
if (result !== undefined) {
|
|
||||||
window.location.href = "/";
|
|
||||||
}
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
hideAddcoursestypes = () => {
|
|
||||||
this.setState({
|
|
||||||
Addcoursestypes: false
|
|
||||||
})
|
|
||||||
};
|
|
||||||
HideAddcoursestypess = (i) => {
|
|
||||||
console.log("调用了");
|
|
||||||
this.setState({
|
|
||||||
Addcoursestypes: false,
|
|
||||||
mydisplay: true,
|
|
||||||
occupation: i,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
ModalCancelsy = () => {
|
|
||||||
this.setState({
|
|
||||||
mydisplay: false,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
hidetojoinclass = () => {
|
|
||||||
this.setState({
|
|
||||||
tojoinclasstype: false,
|
|
||||||
tojoinitemtype: false,
|
|
||||||
tojoinclasstitle: undefined,
|
|
||||||
rolearr: ["", ""],
|
|
||||||
Checkboxteacherchecked: false,
|
|
||||||
Checkboxstudentchecked: false,
|
|
||||||
Checkboxteachingchecked: false,
|
|
||||||
Checkboxteachertype: false,
|
|
||||||
Checkboxteachingtype: false,
|
|
||||||
code_notice: false,
|
|
||||||
checked_notice: false,
|
|
||||||
RadioGroupvalue: undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭
|
|
||||||
cancelModulationModels = () => {
|
|
||||||
this.setState({ isRenders: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setevaluatinghides = () => {
|
|
||||||
this.setState({
|
|
||||||
setevaluatinghides: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
//修改登录方法
|
|
||||||
Modifyloginvalue = () => {
|
|
||||||
this.setState({
|
|
||||||
isRender: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
hideAccountProfile = () => {
|
|
||||||
this.setState({
|
|
||||||
AccountProfiletype: false
|
|
||||||
})
|
|
||||||
};
|
|
||||||
headtypesonClick = (url, bool) => {
|
|
||||||
this.setState({
|
|
||||||
headtypess: url,
|
|
||||||
headtypesonClickbool: bool,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
//获取数据为空的时候
|
|
||||||
gettablogourlnull = () => {
|
|
||||||
this.setState({
|
|
||||||
settings: undefined
|
|
||||||
});
|
|
||||||
var link = document.createElement('link'),
|
|
||||||
oldLink = document.getElementById('dynamic-favicon');
|
|
||||||
link.id = 'dynamic-favicon';
|
|
||||||
link.rel = 'shortcut icon';
|
|
||||||
link.href = "/react/build/./favicon.ico";
|
|
||||||
if (oldLink) {
|
|
||||||
document.head.removeChild(oldLink);
|
|
||||||
}
|
|
||||||
document.head.appendChild(link);
|
|
||||||
};
|
|
||||||
|
|
||||||
//获取数据的时候
|
|
||||||
gettablogourldata = (response) => {
|
|
||||||
document.title = response.data.setting.name;
|
|
||||||
var link = document.createElement('link'),
|
|
||||||
oldLink = document.getElementById('dynamic-favicon');
|
|
||||||
link.id = 'dynamic-favicon';
|
|
||||||
link.rel = 'shortcut icon';
|
|
||||||
link.href = '/' + response.data.setting.tab_logo_url;
|
|
||||||
if (oldLink) {
|
|
||||||
document.head.removeChild(oldLink);
|
|
||||||
}
|
|
||||||
document.head.appendChild(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleVisibleChanges = (boll) => {
|
|
||||||
this.setState({
|
|
||||||
visiblemyss: boll,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getAppdata = () => {
|
|
||||||
try {
|
|
||||||
var chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
|
|
||||||
var chromesettingresponseArray = JSON.parse(localStorage.getItem('chromesettingresponse'));
|
|
||||||
this.setState({
|
|
||||||
settings: chromesettingArray
|
|
||||||
});
|
|
||||||
if (chromesettingArray.tab_logo_url) {
|
|
||||||
this.gettablogourldata(chromesettingresponseArray);
|
|
||||||
} else {
|
|
||||||
this.gettablogourlnull();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.geturlsdata();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
geturlsdata = () => {
|
|
||||||
let url = "/setting.json";
|
|
||||||
axios.get(url).then((response) => {
|
|
||||||
if (response && response.data) {
|
|
||||||
this.setState({ settings: response.data.setting });
|
|
||||||
// localStorage.setItem('chromesetting', JSON.stringify(response.data.setting));
|
|
||||||
// localStorage.setItem('chromesettingresponse', JSON.stringify(response));
|
|
||||||
try {
|
|
||||||
if (response.data.setting.tab_logo_url) {
|
|
||||||
this.gettablogourldata(response);
|
|
||||||
} else {
|
|
||||||
this.gettablogourlnull();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.gettablogourlnull();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.gettablogourlnull();
|
|
||||||
}
|
|
||||||
}).catch((error) => {
|
|
||||||
this.gettablogourlnull();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
matchpaths = (url) => {
|
|
||||||
const { match } = this.props;
|
|
||||||
if(url){
|
|
||||||
if (match.path.indexOf(url) > -1) {
|
|
||||||
return true
|
|
||||||
}else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 处理弹框
|
|
||||||
setgoshowqqgtounp = (bool) => {
|
|
||||||
this.setState({
|
|
||||||
goshowqqgtounp: bool
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
addMenu=(list)=>{
|
|
||||||
return(
|
|
||||||
list && list.length >0 &&
|
|
||||||
<div className="dropdownFlex">
|
|
||||||
<Menu>
|
|
||||||
{
|
|
||||||
list.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
(item.name !=="加入课堂" && item.name !=="加入开发项目") && <Menu.Item><a href={item.url}>{item.name}</a></Menu.Item>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { match} = this.props;
|
|
||||||
let current_user = this.props.user;
|
|
||||||
let { Addcoursestypes,
|
|
||||||
tojoinitemtype,
|
|
||||||
tojoinclasstitle,
|
|
||||||
code_notice,
|
|
||||||
checked_notice,
|
|
||||||
AccountProfiletype,
|
|
||||||
submitapplications,
|
|
||||||
submitapplicationsvalue,
|
|
||||||
user,
|
|
||||||
isRender,
|
|
||||||
showSearchOpentype,
|
|
||||||
headtypesonClickbool,
|
|
||||||
headtypess,
|
|
||||||
settings,
|
|
||||||
goshowqqgtounp,
|
|
||||||
openSearch,
|
|
||||||
} = this.state;
|
|
||||||
/*用户名称 用户头像url*/
|
|
||||||
let activeIndex = false;
|
|
||||||
let activeForums = false;
|
|
||||||
let activeShixuns = false;
|
|
||||||
let activePaths = false;
|
|
||||||
let coursestype = false;
|
|
||||||
let activePackages = false;
|
|
||||||
let activeMoopCases = false;
|
|
||||||
let activeCompetitions = false;
|
|
||||||
|
|
||||||
if (match.path === '/forums') {
|
|
||||||
activeForums = true;
|
|
||||||
} else if (match.path.startsWith('/shixuns')) {
|
|
||||||
activeShixuns = true;
|
|
||||||
} else if (match.path.startsWith('/paths')) {
|
|
||||||
activePaths = true;
|
|
||||||
} else if (match.path.startsWith('/courses')) {
|
|
||||||
coursestype = true;
|
|
||||||
} else if (match.path.startsWith('/crowdsourcing')) {
|
|
||||||
activePackages = true;
|
|
||||||
} else if (match.path.startsWith('/moop_cases')) {
|
|
||||||
activeMoopCases = true;
|
|
||||||
} else if (match.path.startsWith('/competitions')) {
|
|
||||||
activeCompetitions = true;
|
|
||||||
} else {
|
|
||||||
activeIndex = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let headtypes = '/';
|
|
||||||
if (settings) {
|
|
||||||
if (settings.navbar) {
|
|
||||||
if (settings.navbar.length > 0) {
|
|
||||||
if (match.path === '/') {
|
|
||||||
if (headtypesonClickbool === false) {
|
|
||||||
headtypes = undefined;
|
|
||||||
} else {
|
|
||||||
headtypes = headtypess;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (var i = 0; i < settings.navbar.length; i++) {
|
|
||||||
if (match.path === settings.navbar[i].link) {
|
|
||||||
headtypes = settings.navbar[i].link;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let shixuntype = false;
|
|
||||||
let pathstype = false;
|
|
||||||
let coursestypes = false;
|
|
||||||
if (this.props && this.props.mygetHelmetapi != null) {
|
|
||||||
let shixun = "/shixuns";
|
|
||||||
let paths = "/paths";
|
|
||||||
let courses = "/courses";
|
|
||||||
this.props.mygetHelmetapi.navbar.map((item, key) => {
|
|
||||||
var reg = RegExp(item.link);
|
|
||||||
if (shixun.match(reg)) {
|
|
||||||
if (item.hidden === true) {
|
|
||||||
shixuntype = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (paths.match(reg)) {
|
|
||||||
if (item.hidden === true) {
|
|
||||||
pathstype = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (courses.match(reg)) {
|
|
||||||
if (item.hidden === true) {
|
|
||||||
coursestypes = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let search_url = settings && settings.common && settings.common.search;
|
|
||||||
let notice_url = settings && settings.common && settings.common.notice;
|
|
||||||
return (
|
|
||||||
<div className="newHeaders" id="nHeader">
|
|
||||||
<div className="headerContent">
|
|
||||||
{isRender === true ?
|
|
||||||
<LoginDialog
|
|
||||||
{...this.props}
|
|
||||||
{...this.state}
|
|
||||||
Modifyloginvalue={() => this.Modifyloginvalue()}
|
|
||||||
/> : ""}
|
|
||||||
|
|
||||||
{AccountProfiletype === true ?
|
|
||||||
<AccountProfile
|
|
||||||
hideAccountProfile={() => this.hideAccountProfile()}
|
|
||||||
{...this.props}
|
|
||||||
{...this.state}
|
|
||||||
/> : ""}
|
|
||||||
{
|
|
||||||
goshowqqgtounp === true ?
|
|
||||||
<GotoQQgroup {...this.state} {...this.props} setgoshowqqgtounp={(bool) => this.setgoshowqqgtounp(bool)}></GotoQQgroup>
|
|
||||||
:""
|
|
||||||
}
|
|
||||||
<a href={settings && settings.new_course.default_url} className={"fl mr30"} style={{minWidth:"45px"}}>
|
|
||||||
{
|
|
||||||
settings && settings.nav_logo_url ?
|
|
||||||
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={getImageUrl(`/${settings.nav_logo_url}`)}></img>
|
|
||||||
:
|
|
||||||
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={logo}></img>
|
|
||||||
}
|
|
||||||
</a>
|
|
||||||
<div className="head-nav pr" id={"head-navpre1"}>
|
|
||||||
{
|
|
||||||
settings && settings.navbar && settings.navbar.length > 0 ?
|
|
||||||
<ul id="header-nav">
|
|
||||||
{
|
|
||||||
settings.navbar && settings.navbar.map((item, key) => {
|
|
||||||
var new_link = item.link;
|
|
||||||
var user_login = this.props.user && this.props.user.login;
|
|
||||||
var is_hidden = item.hidden
|
|
||||||
if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) {
|
|
||||||
if (user_login) {
|
|
||||||
if (new_link.indexOf("courses") > -1) {
|
|
||||||
new_link = new_link.replace(/courses/g, "users/" + user_login + "/courses")
|
|
||||||
} else if (new_link.indexOf("contests") > -1) {
|
|
||||||
new_link = new_link.replace(/contests/g, "users/" + user_login + "/contests")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
is_hidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (user_login && (new_link && new_link.indexOf("homes") > -1)) {
|
|
||||||
new_link = new_link.replace(/homes/g, "users/" + user_login + "/user_activities")
|
|
||||||
}
|
|
||||||
|
|
||||||
var waiLian = (new_link && str.filter(item=>new_link.indexOf(item)>-1) );
|
|
||||||
var wl = waiLian && waiLian.length>0;
|
|
||||||
return (
|
|
||||||
<li key={key} onClick={() => this.headtypesonClick(item.link, true)} className={`${this.matchpaths(item.link) === true ? 'pr active' : 'pr'}`} style={!is_hidden ? { display: 'flex' } : { display: 'none' }}>
|
|
||||||
<a href={new_link} target={wl ? "_self":"_blank"}>{item.name}</a>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className="head-right">
|
|
||||||
{search_url ? this.SearchInput(openSearch,search_url):""}
|
|
||||||
{
|
|
||||||
current_user && (current_user.main_site || current_user.login) && (settings && settings.add && settings.add.length>0)?
|
|
||||||
<Dropdown overlay={this.addMenu(settings && settings.add)} placement="bottomRight">
|
|
||||||
<i className="iconfont icon-tianjiafangda color-grey-6 ml30"></i>
|
|
||||||
</Dropdown>:""
|
|
||||||
}
|
|
||||||
|
|
||||||
{this.props.user && this.props.user.login && notice_url ?
|
|
||||||
<div className="ml30 edu-menu-panel">
|
|
||||||
{user && user.login &&
|
|
||||||
<a href={`${notice_url}`} style={{ position: 'relative' }}>
|
|
||||||
<i className="iconfont icon-xiaoxilingdang color-grey-6"></i>
|
|
||||||
<span className="newslight" style={{ display: this.props.Headertop === undefined ? "none" : this.props.Headertop.new_message === true ? "block" : "none" }}>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</div>:""
|
|
||||||
}
|
|
||||||
<Modal
|
|
||||||
keyboard={false}
|
|
||||||
title="提示"
|
|
||||||
visible={submitapplications}
|
|
||||||
closable={false}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<div className="task_popup_con ml30">
|
|
||||||
<div className="mr15">
|
|
||||||
<ul>
|
|
||||||
<div className="task-popup-content">
|
|
||||||
<p className="task-popup-text-center font-16">
|
|
||||||
{submitapplicationsvalue}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<li className="clearfix mt10 edu-txt-center">
|
|
||||||
<a className="task-btn mr10"
|
|
||||||
onClick={this.hidesubmitapplications}>取消</a>
|
|
||||||
<a
|
|
||||||
className="task-btn task-btn-orange ml20"
|
|
||||||
onClick={this.submitsubmitapplications}>确定</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
{!user || (user && !user.login) ?
|
|
||||||
<span className="font-15 ml30">
|
|
||||||
<a onClick={() => this.educoderlogin()} className="mr5 color-grey-6">登录</a>
|
|
||||||
{
|
|
||||||
settings && settings.common && settings.common.register &&
|
|
||||||
<span><em className="vertical-line"></em><a className="ml5 color-grey-6" href={`${settings.common.register}`} target="_blank">注册</a></span>
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
:
|
|
||||||
<div className="ml30 edu-menu-panel" style={{ height: "70px", lineHeight: "70px" }}>
|
|
||||||
<a href={`/users/${this.props.current_user === undefined ? "" : this.props.current_user.login}/courses`}>
|
|
||||||
<img alt="头像" className="radius" height="34" id="nh_user_logo" name="avatar_image" src={getImageUrl(`/${user.image_url}`)} width="34">
|
|
||||||
</img>
|
|
||||||
</a>
|
|
||||||
<ul className="edu-menu-list" style={{ top: '60px', textAlign: 'center' }}>
|
|
||||||
<li className="bor-bottom-greyE" style={{cursor:"default",background:"#fff"}}>{this.props.current_user.username}</li>
|
|
||||||
{
|
|
||||||
settings && settings.personal && settings.personal.length > 0 && settings.personal.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<li key={key}><a href={item.url} target="_blank">{item.name}</a></li>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
<li className="bor-top-greyE">
|
|
||||||
<a onClick={() => this.educoderloginysl()}>退出</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NewHeader;
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
|
|
||||||
.dropdownFlex{
|
|
||||||
display:flex;
|
|
||||||
padding:5px;
|
|
||||||
background:#fff;
|
|
||||||
border-radius: 3px;
|
|
||||||
.ant-menu-vertical > .ant-menu-item{
|
|
||||||
border:none;
|
|
||||||
height: 35px;
|
|
||||||
line-height: 35px;
|
|
||||||
margin:0px;
|
|
||||||
}
|
|
||||||
.ant-menu-vertical{
|
|
||||||
border:none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.newFooter {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
background: #323232;
|
|
||||||
clear: both;
|
|
||||||
min-width: 1200px;
|
|
||||||
z-index: 8;
|
|
||||||
left: 0px;
|
|
||||||
p {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom:0px !important;
|
|
||||||
}
|
|
||||||
.footerInfos{
|
|
||||||
display: flex;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin:0px auto;
|
|
||||||
justify-content: space-around;
|
|
||||||
padding:60px 0px;
|
|
||||||
& >ul{
|
|
||||||
padding:0px 40px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
max-width: 25%;
|
|
||||||
text-align: left;
|
|
||||||
li{
|
|
||||||
color: #fff;
|
|
||||||
font-weight: 300;
|
|
||||||
&:first-child{
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
|
||||||
&>a,&>span{
|
|
||||||
color: #bbb;
|
|
||||||
}
|
|
||||||
&>a:hover{
|
|
||||||
color: #4cacff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.footerCopy{
|
|
||||||
color: #bbb;
|
|
||||||
border-top: 1px solid #4e4e4e;
|
|
||||||
padding:10px 0px;
|
|
||||||
a{
|
|
||||||
color: #bbb;
|
|
||||||
&:hover{
|
|
||||||
color: #4cacff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 590 KiB After Width: | Height: | Size: 590 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue