diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index f221492f6..000000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,1190 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1584692398144 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ac0688720..a72d2386e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3425,9 +3425,9 @@ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==" }, "clipboard": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz", - "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz", + "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==", "requires": { "good-listener": "^1.2.2", "select": "^1.1.2", @@ -3875,6 +3875,52 @@ "warning": "^4.0.3" } }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "requires": { + "cross-spawn": "^7.0.1" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "cross-fetch": { "version": "3.1.4", "resolved": "https://registry.nlark.com/cross-fetch/download/cross-fetch-3.1.4.tgz", @@ -4885,7 +4931,7 @@ }, "dom-closest": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz", + "resolved": "https://registry.npm.taobao.org/dom-closest/download/dom-closest-0.2.0.tgz", "integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=", "requires": { "dom-matches": ">=1.0.1" @@ -4929,7 +4975,7 @@ }, "dom-matches": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz", + "resolved": "https://registry.npm.taobao.org/dom-matches/download/dom-matches-2.0.0.tgz", "integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw=" }, "dom-scroll-into-view": { @@ -5187,7 +5233,7 @@ }, "enquire.js": { "version": "2.1.6", - "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "resolved": "https://registry.npm.taobao.org/enquire.js/download/enquire.js-2.1.6.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fenquire.js%2Fdownload%2Fenquire.js-2.1.6.tgz", "integrity": "sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ=" }, "entities": { @@ -5706,7 +5752,7 @@ }, "eventlistener": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz", + "resolved": "https://registry.npm.taobao.org/eventlistener/download/eventlistener-0.0.1.tgz", "integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg=" }, "events": { @@ -8040,7 +8086,7 @@ }, "hammerjs": { "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "resolved": "https://registry.npm.taobao.org/hammerjs/download/hammerjs-2.0.8.tgz", "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" }, "handle-thing": { @@ -8881,7 +8927,7 @@ }, "immutable": { "version": "3.7.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "resolved": "https://registry.npm.taobao.org/immutable/download/immutable-3.7.6.tgz", "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" }, "import-fresh": { @@ -10486,7 +10532,7 @@ }, "lodash.throttle": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "resolved": "https://registry.npm.taobao.org/lodash.throttle/download/lodash.throttle-4.1.1.tgz", "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" }, "lodash.uniq": { @@ -16586,6 +16632,11 @@ } } }, + "save-dev": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/save-dev/-/save-dev-0.0.1-security.tgz", + "integrity": "sha512-k6knZTDNK8PKKbIqnvxiOveJinuw2LcQjqDoaorZWP9M5AR2EPsnpDeSbeoZZ0pHr5ze1uoaKdK8NBGQrJ34Uw==" + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", diff --git a/package.json b/package.json index b58b501df..04142cb4d 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,11 @@ "case-sensitive-paths-webpack-plugin": "2.1.1", "chalk": "1.1.3", "classnames": "^2.2.5", - "clipboard": "^2.0.6", + "clipboard": "^2.0.8", "code-prettify": "^0.1.0", "codemirror": "^5.53.0", "connected-react-router": "4.4.1", + "cross-env": "^7.0.3", "css-loader": "^3.5.2", "dompurify": "^2.0.15", "dotenv": "4.0.0", @@ -103,6 +104,7 @@ "redux-thunk": "2.3.0", "rsuite": "^4.3.4", "sass-loader": "7.3.1", + "save-dev": "0.0.1-security", "scroll-into-view": "^1.14.2", "showdown": "^1.9.1", "showdown-katex": "^0.8.0", @@ -122,8 +124,8 @@ }, "scripts": { "start": "node --max_old_space_size=15360 scripts/start.js", - "build": "NODE_ENV=production node --max_old_space_size=15360 scripts/build.js", - "test-build": "NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js", + "build": "cross-env NODE_ENV=production node --max_old_space_size=15360 scripts/build.js", + "test-build": "cross-env NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js", "pre-build": "NODE_ENV=preBuild node --max_old_space_size=15360 scripts/build.js", "gen_stats": "NODE_ENV=production webpack --profile --config=./config/webpack.config.prod.js --json > stats.json", "ana": "webpack-bundle-analyzer ./stats.json", @@ -193,6 +195,7 @@ "babel-core": "^6.26.0", "babel-plugin-import": "^1.13.0", "babel-plugin-transform-runtime": "^6.23.0", + "babel-polyfill": "^6.26.0", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babel-preset-stage-2": "^6.24.1", diff --git a/public/css/edu-common.css b/public/css/edu-common.css index f2ac9e67e..30ddf03f6 100755 --- a/public/css/edu-common.css +++ b/public/css/edu-common.css @@ -111,14 +111,6 @@ a:visited { color: #898989; } -a:hover { - color: #FF7500; -} - -a:hover.fa { - color: #FF7500; -} - input, textarea, select { diff --git a/public/css/edu-main.css b/public/css/edu-main.css index 3cbe5fcbb..1d9959033 100644 --- a/public/css/edu-main.css +++ b/public/css/edu-main.css @@ -97,10 +97,6 @@ a:visited { color: #05101a; } -a:hover { - color: #459be5; -} - ol, ul, li { diff --git a/public/css/edu-purge.css b/public/css/edu-purge.css index 19b983eca..0d23c5bce 100644 --- a/public/css/edu-purge.css +++ b/public/css/edu-purge.css @@ -1,3 +1,4 @@ +@charset "utf-8"; /* 头部 */ .header { width: 100%; @@ -1271,7 +1272,7 @@ html body { font-size: 14px; line-height: 2.0; background: #fafafa; - font-family: "微软雅黑", "宋体"; + font-family: "Microsoft YaHei", "SimSun"; color: #05101a; height: 100%; position: relative; @@ -1307,6 +1308,7 @@ td, span { margin: 0; padding: 0; + margin-bottom: 0px!important; } table, @@ -1363,10 +1365,6 @@ a:visited { color: #05101a; } -a:hover { - color: #459be5; -} - ol, ul, li { @@ -1473,7 +1471,7 @@ a.edu-txt-w80, /*隐藏*/ .none { - display: none + display: none!important; } .block { @@ -1522,7 +1520,15 @@ a.edu-txt-w80, .font-16 { font-size: 16px !important; } - +.weight400{ + font-weight: 400; +} +.weight500{ + font-weight: 500; +} +.weight{ + font-weight: bold; +} .font-17 { font-size: 17px !important; } @@ -1542,6 +1548,9 @@ a.edu-txt-w80, .font-25 { font-size: 25px !important; } +.font-26 { + font-size: 26px !important; +} .font-24 { font-size: 24px !important; @@ -1563,6 +1572,9 @@ a.edu-txt-w80, font-size: 36px !important; } +.font-40 { + font-size: 40px !important; +} .font-50 { font-size: 50px !important; } @@ -1748,6 +1760,14 @@ a.decoration { margin-bottom: 10px; } +.mb12 { + margin-bottom: 12px; +} + +.mb13 { + margin-bottom: 13px; +} + .mb14 { margin-bottom: 14px; } @@ -2436,7 +2456,11 @@ a.hoverLine:hover{ .color-grey-9 { - color: #999999 !important; + color: #999 !important; +} + +a:hover{ + color: #466AFF !important; } .color-grey-98 { @@ -2471,33 +2495,23 @@ a.hoverLine:hover{ a.color-grey-name:hover, a.color-dark:hover, a.color-grey-6:hover, -a.color-grey-3:hover { - color: #4cacff !important; -} - -a.color-grey-9:hover, -a.color-grey-8:hover, -a.color-grey-c:hover { - color: #111C24 !important; +a.color-grey-3:hover,a.color-ooo:hover { + color: #2A61FF !important; } /*蓝色*/ .color-blue { - color: #4CACFF !important; + color: #2A61FF !important; +} +.color-blue-file { + color: #4598FA!important; } /* 绿色 */ -.color-green-file{ - color: #28BD6C; -} /*主*/ .color-blue_4C { color: #4CACFF !important; } -a.color-blue:hover, -a.color-blue_4C:hover { - color: #459BE6 !important; -} /*橙色*/ .color-orange { diff --git a/public/css/iconfont.css b/public/css/iconfont.css index 4498c693a..3866189c2 100644 --- a/public/css/iconfont.css +++ b/public/css/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 2340181 */ - src: url('iconfont.woff2?t=1630632852475') format('woff2'), - url('iconfont.woff?t=1630632852475') format('woff'), - url('iconfont.ttf?t=1630632852475') format('truetype'); + src: url('iconfont.woff2?t=1632964996877') format('woff2'), + url('iconfont.woff?t=1632964996877') format('woff'), + url('iconfont.ttf?t=1632964996877') format('truetype'); } .iconfont { @@ -13,6 +13,82 @@ -moz-osx-font-smoothing: grayscale; } +.icon-wenjian7:before { + content: "\e8e0"; +} + +.icon-xiangyoujiantou:before { + content: "\e8de"; +} + +.icon-xiangzuojiantou:before { + content: "\e8df"; +} + +.icon-a-liulanicon2x:before { + content: "\e8dd"; +} + +.icon-wenjianicon:before { + content: "\e8dc"; +} + +.icon-a-yuanquan2x:before { + content: "\e8db"; +} + +.icon-xiangmubiaoqian:before { + content: "\e8da"; +} + +.icon-icon:before { + content: "\e8ce"; +} + +.icon-tar:before { + content: "\e8cf"; +} + +.icon-a-fuzhi2:before { + content: "\e8d0"; +} + +.icon-fujian1:before { + content: "\e8d1"; +} + +.icon-a-bianji1:before { + content: "\e8d2"; +} + +.icon-banbenicon:before { + content: "\e8d3"; +} + +.icon-shanchuicon2:before { + content: "\e8d4"; +} + +.icon-a-lajitong_icon3x:before { + content: "\e8d5"; +} + +.icon-xialaanniu2:before { + content: "\e8d6"; +} + +.icon-xiazai-icon:before { + content: "\e8d7"; +} + +.icon-master_icon1:before { + content: "\e8d8"; +} + +.icon-shangchuanicon:before { + content: "\e8d9"; +} + .icon-gerenziliao1:before { content: "\e8c7"; } diff --git a/public/css/iconfont.js b/public/css/iconfont.js index e50f44e1a..0e23ffb85 100644 --- a/public/css/iconfont.js +++ b/public/css/iconfont.js @@ -1 +1 @@ -!function(c){var l,a,h,i,o,z='',t=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss"),p=function(c,l){l.parentNode.insertBefore(c,l)};if(t&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function v(){o||(o=!0,h())}function m(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(m,50)}v()}l=function(){var c,l;(l=document.createElement("div")).innerHTML=z,z=null,(c=l.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",l=c,(c=document.body).firstChild?p(l,c.firstChild):c.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(a=function(){document.removeEventListener("DOMContentLoaded",a,!1),l()},document.addEventListener("DOMContentLoaded",a,!1)):document.attachEvent&&(h=l,i=c.document,o=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,v())})}(window); +!function(c){var a,l,h,i,o,z='',t=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss"),p=function(c,a){a.parentNode.insertBefore(c,a)};if(t&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function v(){o||(o=!0,h())}function m(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(m,50)}v()}a=function(){var c,a;(a=document.createElement("div")).innerHTML=z,z=null,(c=a.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",a=c,(c=document.body).firstChild?p(a,c.firstChild):c.appendChild(a))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(l=function(){document.removeEventListener("DOMContentLoaded",l,!1),a()},document.addEventListener("DOMContentLoaded",l,!1)):document.attachEvent&&(h=a,i=c.document,o=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,v())})}(window); \ No newline at end of file diff --git a/public/css/iconfont.json b/public/css/iconfont.json index e78a264c5..5acfb6e98 100644 --- a/public/css/iconfont.json +++ b/public/css/iconfont.json @@ -5,6 +5,139 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "24656750", + "name": "文件", + "font_class": "wenjian7", + "unicode": "e8e0", + "unicode_decimal": 59616 + }, + { + "icon_id": "630094", + "name": "向右箭头", + "font_class": "xiangyoujiantou", + "unicode": "e8de", + "unicode_decimal": 59614 + }, + { + "icon_id": "630095", + "name": "向左箭头", + "font_class": "xiangzuojiantou", + "unicode": "e8df", + "unicode_decimal": 59615 + }, + { + "icon_id": "24600282", + "name": "浏览icon@2x", + "font_class": "a-liulanicon2x", + "unicode": "e8dd", + "unicode_decimal": 59613 + }, + { + "icon_id": "24567893", + "name": "文件icon", + "font_class": "wenjianicon", + "unicode": "e8dc", + "unicode_decimal": 59612 + }, + { + "icon_id": "24527422", + "name": "圆圈@2x", + "font_class": "a-yuanquan2x", + "unicode": "e8db", + "unicode_decimal": 59611 + }, + { + "icon_id": "24378423", + "name": "项目标签", + "font_class": "xiangmubiaoqian", + "unicode": "e8da", + "unicode_decimal": 59610 + }, + { + "icon_id": "24368060", + "name": "icon", + "font_class": "icon", + "unicode": "e8ce", + "unicode_decimal": 59598 + }, + { + "icon_id": "24368061", + "name": "tar", + "font_class": "tar", + "unicode": "e8cf", + "unicode_decimal": 59599 + }, + { + "icon_id": "24289113", + "name": "复制 (2)", + "font_class": "a-fuzhi2", + "unicode": "e8d0", + "unicode_decimal": 59600 + }, + { + "icon_id": "24289114", + "name": "附件", + "font_class": "fujian1", + "unicode": "e8d1", + "unicode_decimal": 59601 + }, + { + "icon_id": "24289115", + "name": "编 辑", + "font_class": "a-bianji1", + "unicode": "e8d2", + "unicode_decimal": 59602 + }, + { + "icon_id": "24289116", + "name": "版本icon", + "font_class": "banbenicon", + "unicode": "e8d3", + "unicode_decimal": 59603 + }, + { + "icon_id": "24289117", + "name": "删除icon", + "font_class": "shanchuicon2", + "unicode": "e8d4", + "unicode_decimal": 59604 + }, + { + "icon_id": "24289118", + "name": "垃圾桶_icon@3x", + "font_class": "a-lajitong_icon3x", + "unicode": "e8d5", + "unicode_decimal": 59605 + }, + { + "icon_id": "24289119", + "name": "下拉按钮", + "font_class": "xialaanniu2", + "unicode": "e8d6", + "unicode_decimal": 59606 + }, + { + "icon_id": "24289120", + "name": "下载-icon", + "font_class": "xiazai-icon", + "unicode": "e8d7", + "unicode_decimal": 59607 + }, + { + "icon_id": "24289121", + "name": "master_icon", + "font_class": "master_icon1", + "unicode": "e8d8", + "unicode_decimal": 59608 + }, + { + "icon_id": "24289122", + "name": "上传icon", + "font_class": "shangchuanicon", + "unicode": "e8d9", + "unicode_decimal": 59609 + }, { "icon_id": "24059956", "name": "个人资料", diff --git a/public/css/iconfont.ttf b/public/css/iconfont.ttf index a0b8934f1..cffaad689 100644 Binary files a/public/css/iconfont.ttf and b/public/css/iconfont.ttf differ diff --git a/public/css/iconfont.woff b/public/css/iconfont.woff index 98e387c01..af2a764e6 100644 Binary files a/public/css/iconfont.woff and b/public/css/iconfont.woff differ diff --git a/public/css/iconfont.woff2 b/public/css/iconfont.woff2 index eb219c96e..7d4cb19bb 100644 Binary files a/public/css/iconfont.woff2 and b/public/css/iconfont.woff2 differ diff --git a/public/js/editormd/editormd.min.js b/public/js/editormd/editormd.min.js index 0add3b370..860a4de0e 100755 --- a/public/js/editormd/editormd.min.js +++ b/public/js/editormd/editormd.min.js @@ -3319,9 +3319,9 @@ text = text.replace(emailReg, function ($1, $2, $3, $4) { return $1.replace(/@/g, "_#_@_#_"); }); - + // " + editormd.urls.atLinkBase + "" + $2 + " text = text.replace(atLinkReg, function ($1, $2) { - return "" + $1 + ""; + return " " + $1 + " "; }).replace(/_#_@_#_/g, "@"); } diff --git a/src/App.js b/src/App.js index 9af51b21e..fad7e9373 100644 --- a/src/App.js +++ b/src/App.js @@ -19,7 +19,7 @@ import moment from 'moment' import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles'; import SiderBar from './forge/Component/SiderBar' -import { SnackbarHOC } from 'educoder' +import { SnackbarHOC } from 'educoder'; import { initAxiosInterceptors } from './AppConfig' import { Provider } from 'react-redux'; import configureStore from './redux/stores/configureStore'; @@ -39,6 +39,11 @@ const Projects = Loadable({ loader: () => import('./forge/Index'), loading: Loading, }) +// forge项目详情 +const ProjectDetail = Loadable({ + loader: () => import("./forge/Main/DetailAdaptor"), + loading: Loading, +}); //forge安全设置 const Security = Loadable({ loader: () => import('./forge/SecuritySetting/Index'), @@ -93,8 +98,13 @@ const ProjectIndex = Loadable({ loading: Loading, }); +// const CreateMerge = Loadable({ +// loader: () => import('./forge/Merge/NewMerge'), +// loading: Loading, +// }) + // 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。 -const keyWord = ["explore", "settings", "setting", "CCF", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search"]; +const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize"]; class App extends Component { constructor(props) { @@ -268,6 +278,13 @@ class App extends Component { } } /> + {/* 项目PR */} + () + } + > + {/*项目*/} ) } }> - : pathType === '404' ? : + : pathType === '404' ? : ( @@ -360,6 +377,7 @@ class App extends Component { } /> // + // } diff --git a/src/college/colleagecss/colleage.css b/src/college/colleagecss/colleage.css index 9220b8acc..65f32322a 100644 --- a/src/college/colleagecss/colleage.css +++ b/src/college/colleagecss/colleage.css @@ -218,7 +218,7 @@ a:hover { } .color-blue { - color: #4CACFF; + color: #2A61FF; } .color-huang { diff --git a/src/common/TextUtil.js b/src/common/TextUtil.js index 94e736314..d1aa0e9cd 100644 --- a/src/common/TextUtil.js +++ b/src/common/TextUtil.js @@ -69,7 +69,7 @@ export function appendFileSizeToUploadFile(item) { } export function appendFileSizeToUploadFileAll(fileList) { return fileList.map(item => { - if (item.name.indexOf(uploadNameSizeSeperator) == -1) { + if (item.name.indexOf(uploadNameSizeSeperator) === -1) { return Object.assign({}, item, { name: `${item.name}${uploadNameSizeSeperator}${bytesToSize(item.size)}` }) } return item diff --git a/src/common/UrlTool.js b/src/common/UrlTool.js index 6c4fcfb61..4c3911cd4 100644 --- a/src/common/UrlTool.js +++ b/src/common/UrlTool.js @@ -24,6 +24,23 @@ export function getImageUrl(path) { return `${path}`; } +export function numFormat(num, digits){ + let d = digits || 1; + var si = [ + { value: 1, symbol: "" }, + { value: 1E3, symbol: "k" }, + { value: 1E4, symbol: "W" } + ]; + var rx = /\.0+$|(\.[0-9]*[1-9])0+$/; + var i; + for (i = si.length - 1; i > 0; i--) { + if (num >= si[i].value) { + break; + } + } + return (num / si[i].value).toFixed(d).replace(rx, "$1") + si[i].symbol; +} + export function getImage(path) { // https://www.educoder.net // https://testbdweb.trustie.net diff --git a/src/common/educoder.js b/src/common/educoder.js index c6eaf176d..1a063d823 100644 --- a/src/common/educoder.js +++ b/src/common/educoder.js @@ -3,7 +3,7 @@ // export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil'; export { - getUploadLogoActionUrl as getUploadLogoActionUrl, + getUploadLogoActionUrl as getUploadLogoActionUrl,numFormat as numFormat, 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 , 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 diff --git a/src/forge/Activity/Activity.js b/src/forge/Activity/Activity.js index 0272f0652..4a7a1d345 100644 --- a/src/forge/Activity/Activity.js +++ b/src/forge/Activity/Activity.js @@ -10,6 +10,10 @@ import ActivityItem from './ActivityItem'; import axios from 'axios'; const LIMIT = 15; const ARRAY = [ + { + id:"", + name:'全部' + }, { id:1, name:'1天' @@ -32,10 +36,15 @@ class Activity extends Component{ constructor(props){ super(props); this.state={ - time:'30', + time:undefined, type:undefined, state:undefined, page:1, + pr_count:undefined, + new_pr_count:undefined, + close_issues_count:undefined, + open_issues_count:undefined, + pr_all_count:undefined,issues_count:undefined, data:undefined, project_trends:undefined, @@ -63,8 +72,15 @@ class Activity extends Component{ this.setState({ data:result.data, project_trends:result.data.project_trends, - isSpin:false + isSpin:false, + pr_count:result.data.pr_count, + new_pr_count:result.data.new_pr_count, + close_issues_count:result.data.close_issues_count, + open_issues_count:result.data.open_issues_count, + pr_all_count:result.data.pr_all_count, + issues_count:result.data.issues_count, }) + window.scrollTo(0,0); } }).catch(error=>{ console.log(error); @@ -74,19 +90,19 @@ class Activity extends Component{ // 切换周期 changeTime=(e)=>{ this.setState({ - time:e.key, + time:e.key ==="item_0"?undefined:e.key, isSpin:true }) const { type,status,page } = this.state; - this.getInfo(e.key,type,status,page); + this.getInfo(e.key ==="item_0"?undefined:e.key,type,status,page); } //筛选 changeTrends=(type,status)=>{ this.setState({ - type,status + type,status,page:1 }) - const {time,page}=this.state; - this.getInfo(time,type,status,page); + const {time}=this.state; + this.getInfo(time,type,status,1); } // 分页 ChangePage=(page)=>{ @@ -108,12 +124,14 @@ class Activity extends Component{ ) render(){ - const { time , data , page , project_trends , isSpin } = this.state; + const { time , data , page , project_trends , isSpin , pr_count , new_pr_count , close_issues_count , open_issues_count , pr_all_count ,issues_count } = this.state; + let name = time ? ARRAY.filter(item=>item.id === parseInt(time)) :[{name:"全部"}]; - let name = time && ARRAY.filter(item=>item.id === parseInt(time)) ; - 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 fourth_per = (parseInt(data && data.open_issues_count)/parseInt(data && data.issues_count)*100)+'%'; + const first_per = pr_all_count > 0 ? `${parseFloat(pr_count/pr_all_count).toFixed(2)*100}%` :"50%"; + const second_per =pr_all_count > 0 ? `${parseFloat(new_pr_count/pr_all_count).toFixed(2)*100}%` :"50%"; + const third_per =issues_count > 0 ?`${parseFloat(close_issues_count/issues_count).toFixed(2)*100}%` :"50%"; + const fourth_per =issues_count > 0 ?`${parseFloat(open_issues_count/issues_count).toFixed(2)*100}%` :"50%"; + return(
@@ -122,7 +140,7 @@ class Activity extends Component{
-

+

{data && data.pr_all_count}合并请求 @@ -132,25 +150,25 @@ class Activity extends Component{

- {data && data.issues_count}任务 + {data && data.issues_count}易修
  • {data && data.pr_count} - this.changeTrends("PullRequest","close")}>已处理的合并请求 + this.changeTrends("PullRequest","delay")}>已处理的合并请求
  • {data && data.new_pr_count} - this.changeTrends("PullRequest","create")}>未处理的合并请求 + this.changeTrends("PullRequest","not_delay")}>未处理的合并请求
  • {data && data.close_issues_count} - this.changeTrends("Issue","close")}>已关闭的任务 + this.changeTrends("Issue","delay")}>已关闭的易修
  • {data && data.open_issues_count} - this.changeTrends("Issue","create")}>未处理的任务 + this.changeTrends("Issue","not_delay")}>未处理的易修
diff --git a/src/forge/Activity/ActivityItem.js b/src/forge/Activity/ActivityItem.js index 2b1ad3c8c..48a72e797 100644 --- a/src/forge/Activity/ActivityItem.js +++ b/src/forge/Activity/ActivityItem.js @@ -27,7 +27,7 @@ class ActivityItem extends Component { : // 如果是合并请求

- {item.name} + {item.name} {item.trend_type}

} diff --git a/src/forge/Branch/CloneAddress.js b/src/forge/Branch/CloneAddress.js index e936bb705..c2e6dd746 100644 --- a/src/forge/Branch/CloneAddress.js +++ b/src/forge/Branch/CloneAddress.js @@ -7,7 +7,7 @@ function CloneAddress({http_url , ssh_url , zip_url , tar_url}) { const [ key , setKey ] = useState("HTTP"); return (
-
+
{setKey(e.key)}}>HTTP {setKey(e.key)}}>SSH diff --git a/src/forge/Branch/Select.jsx b/src/forge/Branch/Select.jsx index fedd288c1..ff3dd829e 100644 --- a/src/forge/Branch/Select.jsx +++ b/src/forge/Branch/Select.jsx @@ -1,116 +1,65 @@ -import React , { useState , useEffect } from 'react'; -import { Popover , Input , Spin } from 'antd'; +import React , { useState , useEffect , useRef } from 'react'; +import { Dropdown} from 'antd'; import './branch.scss'; -import { getBranch , getTag } from '../GetData/getData'; - +import SelectOverlay from './SelectOverlay'; +import { findDOMNode } from 'react-dom'; export default (({ projectsId , branch , owner , changeBranch , branchList , tagflag = true })=>{ const [ showValue , setShowValue ] = useState(branch); - const [ inputValue , setInputValue] = useState(undefined); - const [ nav , setNav ] = useState(0); - const [ isSpin , setIsSpin ] = useState(true); - const [ flag , setFlag ] = useState(false); + const [ visible , setVisible ] = useState(false); - const [ data , setData ] = useState(undefined); - const [ datas , setDatas ] = useState(undefined); + const refFa = useRef(null); + const refBox = useRef(null); + + useEffect(() => { + document.addEventListener('click', clickMe , false); + }, []) + + const clickMe = ({ target }) => { + // 查找父组件 + const faComponent = findDOMNode(refFa.current); + const boxComponent = findDOMNode(refBox.current); + + if (faComponent && boxComponent) { + const isChild = faComponent.contains(target); + const isBox = boxComponent.contains(target); + if(!isChild && !isBox){ + setVisible(false); + } + } + } useEffect(()=>{ setShowValue(branch); },[branch]) - useEffect(()=>{ - document.body.addEventListener('click', e => { - let name = e.target.className; - let turn = name === "ant-input OptionsInput" || name === "navli active"|| name === "navli" || name === "padding10 bor-bottom-greyE"; - if(turn){ - return; - }else{ - setFlag(false); - } - }) - }) - - useEffect(()=>{ - if(branchList){ - setData(branchList); - setDatas(branchList); - setIsSpin(false); - } - },[branchList]) - - - async function getBranchs(id,owner){ - let result = await getBranch(id,owner); - setData(result); - setDatas(result); - setIsSpin(false); + function ChangeB(params) { + setVisible(false); + changeBranch(params); } - async function getTags(id,owner){ - let result = await getTag(id,owner); - setData(result); - setDatas(result); - setIsSpin(false); - } - - function changeInputValue(e){ - setInputValue(e.target.value); - let filter = e.target.value ? data && data.length>0 && data.filter(item=>item.name.indexOf(e.target.value)>-1) : data; - setDatas(filter); - } - - function changeNav(nav){ - setNav(nav); - setIsSpin(true); - if(nav === 0){ - getBranchs(projectsId,owner); - }else{ - getTags(projectsId,owner); - } - } - function chooseitem(value){ - // setShowValue(value); - changeBranch(value); - } - const menu = ( -
-
- -
    -
  • changeNav(0)}>分支列表
  • - { tagflag &&
  • changeNav(1)}>标签列表
  • } -
-
- -
    - { - datas && datas.length>0 ? - datas.map((item,key)=>{ - return( -
  • chooseitem(item.name)}>{item.name}
  • - ) - }): -

    暂无{inputValue}{nav === 0 ?"分支":"标签"}~

    - } -
-
+
+
); return( - setFlag(!flag)} overlayClassName="branch-tagBox-list"> -
+ +
setVisible(visible ? false : true)}> {/* {nav === 0 ?"分支":"标签"} */} - + {showValue} - - + +
- +
) }) \ No newline at end of file diff --git a/src/forge/Branch/SelectOverlay.jsx b/src/forge/Branch/SelectOverlay.jsx new file mode 100644 index 000000000..0be567324 --- /dev/null +++ b/src/forge/Branch/SelectOverlay.jsx @@ -0,0 +1,90 @@ +import React , { useState , useEffect } from 'react'; +import { Input , Spin , Menu } from 'antd'; +import { getBranch , getTag } from '../GetData/getData'; + +function SelectOverlay({ changeBranch , tagflag , projectsId , owner , visible }) { + const [ inputValue , setInputValue] = useState(undefined); + const [ nav , setNav ] = useState(0); + const [ isSpin , setIsSpin ] = useState(true); + + const [ data , setData ] = useState(undefined); + const [ datas , setDatas ] = useState(undefined); + const [ keys ,setKeys] = useState("branch"); + + useEffect(()=>{ + if(visible){ + setKeys("branch"); + getBranchs(projectsId,owner); + setIsSpin(true); + } + },[visible]) + + async function getBranchs(id,owner){ + let result = await getBranch(id,owner); + setData(result); + setDatas(result); + setIsSpin(false); + } + async function getTags(id,owner){ + let result = await getTag(id,owner); + setData(result); + setDatas(result); + setIsSpin(false); + } + function chooseitem(value){ + changeBranch(value); + } + function changeInputValue(e){ + setInputValue(e.target.value); + let filter = e.target.value ? data && data.length>0 && data.filter(item=>item.name.indexOf(e.target.value)>-1) : data; + setDatas(filter); + } + + function changeNav(e){ + setKeys(e.key); + setIsSpin(true); + if(e.key === "branch"){ + getBranchs(projectsId,owner); + setNav(0); + }else{ + getTags(projectsId,owner); + setNav(1); + } + } + + return( +
+
+ } + placeholder={`请输入分支${tagflag ? "或标签" :""}名称搜索`} + autocomplete="off" className="OptionsInput" + value={inputValue} + onChange={changeInputValue} + /> +
+ + 分支 + { tagflag && 标签 } + + +
    + { + datas && datas.length>0 && + datas.map((item,key)=>{ + return( +
  • chooseitem(item.name)}>{item.name}
  • + ) + }) + + } + { + datas && datas.length === 0 && +

    暂无{inputValue}{nav === 0 ?"分支":"标签"}~

    + } +
+
+
+ ) +} +export default SelectOverlay; \ No newline at end of file diff --git a/src/forge/Branch/branch.scss b/src/forge/Branch/branch.scss index 0c7e136a8..030b05cd1 100644 --- a/src/forge/Branch/branch.scss +++ b/src/forge/Branch/branch.scss @@ -23,14 +23,16 @@ max-height: 300px; } .OptionsUl{ + min-height: 50px; max-height: 220px; overflow-y: auto; } .OptionsUl li{ - height: 35px; - line-height: 35px; + height: 30px; + line-height: 30px; cursor: pointer; - padding:0px 10px; + padding:0px 20px; + margin:5px 0px; } .OptionsUl li:hover{ background-color: #F0F0F0; @@ -45,38 +47,56 @@ width: 100%; } .branch-tagBox{ - border:1px solid #eee; + border:1px solid #D0D0D0; border-radius: 3px; - height: 40px; + height: 32px; display: flex; align-items: center; cursor: pointer; - min-width: 140px; + min-width: 104px; } -.branch-tagBox-list .ant-popover-arrow{ - display: none; +.branch-tagBox:hover{ + background-color: #F3F4F6; } -.branch-tagBox-list.ant-popover.ant-popover-placement-bottom{ - padding-top:0px; +.branch-tagBox-list{ + background: #FFFFFF; + box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5); + border-radius: 4px; + .ant-popover-arrow{ + display: none; + } + &.ant-popover.ant-popover-placement-bottom{ + padding-top:0px; + } + .branch-tagBox .ant-dropdown-link{ + display: block; + flex:1; + max-width: 105px; + } + .ant-popover-inner-content{ + padding:0px; + } } -.branch-tagBox .ant-dropdown-link{ - display: block; - flex:1; -} -.branch-tagBox-list .ant-popover-inner-content{ - padding:0px; -} -.navUl{ - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 5px; -} -.navUl li{ - cursor: pointer; -} -.navUl li.active{ - color:#5091FF; +.overlayBranch{ + width: 325px; + .navUl{ + margin-top: 8px; + height: 30px; + line-height: 30px; + li{ + height: 30px; + line-height: 30px; + padding:0px 5px; + margin-left: 20px!important; + &.ant-menu-item-selected{ + border-color:#466aff!important; + color:#466aff!important; + } + &.ant-menu-item-active{ + border-color:transparent ; + } + } + } } .listTips{ padding:20px 0px; @@ -86,6 +106,7 @@ .urlMenu{ line-height: 30px; margin-bottom: 10px; + padding:15px 20px 0px 20px; border-bottom: none; li.ant-menu-item{ height: 30px; @@ -96,7 +117,7 @@ color: #333; } &.ant-menu-item-selected{ - border-color:#1890ff!important; + border-color:#466aff!important; } &.ant-menu-item-active{ border-color:transparent ; diff --git a/src/forge/Component/Component.scss b/src/forge/Component/Component.scss index 3a94c3751..30a62005c 100644 --- a/src/forge/Component/Component.scss +++ b/src/forge/Component/Component.scss @@ -113,7 +113,14 @@ li.ant-menu-item{ z-index: 10000; } .laterest{ - color: #05690d; + background-color: #EF3131; + color: #fff; + font-size: 12px; + margin-left: 10px; + padding:0px 5px; + border-radius: 2px; + height: 18px; + line-height: 18px; } @media screen and (max-width: 1800px){ @@ -155,41 +162,112 @@ li.ant-menu-item{ margin:0px 20px!important; } } - +.hoverA{ + display:flex; + align-items: center; + max-width: 78px; + &:hover a{ + color:#2A61FF !important ; + } +} .menuPanels{ - width: 240px; - height: 180px; + width: 295px; + .leftline{ + position: relative; + color: #666; + height: 16px; + margin-left: 14px; + font-size: 12px; + &::before{ + position: absolute; + left: -7px; + top:3px; + height: 12px; + width: 1px; + background-color: #999; + content: ""; + } + } + .ant-btn{ + height: 36px; + line-height: 34px; + width: 83px; + text-align: center; + padding:0px ; + font-weight: 500; + font-size: 14px; + &.currentBtn{ + cursor: default; + color: #333; + &:hover{ + color: #333; + border-color: #d0d0d0; + } + } + } + .ant-btn-default{ + color: #333; + border-color: #d0d0d0; + &:hover{ + background: #F3F4F6; + } + } + .ant-btn{ + width: 102px; + height: 32px; + line-height: 30px; + } + .ant-btn-primary{ + color: #fff; + background-color: #466AFF; + border:none; + &:hover{ + background-color: rgba(70,106,255,0.85); + } + } + .focusPanelHeadInfo{ + padding:14px 16px; + border-bottom: 1px solid #eee; + } .ant-popover-content,.ant-popover-inner{ height: 100%; width: 100%; } + .ant-popover-inner-content{ + padding:0px; + } } .halfs{ margin-top: 24px; padding:24px 0px 0px 0px; border-top: 1px solid #e8e8e8; - .attrPerson{ - padding-bottom: 24px; - } +} +.aboutSubTitle{ + display: flex; + align-items: center; +} +.menuMaininfos{ + padding:10px 16px 14px; + border-bottom: 1px solid #eee; } .menuinfos{ - padding:15px 0px; + padding:10px 20px 16px; &>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; + font-size: 16px; + font-weight: 500; color: #333; + line-height: 22px; } & >span:last-child{ color: #666; - } - &:last-child{ - border-right: none; + font-weight: 400; + line-height: 20px; + margin-top: 6px; } } } diff --git a/src/forge/Component/Contributors.jsx b/src/forge/Component/Contributors.jsx index 2f67b3ab5..e88acc056 100644 --- a/src/forge/Component/Contributors.jsx +++ b/src/forge/Component/Contributors.jsx @@ -1,13 +1,13 @@ import React, { useEffect, useState } from 'react'; import { AlignCenter , FlexAJ } from '../Component/layout'; import { Link } from 'react-router-dom'; -import { Popover , Spin } from 'antd'; +import { Popover , Spin , Button } from 'antd'; import { getImageUrl } from 'educoder'; import './Component.scss'; import { getUser } from '../GetData/getData'; import axios from 'axios'; -function Contributors({contributors,owner,projectsId}){ +function Contributors({contributors,owner,projectsId,currentLogin}){ const [ menuList ,setMenuList ]= useState([]); const [ list , setList ]= useState(undefined); const [ total , setTotal ]= useState(0); @@ -46,46 +46,60 @@ function Contributors({contributors,owner,projectsId}){ } } + function renderOrganize(list) { + let str = ""; + list.map(i=>{ + str = str+i.name + "、"; + }) + return str && str.substr(0,str.length - 1); + } + function setMenusFunc(data){ if(data){ let ele = ( - + - {data.name} - - { - data.is_watch ? FocusFunc(false,data.login)}>取消关注:FocusFunc(true,data.login)}>关注 - } - - - - {data.projects_count} - 项目数 - - - {data.followers_count} - 粉丝数 - - - {data.following_count} - 关注数 - - - { - data.organizations && data.organizations.length > 0 ? - - 所属组织: -
- {renderArray(data.organizations)} + +
+ {data.name} + { data.location && {data.location} } + { + data.organizations && data.organizations.length>0&& +

+ 所属组织:{renderOrganize(data.organizations)} +

+ }
- :"" - } - { - data.location && 所在地址:{data.location} - } + + + + {data.projects_count} + 项目数 + + + {data.followers_count} + 粉丝数 + + + {data.following_count} + 关注数 + + +
+ { + currentLogin && (currentLogin === data.login) + ? + + : + data.is_watch ? + + : + + } +
) setMenu(ele); @@ -135,10 +149,10 @@ function Contributors({contributors,owner,projectsId}){ return(
- - 贡献者{ contributors && contributors.total_count > 0 && {contributors.total_count}} - 全部 - + + 贡献者 + { contributors && contributors.total_count > 0 && {contributors.total_count}} +
setVisibleFunc(false)}> { total > 0 ? diff --git a/src/forge/Component/CopyTool.jsx b/src/forge/Component/CopyTool.jsx index 86fb32dab..36ed1d8c1 100644 --- a/src/forge/Component/CopyTool.jsx +++ b/src/forge/Component/CopyTool.jsx @@ -2,7 +2,7 @@ import React, { useState, useCallback, memo } from 'react'; import { Tooltip } from 'antd'; CopyTool.defaultProps = { - beforeText: '复制', //浮动过去显示的文字 + beforeText: '复制链接', //浮动过去显示的文字 afterText: '复制成功', //点击后显示的文字 className: '', //传给svg的class inputId: 'copyText', //要复制的文本的ID @@ -26,6 +26,7 @@ function CopyTool({ beforeText, afterText, className , inputId , timeOut }) { if (document.execCommand('copy')) { document.execCommand('copy'); } + document.getSelection().removeAllRanges(); setTitle(afterText); if(timeOut){ diff --git a/src/forge/Component/LanguagePower.jsx b/src/forge/Component/LanguagePower.jsx index 3197309c4..8d3e361b5 100644 --- a/src/forge/Component/LanguagePower.jsx +++ b/src/forge/Component/LanguagePower.jsx @@ -25,7 +25,7 @@ function LanguagePower({languages}){ } return(
-

开发语言

+

开发语言

{ array && array.map((item,key)=>{ diff --git a/src/forge/Component/ModalFun/index.jsx b/src/forge/Component/ModalFun/index.jsx new file mode 100644 index 000000000..8fee2c73e --- /dev/null +++ b/src/forge/Component/ModalFun/index.jsx @@ -0,0 +1,125 @@ +/* eslint-disable react/jsx-no-duplicate-props */ +import React, { useState } from 'react'; +import * as ReactDOM from 'react-dom'; +import { Modal, Button } from 'antd'; +import './index.scss'; + +// 函数式调用删除、通知等模态框 + +InitModal.defaultProps = { + okText: '确认', //确定按钮的文字 + cancelText: '取消', //取消按钮的文字 + className: '', //传入的模态框类名 + inputId: 'copyText', //要复制的文本的ID + onCancel:()=>{}, //取消的回调 + onOk:()=>{}, //确认的回调 + title:'提示', //模态框名字 + contentTitle:'', //内容标题 + content:'', //详细内容 + afterClose:()=>{}, //关闭模态框以后的回调 +}; + +// 使用函数调用删除组件 +export default function DelModal(props) { + renderModal({ ...props, type: 'delete' }) +} + +// 使用函数调用选择模态框组件 +export function Confirm(props) { + renderModal({ ...props, type: 'confirm' }) +} + +function renderModal(props) { + const { type, afterClose } = props; + const div = document.createElement('div'); + document.body.appendChild(div); + + function destroy() { + afterClose && afterClose(); + const unmountResult = ReactDOM.unmountComponentAtNode(div); + if (unmountResult && div.parentNode) { + div.parentNode.removeChild(div); + } + } + + function modalType(type) { + if (type === 'delete') { + return + + {props.contentTitle} + } + /> + } else if (type === 'confirm') { + return + } else { + return + } + } + + function render() { + setTimeout(() => { + ReactDOM.render( + modalType(type), + div, + ); + }); + } + render(); +} + +// 选择模态框组件 +function InitModal({ + onCancel, + onOk, + title, + contentTitle, + content, + okText, + cancelText, + afterClose, + className, +}) { + + const [visible, setVisible] = useState(true); + + function onCancelModal() { + setVisible(false); + onCancel && onCancel() + } + + function onSuccess() { + setVisible(false); + onOk && onOk(); + } + + return ( + + {cancelText} + , + , + ]} + > +
+ {contentTitle &&

{contentTitle}

} +

{content}

+
+
+ ) +} diff --git a/src/forge/Component/ModalFun/index.scss b/src/forge/Component/ModalFun/index.scss new file mode 100644 index 000000000..24f7b0219 --- /dev/null +++ b/src/forge/Component/ModalFun/index.scss @@ -0,0 +1,63 @@ +.myself-modal { + .ant-modal-header { + padding: 9px 24px; + background: #f8f8f8; + border-bottom: 1px solid #eee; + } + .ant-modal-title { + text-align: left; + } + .ant-modal-close { + top: 0px !important; + } + .ant-modal-close-x { + font-size: 24px; + } + .ant-modal-body { + text-align: center; + } + .content-title { + display: flex; + justify-content: center; + align-items: center; + margin: 2rem 0 1rem !important; + font-size: 16px; + color: #333; + letter-spacing: 0; + line-height: 29px; + font-weight: 400; + } + .red-circle { + align-self: flex-start; + color: #ca0002; + font-size: 1.5rem !important; + } + .content-descibe { + font-size: 14px; + color: #666; + line-height: 33px; + font-weight: 400; + } + .ant-modal-footer { + padding: 2rem 0; + text-align: center; + border: 0; + .ant-btn { + width: 6rem; + } + } + .foot-submit { + margin-left: 3rem; + color: #df0002; + &:hover { + border-color: #df0002; + } + } + .ant-btn-default:hover, + .ant-btn-default:active, + .ant-btn-default:focus { + background: #f3f4f6; + color: #333; + border-color: #d0d0d0; + } +} \ No newline at end of file diff --git a/src/forge/Component/NoticeModal/Index.scss b/src/forge/Component/NoticeModal/Index.scss new file mode 100644 index 000000000..18e4dcda0 --- /dev/null +++ b/src/forge/Component/NoticeModal/Index.scss @@ -0,0 +1,90 @@ +.systemBox{ + .ant-modal-body{ + padding:1px 0px 0px 0px; + .sysBox{ + background-image: url('./bg.png'); + background-repeat: no-repeat; + background-size: 100% 334px; + margin-top: -55px; + } + .sysnoticeBox{ + width: 100%; + padding:80px 0px 34px; + display: flex; + flex-direction: column; + width: 780px; + margin: 0px auto; + p.ntitle{ + height: 33px; + font-size: 24px; + font-weight: 500; + color: #31FFF7; + line-height: 33px; + text-align: center; + } + p.nSubtitle{ + height: 25px; + line-height: 25px; + font-size: 18px; + font-weight: 500; + color: #FFFFFF; + margin-top: 60px; + padding-left: 20px; + } + .markdown-body{ + box-shadow: 0px 0px 17px rgba(0,0,0,0.2); + border-radius: 4px; + margin-top: 17px!important; + } + .nContent{ + padding:20px 34px; + background-color: #fff; + line-height: 30px; + font-size: 15px; + font-weight: 400; + color: #333; + .realmName{ + margin-top: 20px; + display: flex; + ul{ + width: 50%; + padding-left: 0px!important; + li{ + font-size: 15px; + font-weight: 500; + line-height: 32px; + text-align: left; + color: #000; + list-style-type: none!important; + &:first-child{ + color: #E65714; + } + } + } + } + .nSubdesc{ + font-size: 15px; + font-weight: 400; + color: #000000; + line-height: 31px; + margin-top: 20px; + } + .nInfo{ + font-size: 14px; + font-weight: 400; + color: #333333; + text-align: right; + margin-top: 25px; + p{ + height: 20px; + line-height: 20px; + } + } + } + .nBtn{ + text-align: center; + margin-top: 33px; + } + } + } +} \ No newline at end of file diff --git a/src/forge/Component/NoticeModal/SystemNotice.jsx b/src/forge/Component/NoticeModal/SystemNotice.jsx new file mode 100644 index 000000000..1560c1ffc --- /dev/null +++ b/src/forge/Component/NoticeModal/SystemNotice.jsx @@ -0,0 +1,76 @@ +import React , { useEffect , useState } from 'react'; +import { Modal , Button } from 'antd'; +import './Index.scss'; +import '../../css/index.scss'; +import RenderHtml from '../../../components/render-html'; +import cookie from 'react-cookies'; + +function SystemNotice({system_notification,history}){ + const [ visible , setVisible ] = useState(false); + + useEffect(()=>{ + if(system_notification && !cookie.load('notice_stage')){ + setVisible(true); + } + },[system_notification,history.location]) + + function sureContinue() { + cookie.remove('notice_stage'); + + let inFifteenMinutes = new Date(new Date().getTime() + 24 * 3600 * 1000);//一天 + // let inFifteenMinutes = new Date(new Date().getTime() + 60 * 1000);//一分钟 + cookie.save('notice_stage', true,{ expires: inFifteenMinutes,path:"/" }); + + setVisible(false); + } + + return ( + +
+
+

{system_notification && system_notification.subject}

+

{system_notification && system_notification.sub_subject}

+ {/*
+
+ 为了给用户提供更加稳定、优质的服务,我们即将对平台门户首页、平台名称、平台域名进行一次全面升级与变更。原平台名称:Trustie(中文名:确实)将于2021年10月xx日统一更改为Gitlink(中文名:确实开源)。届时平台域名将统一进行更换,更换规则如下 +
+
+
    +
  • 原域名:
  • +
  • 官网顶级域名https://www.trustie.net
  • +
  • 版本库子域名https://forgeplus.trustie.net
  • +
  • 论坛子域名https://forum.trustie.net/forums
  • +
+
    +
  • 更换后域名:
  • +
  • 官网顶级域名https://www.gitlink.org.cn
  • +
  • 版本库子域名https://www.git.gitlink.org.cn
  • +
  • 论坛子域名https://forum.gitlink.org.cn
  • +
+
+
+ 自2021年10月xx日起,旧域名将停止访问。因平台名称与域名变更给您带来的不便,我们深表歉意!非常感谢您一直以来对本平台的信任与支持,我们将一如既往地为您提供优质的服务。 特此通知! +
+
+

Gitlink运营团队

+

2021年10月xx日

+
+
*/} + +
+ +
+
+
+
+ ) +} +export default SystemNotice; \ No newline at end of file diff --git a/src/forge/Component/NoticeModal/bg.png b/src/forge/Component/NoticeModal/bg.png new file mode 100644 index 000000000..625b3c311 Binary files /dev/null and b/src/forge/Component/NoticeModal/bg.png differ diff --git a/src/forge/Component/Releases.jsx b/src/forge/Component/Releases.jsx index bf8caebd4..260e6bd2d 100644 --- a/src/forge/Component/Releases.jsx +++ b/src/forge/Component/Releases.jsx @@ -1,38 +1,35 @@ import React from 'react'; -import { AlignCenter , AlignTop , FlexAJ } from '../Component/layout'; +import { AlignTop } from '../Component/layout'; import { Link } from 'react-router-dom'; -function Releases({owner,projectsId,releaseVersions , baseOperate , projectType}){ - +function Releases({ owner, projectsId, releaseVersions, distribution }) { return(
- - 发行版 - { releaseVersions && releaseVersions.total_count > 0 && {releaseVersions.total_count}} - - { (releaseVersions && releaseVersions.total_count > 0) || projectType ===2 ? - 全部 - : - baseOperate && 新建 - } - + + 发行版 + { releaseVersions && releaseVersions.total_count > 0 && {releaseVersions.total_count}} + { releaseVersions && releaseVersions.total_count>0 ? releaseVersions.list.map((item,key)=>{ return( key === 0 && -
-

- {item.name} +

+ {/* 如果是点击最新则发行版列表页只展示最新的一个 */} + {item.name} 最新

-

{item.created_at}

+

{item.created_at}

) }) - :"" + : +
+ 您暂未发布任何版本 + {distribution && 创建新版本} +
}
diff --git a/src/forge/Component/Sort.jsx b/src/forge/Component/Sort.jsx index 58f8a5d2f..a15f04da9 100644 --- a/src/forge/Component/Sort.jsx +++ b/src/forge/Component/Sort.jsx @@ -2,9 +2,9 @@ import React from 'react'; import {Popover} from 'antd'; import './Component.scss'; -export default (({menu , children})=>{ +export default (({menu , children, overlayClassName})=>{ return( - + {children} ) diff --git a/src/forge/Component/User.jsx b/src/forge/Component/User.jsx index bc652dcfb..0d970173f 100644 --- a/src/forge/Component/User.jsx +++ b/src/forge/Component/User.jsx @@ -5,6 +5,7 @@ import { Link } from 'react-router-dom'; export default ({ url , name , column , id , login })=>{ const Img = styled.span` display:flex; + font-weight: bold; ${column && "flex-direction: column;text-align:center;"} align-items: center; & img{ diff --git a/src/forge/DevOps/Dispose/PipelineName.jsx b/src/forge/DevOps/Dispose/PipelineName.jsx index 4acaa2068..63ee54378 100644 --- a/src/forge/DevOps/Dispose/PipelineName.jsx +++ b/src/forge/DevOps/Dispose/PipelineName.jsx @@ -51,7 +51,7 @@ function PipelineName({visible,onCancel,onOk,value ,branchList}){ }) } - {setEventValue(e)}}> { EVENT.map((item,key)=>{ return( diff --git a/src/forge/DevOps/ssh/XmlPanel.jsx b/src/forge/DevOps/ssh/XmlPanel.jsx index ae642caa3..295d96ce0 100644 --- a/src/forge/DevOps/ssh/XmlPanel.jsx +++ b/src/forge/DevOps/ssh/XmlPanel.jsx @@ -24,7 +24,6 @@ function onLayout(term, el) { entry.target.offsetHeight, term, ); - console.log('cols, rows', cols, rows); term.resize(cols, rows); mediator.publish('ssh-xterm-resize', { columns: cols, @@ -139,12 +138,10 @@ export default ({ sshConfigData, sid }) => { }, 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); }; diff --git a/src/forge/Divert/DivertModal.jsx b/src/forge/Divert/DivertModal.jsx index 6763c5778..a82cf5df0 100644 --- a/src/forge/Divert/DivertModal.jsx +++ b/src/forge/Divert/DivertModal.jsx @@ -44,7 +44,6 @@ function DivertModal({form , visible , onSuccess , onCancel,owner,repo}){ }else{ onSuccess(); } - }).catch(error=>{}) } }) diff --git a/src/forge/Head/AppPullRefresh.jsx b/src/forge/Head/AppPullRefresh.jsx new file mode 100644 index 000000000..2b9c76606 --- /dev/null +++ b/src/forge/Head/AppPullRefresh.jsx @@ -0,0 +1,108 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Icon } from 'antd'; +import _ from 'lodash'; +import Nodata from '../Nodata'; + + +class PullRefresh extends Component { + constructor(props) { + super(props); + this.state = { + } + this.pullRef = {}; + // 节流 + this.onScrollList = _.throttle(this.handleScroll, 200, { + leading: false, + trailing: true, + }); + } + + componentDidMount() { + let dom = document.querySelector('.pull-refresh-wrap'); + dom && dom.addEventListener('scroll', this.onScrollList); + } + + componentWillUnmount() { + let dom = document.querySelector('.pull-refresh-wrap'); + dom && dom.removeEventListener('scroll', this.onScrollList) + } + + + handleScroll = () => { + if (this.props.count < this.props.pageSize) return; + if (this.props.type === 1 || this.props.type === 2) return; + const wrap = this.pullRef; + const currentScroll = wrap.scrollTop + wrap.clientHeight + + // 触底 + if (currentScroll >= (wrap.scrollHeight - 200)) { + this.loadData() + } + } + + + handleLoadClick = () => { + this.loadData(); + } + + loadData = () => { + this.props.onPullRefresh() + } + + + renderLoading() { + switch (this.props.type) { + case 0: // 加载更多 + return
显示更多
+ case 1: // 加载中 + return ( +
+ + 加载中... +
+ ) + case 2: // 无样式 + return
没有更多了
+ default: + return
没有更多了
+ } + } + + + render() { + const { className, count, children } = this.props; + return ( +
{ this.pullRef = dom }} + > + + {children} + + { + count < 1 && + } + + {/* 大于分页数据才显示loading */} + {/* {this.props.count >= this.props.pageSize ? this.renderLoading() : null} */} + +
+ + ) + } + +} + + +PullRefresh.propTypes = { + className: PropTypes.string, + children: PropTypes.any, + onPullRefresh: PropTypes.func.isRequired, + type: PropTypes.oneOf([0, 1, 2]), + count: PropTypes.number.isRequired, + pageSize: PropTypes.number.isRequired, +} + + +export default PullRefresh diff --git a/src/forge/Head/Header.js b/src/forge/Head/Header.js index 870e5888a..f0bfe38e6 100644 --- a/src/forge/Head/Header.js +++ b/src/forge/Head/Header.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import AccountProfile from "../../modules/user/AccountProfile"; import { getImageUrl } from 'educoder' import axios from 'axios'; -import { Input , notification , Dropdown , Menu } from 'antd'; +import { Input , notification , Dropdown ,Popover, Menu,Badge, Button } from 'antd'; import { Link } from 'react-router-dom'; import LoginDialog from '../../modules/login/LoginDialog'; @@ -13,6 +13,7 @@ import '../../modules/tpm/TPMIndex.css'; import CheckProfile from '../Component/ProfileModal/Profile'; import './header.scss'; +import NoticeContent from './NoticeContent'; const $ = window.$ // TODO 这部分脚本从公共脚本中直接调用 const { Search } = Input; @@ -47,6 +48,7 @@ class NewHeader extends Component { settings: null, visiblemyss: false, openSearch:false, + visible:false, //浮动消息框展示控制 } } componentDidMount() { @@ -122,9 +124,6 @@ class NewHeader extends Component { this.setState({ user: newProps.user }) - if (newProps.Headertop !== undefined) { - old_url = newProps.Headertop.old_url - } } submitsubmitapplications = () => { @@ -171,7 +170,6 @@ class NewHeader extends Component { }) }; HideAddcoursestypess = (i) => { - console.log("调用了"); this.setState({ Addcoursestypes: false, mydisplay: true, @@ -323,6 +321,7 @@ class NewHeader extends Component {
) } + renderMenu=(personal)=>{ const { current_user } = this.props; return( @@ -337,14 +336,18 @@ class NewHeader extends Component { ) }) } -
  • 设置
  • + {/*
  • 设置
  • */} this.educoderloginysl()}>退出
    ) } + handleVisibleChange = visible => { + this.setState({ visible }); + }; + render() { - const { match} = this.props; + const { match ,resetUserInfo ,showNotification} = this.props; let current_user = this.props.user; let { Addcoursestypes, tojoinitemtype, @@ -357,6 +360,7 @@ class NewHeader extends Component { headtypesonClickbool, headtypess, settings, + visible, } = this.state; /*用户名称 用户头像url*/ let activeIndex = false; @@ -435,7 +439,6 @@ class NewHeader extends Component { } let search_url = settings && settings.common && settings.common.search; - let notice_url = settings && settings.common && settings.common.notice; return (
    @@ -467,7 +470,7 @@ class NewHeader extends Component { { settings.navbar && settings.navbar.map((item, key) => { var new_link = item.link; - var user_login = this.props.user && this.props.user.login; + var user_login = current_user && current_user.login; var is_hidden = item.hidden if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) { if (user_login) { @@ -498,25 +501,30 @@ class NewHeader extends Component { }
    - {/* {search_url ? this.SearchInput(openSearch,search_url):""} */} { search_url && } { current_user && (current_user.main_site || current_user.login) && (settings && settings.add && settings.add.length>0)? - + :"" } - {this.props.user && this.props.user.login && notice_url ? -
    - {user && user.login && - - - - - - } -
    :"" + { (settings && settings.common && settings.common.notice) && (current_user && current_user.login)? + } + visible={visible} + onVisibleChange={this.handleVisibleChange} + destroyTooltipOnHide + > + + {current_user && + + } + + + : "" }
    {!user || (user && !user.login) ? diff --git a/src/forge/Head/NoticeContent.jsx b/src/forge/Head/NoticeContent.jsx new file mode 100644 index 000000000..634a7adc8 --- /dev/null +++ b/src/forge/Head/NoticeContent.jsx @@ -0,0 +1,262 @@ +import React, { useEffect, useState } from 'react'; +import { Badge, Menu } from 'antd'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; +import AppPullRefresh from './AppPullRefresh'; +import { noticeSourceType } from '../common/static'; +import './header.scss'; +import '../SecuritySetting/notice/manager/Index.scss'; +import '../SecuritySetting/Index.scss'; +import '../SecuritySetting/notice/myNotice/Index.scss'; + + +function NoticeContent({ visible, showNotification, resetUserInfo, current_user: { login } }) { + const [initialize, setInitialize] = useState(true); + const [noticeType, setNoticeType] = useState("notification"); + const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量 + + const [noticeUnreadCount, setNoticeUnreadCount] = useState(0);//未读系统通知数量 + const [noticePage, setNoticePage] = useState(0); + const [noticeUnreadList, setNoticeUnreadList] = useState([]);//未读系统通知列表 + + const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量 + const [atPage, setAtPage] = useState(0); + const [atUnreadList, setAtUnreadList] = useState([]);//未读@我列表 + + useEffect(() => { + resetUserInfo(); + }, [noticeUnreadCount,atUnreadCount]); + + useEffect(()=>{ + setNoticePage(0); + setAtPage(0); + },[visible]) + + useEffect(() => { + const params = { + type: noticeType, + limit: 10, + page: noticeType === "notification" ? noticePage : noticeType === "atme" ? atPage : "", + status: 1, + } + getMessageList(params); + }, [noticePage, atPage]); + + useEffect(() => { + const params = { + type: noticeType, + limit: 10, + page: 0, + status: 1, + }; + if (initialize) { + params.type = "atme" + } + visible && getMessageList(params); + }, [visible]); + + + function getMessageList(params) { + axios.get(`/users/${login}/messages.json`, { + params: params, + }).then((response) => { + if (response && response.data) { + setNoticeUnreadCount(response.data.unread_notification); + setAtUnreadCount(response.data.unread_atme); + if (params.type === "notification") { + let list = response.data.messages; + if (params.page !== 0) { + list = [...noticeUnreadList, ...list]; + } + setNoticeUnreadList(list); + if (initialize) { + // 如果是第一次加载,根据数据量判断是否切换tab栏 + setInitialize(false); + if (response.data.unread_notification === 0 && response.data.unread_atme !== 0) { + setNoticeType("atme"); + } + } + } else if (params.type === "atme") { + let list = response.data.messages; + if (params.page !== 0) { + list = [...atUnreadList, ...list]; + } + setAtUnreadList(list); + } + } + }) + } + + function readAll() { + axios.post(`/users/${login}/messages/read.json`, { + type: noticeType, + ids: [-1] + }).then((response) => { + let data = response.data; + if (!data) return; + if (data.status === 0) { + changeReadMarkAll(noticeType); + } else { + showNotification(data.message); + } + }); + } + + + function changeReadMarkAll(noticeType) { + if (noticeType === "notification") { + let list = noticeUnreadList.slice(); + list.forEach(item => { + item.status = 2; + }) + setNoticeUnreadList(list); + setNoticeUnreadCount(0); + } else if (noticeType === "atme") { + let list = atUnreadList.slice(); + list.forEach(item => { + item.status = 2; + }) + setAtUnreadList(list); + setAtUnreadCount(0); + } + } + + // const [letter_unread_list, setLetter_unread_list] = useState([ + // { + // id: 122, + // read: 0, //是否已读,0未读,1已读 + // send_name: "蒋宇航", //消息发送人 + // send_login: "jiangYuHang", //消息发送人的login,前端根据这个跳转到消息内页 + // content: "私信内容", //最近一条未读消息的内容 + // create_time: "2019-03-04 18:08", //发送时间 + // }, + // ]); + + function readItem(item) { + axios.post(`/users/${login}/messages/read.json`, { + type: noticeType, + ids: [item.id] + }).then((response) => { + let data = response.data; + if (!data) return; + if (data.status === 0) { + changeReadMark(item); + item.notification_url && window.open(item.notification_url); + } else { + showNotification(data.message); + } + }); + } + + + function changeReadMark(item) { + if (item.type === "notification") { + let list = noticeUnreadList.slice(); + let index = noticeUnreadList.indexOf(item); + list[index].status = 2; + setNoticeUnreadList(list); + if (noticeUnreadCount > 0) { + setNoticeUnreadCount(noticeUnreadCount - 1); + } + } else if (item.type === "atme") { + let list = atUnreadList.slice(); + let index = atUnreadList.indexOf(item); + list[index].status = 2; + setAtUnreadList(list); + if (atUnreadCount > 0) { + setAtUnreadCount(atUnreadCount - 1); + } + } + } + + return ( +
    +
    + setNoticeType(e.key)}> + 系统通知 + {/* 私信 */} + @我 + +
    + + {/* 系统通知 */} + {noticeType === "notification" && { setNoticePage(noticePage + 1); }} //触发加载ajax的function + // type={2} // 传送加载组件的状态 + count={noticeUnreadList.length} // 数据当前的总数量 + pageSize={10} // + > + { + noticeUnreadList.map(item => { + return ( +
    { readItem(item) }}> +
    + + + + +
    + + {item.time_ago} +
    +
    +
    + ) + }) + } +
    + } + + {/* @我 */} + {noticeType === "atme" && { setAtPage(atPage + 1); }} //触发加载ajax的function + // type={1} // 传送加载组件的状态 + count={atUnreadList.length} // 数据当前的总数量 + pageSize={10} // + > + {atUnreadList.map(item => { + return ( +
    { readItem(item) }}> +
    + + + +
    + " + (item.sender ? item.sender.name : '') + "   " + item.content + " 中@我" }}> + {item.time_ago} +
    +
    +
    + ) + }) + } +
    + } + + {/* 私信 */} + {/* {noticeType === "1" ? letter_unread_list.length > 0 ? letter_unread_list.map(item => { + return ( +
    +
    = 30 && item.content.length <= 34 ? '65px' : "" }}> + +
    + {item.send_name}: + = 50 ? item.content.substr(0, 50) + "..." : item.content }}> + {item.create_time ? timeAgo(item.create_time) : "刚刚"} +
    +
    +
    + ) + }) : "暂无数据" : ""} */} +
    + 全部消息 + {noticeUnreadCount > 0 && noticeType === "notification" && 所有系统消息一键已读} + {atUnreadCount > 0 && noticeType === "atme" && 所有@我一键已读} +
    +
    + + ) +} +export default NoticeContent; diff --git a/src/forge/Head/header.scss b/src/forge/Head/header.scss index 6c696fd91..263cd1ee5 100644 --- a/src/forge/Head/header.scss +++ b/src/forge/Head/header.scss @@ -24,8 +24,9 @@ width: 34px; height: 34px; border-radius: 50%; - margin-left: 30px; + margin-left: 15px; } + .currentMenu{ width: 120px; text-align: center; @@ -128,54 +129,137 @@ } } -.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; +// 右上角小铃铛单独样式 +.notice-popover{ + //popover小尖尖 + .ant-popover-arrow{ + display: none; } - .footerInfos{ + + //popover框 + .ant-popover-inner-content { + width: 386px; + height: 446px; + box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5); + border-radius: 4px; + margin-top: -10px; + padding: 12px 1px 12px 0; + } +} + +.messageHoverDiv .ant-menu-item{ + margin-right: 24px !important; +} + +.hoverNotice-head{ + margin-left: 18px; + + & .ant-badge{ + font-size: 14px !important; + } + + &>.ant-menu-horizontal { + border-bottom: 1px solid #e8e8e8 !important; + } +} + +.hoverNotice-body{ + height: 342px; + overflow-y: scroll; + + & b{ + font-weight: 400; + text-shadow: 0.5px 0 0 #333; + } + + .none_panels{ + height: 100%; + } +} + +.message-icon{ + position: relative; + .ant-scroll-number{ + right:12px; + padding: 0 0px; + } +} + + + +.hoverNotice-buttom{ + display: flex; + justify-content: space-between; + padding: 12px 18px; + a{ + color: #466AFF; + &:hover{ + opacity:0.85; + } + } +} + +.noticeCont-back{ + .pointer{ + cursor: pointer; + } + &:hover{ + background: #F3F4F6; + } +} + +.noticeCont{ + display: flex; + margin: 0 16px 0 18px; + padding: 12px 0 10px 0; + line-height: 24px; + border-bottom: 1px solid #EEEEEE; + cursor: default; + i{ + font-size: 14px !important; + margin-right: 6px; + color: #333333; + } + + .boldSpan{ + font-weight: 400; + text-shadow: 0.5px 0 0 #333; + } + + .noticeCont-text{ 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; - } + color:#333333; + flex:auto; + justify-content: space-between; + + & .content-span{ + word-break: break-all; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + } + + & .atme-cont-span{ + width: 272px; + } + + & .notice-cont-span{ + width: 255px; + } + + .timeSpan{ + font-size: 12px; + color: #666666; + } + + .at-name{ + margin-right: 12px; } } +} + +.text-center{ + text-align: center; } \ No newline at end of file diff --git a/src/forge/Main/CoderDepot.jsx b/src/forge/Main/CoderDepot.jsx index 80012663b..f50c134a3 100644 --- a/src/forge/Main/CoderDepot.jsx +++ b/src/forge/Main/CoderDepot.jsx @@ -1,8 +1,9 @@ import React , { useEffect , useState } from 'react'; import { WhiteBack , Box , LongWidth , ShortWidth , Gap , AlignCenter , FlexAJ } from '../Component/layout'; -import { Dropdown , Menu , Divider , Spin, Button } from 'antd'; +import { Dropdown , Menu , Divider , Spin, Button , Typography } from 'antd'; import { getImageUrl } from "educoder"; import { Link } from 'react-router-dom'; +import { truncateCommitId } from "../common/util"; import CloneAddress from '../Branch/CloneAddress'; import SelectBranch from '../Branch/Select'; @@ -21,10 +22,11 @@ import UpdateDescModal from './sub/UpdateDescModal'; import Nodata from '../Nodata'; import Invite from './sub/Invite'; import CheckProfile from '../Component/ProfileModal/Profile'; +import RenderHtml from '../../components/render-html'; /** * projectDetail.type:0是托管项目,1是镜像项目,2是同步镜像项目(为2时不支持在线创建、在线上传、在线修改、在线删除、创建合并请求等功能) */ - + const { Paragraph } = Typography; function turnbar(str){ if(str && str.length>0 && str.indexOf("/")>-1){ return str.replaceAll('/','%2F'); @@ -66,6 +68,7 @@ function CoderDepot(props){ const [ editReadme , setEditReadme ] = useState(false); const [ pullsFlag , setPullsFlag ] = useState(true); const [ issuesFlag , setIssuesFlag ] = useState(true); + const [ releaseVersions , setReleaseVersions] = useState(undefined); const owner = props.match.params.owner; const projectsId = props.match.params.projectsId; @@ -73,7 +76,8 @@ function CoderDepot(props){ branchName = returnbar(branchName); const details = props.projectDetail; let pathname = props.history.location.pathname; - + //distribution:判断此用户是否可以创建发行版 + const distribution = details && details.type != 2 && (details.permission === "Admin" || details.permission === "Owner" || details.permission === "Manager"); const { bannerList } = props; useEffect(()=>{ @@ -118,14 +122,47 @@ function CoderDepot(props){ setTreeValue(url); getFileInfo(url,branchName); setType("file"); + // getReadmeInfo(url,branchName); + // setReadme(undefined); }else{ setTreeValue(undefined); getDirInfo(branchName || defaultBranch); setType("dir"); + // getReadmeInfo('', branchName || defaultBranch); } } },[projectsId,owner,pathname,defaultBranch]) + useEffect(()=>{ + axios.get(`/${owner}/${projectsId}/releases.json`).then((result)=>{ + if(result && result.data){ + const release = { + "list":result.data.releases, + "total_count":result.data.releases.length + } + setReleaseVersions(release); + } + }) + },[]) + + // 获取readme信息 + function getReadmeInfo(path, ref) { + axios.get(`/${owner}/${projectsId}/readme.json`, { + params:{ + owner: owner, + repo: projectsId, + filepath:path, + ref:ref || branchName + } + }).then((result) => { + if (result) { + setReadme(result.data); + } else { + setReadme(undefined); + } + }) + } + // 获取主目录列表 function getDirInfo(branch){ setIsSpin(true); @@ -145,9 +182,10 @@ function CoderDepot(props){ setLastCommitAuthor(c && c.committer); setMainFlag(true); setReadOnly(true); - setReadme(result.data.readme); + // setReadme(result.data.readme); setEditReadme(false); setHide(true); + getReadmeInfo('', branchName || defaultBranch); } setTimeout(function(){setIsSpin(false);},500); }).catch(error=>{setIsSpin(false);}) @@ -159,7 +197,7 @@ function CoderDepot(props){ let ele = document.getElementById("ptxt"); if(ele){ let h = ele.offsetHeight; - if( h > 18 ) setHideBtn(true); + if( h > 35 ) setHideBtn(true); } } },[projectDetail,lastCommit]) @@ -180,15 +218,18 @@ function CoderDepot(props){ setDirInfo(undefined); setFileInfo(en); setType(en.type); + setReadme(undefined); }else{ setFileInfo(undefined); setDirInfo(en); setType("dir"); + getReadmeInfo(path, branchName || defaultBranch); } let c = result.data.last_commit setLastCommit(c && c.commit); setLastCommitAuthor(c && c.committer); setMainFlag(false); + setReadOnly(true); setReadOnly(!editReadme); setHide(true); } @@ -208,7 +249,7 @@ function CoderDepot(props){ let b = branchName || defaultBranch; let checkvalue = turnbar(b); return ( - + urlLink(`/${owner}/${projectsId}/${checkvalue}/uploadfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>上传文件 @@ -300,8 +341,10 @@ function CoderDepot(props){ const mdFlag = n && n.substring(n.length-3,n.length) === ".md"; const { current_user } = props; - const baseOperate = projectDetail && projectDetail.permission && projectDetail.permission !=="Reporter"; + const baseOper = current_user && current_user.login && issuesFlag; + const baseOperate = projectDetail && projectDetail.permission && projectDetail.permission !=="Reporter" && projectDetail.type !== 2 && pullsFlag; const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin)); + return( setOpenModal(false)} onOk={okUpdate}/> @@ -320,7 +363,7 @@ function CoderDepot(props){ list = {mainFlag ? dirInfo : undefined} />
    setVisible(true)}> - + 目录
    @@ -333,7 +376,7 @@ function CoderDepot(props){
    -
    +
    { props && props.platform ? : - 分支:{branchName || defaultBranch} + 分支:{branchName || defaultBranch} }
    - - - - {projectDetail && projectDetail.branches && projectDetail.branches.total_count}个分支 - - - - - - {projectDetail && projectDetail.tags && projectDetail.tags.total_count}个标签 - - + { + treeValuePath && treeValuePath.length > 0 ? + + : + + + + + 分支 + {projectDetail && projectDetail.branches_count} + + + + + + 标签 + {projectDetail && projectDetail.tags_count} + + + + } - + { - baseOperate && ((projectDetail.type !== 2 && pullsFlag) || issuesFlag )&& -
    + (baseOperate || baseOper) && +
    { - projectDetail.type !== 2 && pullsFlag && - urlLink(`/${owner}/${projectsId}/pulls/new`)} >+ 合并请求 + baseOperate && + urlLink(`/${owner}/${projectsId}/compare/master...${branchName || defaultBranch}`)} >+ 合并请求 } { - issuesFlag && - urlLink(`/${owner}/${projectsId}/issues/new`)} >+ 任务 + baseOper && + urlLink(`/${owner}/${projectsId}/issues/new`)} >+ 易修 }
    } { fileOperate && - - + + 文件 } - + @@ -395,27 +458,20 @@ function CoderDepot(props){
    -
    {lastCommit && lastCommit.message}
    +
    { hideBtn && changeHide(hide)}> } - {lastCommit && lastCommit.time_from_now} - { commitCount ? - {commitCount}次提交 - :"" } + {lastCommit.time_from_now} + { + commitCount ? + + {commitCount}次提交 + :"" + }
    }
      - { - treeValuePath && treeValuePath.length > 0 && - - } { dirInfo && dirInfo.length > 0 && dirInfo.map((item,key)=>{ @@ -449,81 +505,80 @@ function CoderDepot(props){ (dirInfo && dirInfo.length === 0) && !fileInfo ? :"" } {/* readme文件显示(显示文件详情时不显示readme文件) */} - { dirInfo && (projectDetail && projectDetail.readme) ? :"" } + { (readme && readme.content) ? :"" }
    { - !fileInfo && + (!(treeValuePath && treeValuePath.length > 0) && !fileInfo) &&
    - 简介 + 关于 { projectDetail.permission && (projectDetail.permission==="Admin" || projectDetail.permission==="Owner" || projectDetail.permission==="Manager") && - setOpenModal(true)} className="iconfont icon-anquanshezhi color-grey-9 font-15"> + setOpenModal(true)} className="iconfont icon-a-shezhi color-grey-9 font-15"> } - {desc &&

    {desc}

    } + {desc &&

    {desc}

    } { website && -

    - - {website} -

    +
    + + {website} +
    } -

    - - README.md -

    -

    - +

    + + README.md +
    +
    + {projectDetail && projectDetail.size} -

    +
    { projectDetail && projectDetail.license_name && -

    - - {projectDetail.license_name} -

    +
    + + {projectDetail.license_name} +
    }
    { inviteCode &&
    - +
    } { lesson_url &&
    -

    实践课程

    +

    实践课程

    {lesson_url}
    } {/* 发布 */} { - projectDetail && projectDetail.release_versions && + releaseVersions && } {/* 贡献者 */} { - projectDetail && projectDetail.contributors && projectDetail.contributors.length >0 && - + projectDetail && projectDetail.contributors && projectDetail.contributors.total_count >0 && + } {/* 语言 */} - { projectDetail && projectDetail.languages && projectDetail.languages.length >0 && + { projectDetail && projectDetail.languages && diff --git a/src/forge/Main/CoderDepotCatalogue.jsx b/src/forge/Main/CoderDepotCatalogue.jsx index 3aa5ccc82..4f5d86c1a 100644 --- a/src/forge/Main/CoderDepotCatalogue.jsx +++ b/src/forge/Main/CoderDepotCatalogue.jsx @@ -4,8 +4,8 @@ import { truncateCommitId } from '../common/util'; const typeIco = { "submodule":"icon-file-submodule font-17", - "file":'icon-wenjia font-15', - "dir":"icon-wenjianjia1 font-15" + "file":'icon-wenjian6 font-15 color-blue-file', + "dir":"icon-wenjianjia4 font-15 color-blue_4C" } function CoderDepotCatalogue({item , goToSubRoot , owner , projectsId }){ @@ -13,7 +13,7 @@ function CoderDepotCatalogue({item , goToSubRoot , owner , projectsId }){
  • goToSubRoot(item.path,item.type,item.name)} className={item.type === "submodule" && "submoduleStyle"}> - {item.name} + {item.name} @@ -21,7 +21,7 @@ function CoderDepotCatalogue({item , goToSubRoot , owner , projectsId }){ {item.commit && item.commit.message} - {item.commit && item.commit.time_from_now} + {item.commit && item.commit.time_from_now}
  • ) } diff --git a/src/forge/Main/CoderDepotPath.jsx b/src/forge/Main/CoderDepotPath.jsx index ad4bb1096..89d6a5ca3 100644 --- a/src/forge/Main/CoderDepotPath.jsx +++ b/src/forge/Main/CoderDepotPath.jsx @@ -1,3 +1,4 @@ +import { result } from 'lodash'; import React from 'react'; diff --git a/src/forge/Main/CoderDepotReadme.jsx b/src/forge/Main/CoderDepotReadme.jsx index 1c54663d4..315f70805 100644 --- a/src/forge/Main/CoderDepotReadme.jsx +++ b/src/forge/Main/CoderDepotReadme.jsx @@ -1,8 +1,9 @@ import React, { useEffect, useState } from 'react'; import RenderHtml from '../../components/render-html'; import { AlignCenter } from '../Component/layout'; -import { Dropdown , Menu , Spin } from 'antd'; -import { Link } from 'react-router-dom'; +import { Dropdown , Anchor , Spin } from 'antd'; + +import ReadmeCatelogue from './sub/ReadmeCatelogue'; const $ = window.$; function CoderDepotReadme({ operate , history , readme , ChangeFile }){ @@ -23,49 +24,45 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){ const anchor = el.id; const level = el.tagName.replace("H", ""); const href = `#${anchor}`; - return { href:`${path}${href}`,text:el.textContent , level:level } + return { href:`${href}`,text:el.textContent , level:level } }); setMenuList(items); },[content]) function menu(){ if(menuList && menuList.length > 0){ - let hash = history.location.hash; return( - - { - menuList.map((item,key)=>{ - return( - -1 ?"active":""}>{item.text} - ) - }) - } - + ) }else{ return } } + return( -
    -
    - - - - - 目录 - - - README.md - - { - operate ? - ChangeFile(readme && readme.path, false)}> - - - :"" - } -
    +
    + +
    + + + + + 目录 + + + + README.md + + + { + operate ? + ChangeFile(readme && readme.path, false)}> + + + :"" + } +
    +
    { content &&
    diff --git a/src/forge/Main/CoderRootBranch.js b/src/forge/Main/CoderRootBranch.js deleted file mode 100644 index 3f71d6920..000000000 --- a/src/forge/Main/CoderRootBranch.js +++ /dev/null @@ -1,92 +0,0 @@ -import React , { useState, useEffect } from 'react'; -import { Link } from "react-router-dom"; -import { Dropdown , Menu , Icon , Tooltip , Spin } from 'antd'; -import { truncateCommitId } from '../common/util'; -import { getBranch } from '../GetData/getData'; -import Nodata from '../Nodata'; -import './list.css'; - -function turnbar(str){ - if(str && str.length>0 && str.indexOf("/")>-1){ - return str.replaceAll('/','%2F'); - } - return str; -} -export default ((props)=>{ - const [ data , setData ] =useState(undefined); - const [ isSpin , setIsSpin ] =useState(true); - - const { projectsId , owner } = props.match.params; - const { isManager , isDeveloper , projectDetail } = props; - useEffect(()=>{ - getBranchs(projectsId, owner); - },[projectsId]) - - async function getBranchs(id,owner){ - let result = await getBranch(id,owner); - setData(result); - setIsSpin(false); - } - - const list =()=>{ - if(data && data.length>0){ - return( - -
      - { - data.map((item,key)=>{ - return( -
    • -
      - {item.name} -

      - {item.last_commit && truncateCommitId(item.last_commit.sha)} - {item.last_commit && item.last_commit.message} - 最后更新于{item.last_commit && item.last_commit.time_from_now} -

      -
      - - { - (isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && - 创建合并请求 - } - - - - - - -
    • - ) - }) - } -
    -
    - ) - }else if(data && data.length === 0){ - return ( ) - } - } - const menu =(zip_url,tar_url)=> ( - - ZIP - TAR.GZ - - ) - - return( - -
    - -
    -

    分支列表

    -
    {list()}
    -
    -
    -
    -
    - ) -}) - - - diff --git a/src/forge/Main/CoderRootCommit.js b/src/forge/Main/CoderRootCommit.js index c1d7a45b3..b542c3905 100644 --- a/src/forge/Main/CoderRootCommit.js +++ b/src/forge/Main/CoderRootCommit.js @@ -1,13 +1,19 @@ import React , { Component } from 'react'; -import { Spin , Pagination } from 'antd'; +import { Spin , Pagination, Timeline } from 'antd'; import { getImageUrl } from 'educoder'; -import { truncateCommitId } from '../common/util'; +import { truncateCommitId ,timeFormat } from '../common/util'; import { AlignTop } from '../Component/layout'; import SelectBranch from '../Branch/Select'; import Nodata from '../Nodata'; +import User from '../Component/User'; +import RenderHtml from '../../components/render-html.jsx'; +import Tree from './img/tree.png'; import axios from 'axios'; import {Link} from "react-router-dom"; +import CopyTool from '../Component/CopyTool'; + +import './tree/Index.scss' function returnbar(str){ if(str && str.length>0 && str.indexOf("%2F")>-1){ @@ -15,14 +21,16 @@ function returnbar(str){ } return str; } + +//代码库--提交页面 class CoderRootCommit extends Component{ constructor(props){ - super(props) + super(props); this.state={ commitDatas:undefined, dataCount:undefined, - limit:20, - page:1, + limit:10, + page: 1, isSpining:false, branchList:undefined } @@ -50,20 +58,34 @@ class CoderRootCommit extends Component{ this.Init(); } } + + UrlParamHash(url){ + const params = {}; + let h; + let hash = url.slice(url.indexOf('?')+1).split('&'); + for(let i = 0; i{ const { branchName } = this.props.match.params; - const { page , limit } = this.state; + const { limit } = this.state; + const {search} = this.props.location; + const realPage = (search && this.UrlParamHash(search).page) ? parseInt(this.UrlParamHash(search).page) : 1; this.setState({ - isSpining:true + isSpining:true, + page:realPage }) - this.getCommitList( branchName , page , limit ); + this.getCommitList( branchName , realPage , limit ); } getCommitList=(branch , page , limit)=>{ this.setState({ isSpining:true }) - console.log(returnbar(branch)); const { projectsId , owner } = this.props.match.params; const url = `/${owner}/${projectsId}/commits.json`; axios.get(url,{ @@ -86,7 +108,8 @@ class CoderRootCommit extends Component{ image_url:item.author && item.author.image_url, sha:item.sha, time_from_now:item.time_from_now, - message:item.message + message:item.message, + timestamp:item.timestamp }) }) this.setState({ @@ -105,10 +128,9 @@ class CoderRootCommit extends Component{ } ChangePage=(page)=>{ - const { branchName } = this.props.match.params; - const { limit } = this.state; - this.getCommitList(branchName , page , limit); + this.props.history.push({pathname: this.props.history.location.pathname,search: `page=${page}`}) } + render(){ const { commitDatas , dataCount , limit , page , isSpining , branchList } = this.state; const { projectDetail, commit_class , defaultBranch } = this.props; @@ -129,46 +151,50 @@ class CoderRootCommit extends Component{ >
    -
    -
    -
    - {dataCount}次提交代码({branch}) -
    -
    -
    - { - commitDatas && commitDatas.length > 0 && commitDatas.map((item,k)=>{ - return( -
    - - {truncateCommitId(`${item.sha}`)} - {item.message} - -

    - { - item.id ? - - {item.image_url?:""} - - : - - {item.image_url?:""} - - - } - -

    + + { + commitDatas && commitDatas.length > 0 && commitDatas.map((item,k)=>{ + return( + 最新:}> +
    +
    + +
    +
    +

    + + {item.timestamp && } +

    +
    +
    +
    +
    + + sha + {truncateCommitId(`${item.sha}`)} + + + +
    + +
    +
    - ) - }) - } - {commitDatas && commitDatas.length === 0 && } -
    -
    + + ) + }) + } + {commitDatas && commitDatas.length === 0 && } + { dataCount > limit ?
    - +
    :"" } diff --git a/src/forge/Main/CoderRootFileDetail.js b/src/forge/Main/CoderRootFileDetail.js index fb17098bc..bdcfc98a7 100644 --- a/src/forge/Main/CoderRootFileDetail.js +++ b/src/forge/Main/CoderRootFileDetail.js @@ -1,10 +1,12 @@ import React, { Component } from "react"; -import { Popconfirm , Select } from "antd"; -import "./list.css"; +import { Popconfirm , Select , Dropdown , Spin , Anchor } from "antd"; +import "./list.scss"; import axios from "axios"; import Meditor from "../Newfile/m_editor"; import RenderHtml from "../../components/render-html"; +import ReadmeCatelogue from "./sub/ReadmeCatelogue"; +const $ = window.$; function bytesToSize(bytes) { if (bytes === 0) return "0 B"; let k = 1024, @@ -19,11 +21,13 @@ class CoderRootFileDetail extends Component { value: undefined, language: undefined, languages: undefined, - description: props.detail.content + description: props.detail.content, + menuList:undefined }; } componentDidMount = () => { + window.scrollTo(0, 0); const { detail , mdFlag } = this.props; this.setState({ value: detail.content, @@ -169,6 +173,31 @@ class CoderRootFileDetail extends Component { }); }; + renderMenulist=()=>{ + const { description } = this.state; + if(description){ + const items = $.map($("#files-md").find("h1,h2,h3,h4,h5,h6"), function (el, _) { + const anchor = el.id; + const level = el.tagName.replace("H", ""); + const href = `#${anchor}`; + return { href:`${href}`,text:el.textContent , level:level } + }); + return items; + } + return []; + } + + menu=()=>{ + const menuList = this.renderMenulist(); + if(menuList && menuList.length > 0){ + return( + + ) + }else{ + return + } + } + render() { const { readOnly, @@ -186,79 +215,88 @@ class CoderRootFileDetail extends Component { const Option = Select.Option; return ( -
    -
    - + +
    + { + md && readOnly && + + + + 目录 + + + } + {bytesToSize(detail && detail.size)}

    - {flag && platform && ( -

    - {readOnly ? ( - - { - !detail.direct_download? - - this.DownLoadFile(detail.download_url)} className="ml20"> - - - { - type !==2 && - this.EditFile(false)} className="ml20"> - + {flag && platform && ( +
    + {readOnly ? ( + + { + !detail.direct_download? + + this.DownLoadFile(detail.download_url)} className="ml20"> + - } - :"" - } - - ) : ( - - - - - )} - { - type !==2 && - - - - - - } - -
    - )} -

    -
    + { + type !==2 && + this.EditFile(false)} className="ml20"> + + + } +
    :"" + } + + ) : ( + + + + + )} + { + type !==2 && + + + + + + } + +
    + )} +

    +
    {detail.image_type ? (
    @@ -272,7 +310,7 @@ class CoderRootFileDetail extends Component {
    ) : ( md && readOnly ? -
    +
    : diff --git a/src/forge/Main/CoderRootIndex.js b/src/forge/Main/CoderRootIndex.js index 0fb30c4e1..7ff25dae1 100644 --- a/src/forge/Main/CoderRootIndex.js +++ b/src/forge/Main/CoderRootIndex.js @@ -1,9 +1,9 @@ import React , { Component } from 'react'; import { Route , Switch } from 'react-router-dom'; -import Top from './DetailTop'; +// import Top from './DetailTop'; import Loadable from 'react-loadable'; import Loading from '../../Loading'; -import axios from 'axios'; +import './Index.scss'; const FileNew = Loadable({ loader: () => import('../Newfile/Index'), @@ -18,25 +18,25 @@ const CoderRootCommit = Loadable({ loading: Loading, }) const CoderRootBranch = Loadable({ - loader: () => import('./CoderRootBranch'), + loader: () => import('./tree/Index'), loading: Loading, }) const CoderRootTag = Loadable({ - loader: () => import('./CoderRootTag'), + loader: () => import('./tag/Index'), loading: Loading, }) const CoderRootVersion = Loadable({ - loader: () => import('../Version/version'), - loading: Loading, -}) -const CoderRootVersionNew = Loadable({ - loader: () => import('../Version/New'), - loading: Loading, -}) -const CoderRootVersionUpdate = Loadable({ - loader: () => import('../Version/New'), + loader: () => import('./version/Index'), loading: Loading, }) +// const CoderRootVersionNew = Loadable({ +// loader: () => import('./version/New'), +// loading: Loading, +// }) +// const CoderRootVersionUpdate = Loadable({ +// loader: () => import('./version/New'), +// loading: Loading, +// }) const Diff = Loadable({ loader: () => import('./Diff'), loading: Loading, @@ -50,41 +50,41 @@ class CoderRootIndex extends Component{ } } - componentDidMount=()=>{ - this.Init(); - } - componentDidUpdate=(prevProps)=>{ - const { location } = this.props; - const prevlocation = prevProps && prevProps.location; - if (location !== prevlocation) { - this.Init(); - } - } + // componentDidMount=()=>{ + // this.Init(); + // } + // componentDidUpdate=(prevProps)=>{ + // const { location } = this.props; + // const prevlocation = prevProps && prevProps.location; + // if (location !== prevlocation) { + // this.Init(); + // } + // } - Init=()=>{ - const { branchName } = this.props.match.params; - const { defaultBranch } = this.props; - this.getTopCount(branchName || defaultBranch); - } + // Init=()=>{ + // const { branchName } = this.props.match.params; + // const { defaultBranch } = this.props; + // this.getTopCount(branchName || defaultBranch); + // } // 获取组件里要显示的数据 - getTopCount=(branch)=>{ - const { projectsId , owner } = this.props.match.params; - const url = `/${owner}/${projectsId}/top_counts.json`; - axios.get(url,{params:{ - ref:branch - }}).then(result=>{ - if(result){ - this.setState({ - coderCount:result.data - }) - } - }).catch(error=>{console.log(error);}) - } + // getTopCount=(branch)=>{ + // const { projectsId , owner } = this.props.match.params; + // const url = `/${owner}/${projectsId}/top_counts.json`; + // axios.get(url,{params:{ + // ref:branch + // }}).then(result=>{ + // if(result){ + // this.setState({ + // coderCount:result.data + // }) + // } + // }).catch(error=>{console.log(error);}) + // } render(){ return( -
    - +
    + {/* */} {/* 新建文件 */} () + (props) => () } > () + () => () } > () + () => () } > - () } @@ -126,7 +126,7 @@ class CoderRootIndex extends Component{ render={ () => () } - > + > */} () diff --git a/src/forge/Main/CoderRootTag.js b/src/forge/Main/CoderRootTag.js index da1fa96d1..484836213 100644 --- a/src/forge/Main/CoderRootTag.js +++ b/src/forge/Main/CoderRootTag.js @@ -34,7 +34,7 @@ export default (( props, { projectDetail }) => {
    • - 标签名 + 标记名 提交信息 下载
    • diff --git a/src/forge/Main/Detail.js b/src/forge/Main/Detail.js index b3262f880..06d2fc44b 100644 --- a/src/forge/Main/Detail.js +++ b/src/forge/Main/Detail.js @@ -1,10 +1,13 @@ import React, { Component } from 'react'; -import { Spin, Tooltip, Button } from 'antd'; +import { Spin, Tooltip } from 'antd'; import { Link, Route, Switch } from 'react-router-dom'; import { Content, AlignTop } from '../Component/layout'; import DetailBanner from './sub/DetailBanner'; import '../css/index.scss' -import './list.css'; +import './list.scss'; + +import { ImageLayerOfCommentHOC } from "../../modules/page/layers/ImageLayerOfCommentHOC"; + import Loadable from 'react-loadable'; import Loading from '../../Loading'; @@ -67,7 +70,7 @@ const MergeIndexDetail = Loadable({ }) const CreateMerge = Loadable({ - loader: () => import('../Merge/NewMerge'), + loader: () => import('../Merge/CreateMerge'), loading: Loading, }) @@ -147,7 +150,9 @@ function checkPathname(projectsId, owner, pathname) { name = "about" } else if (url.indexOf("/issues") > -1 || url.indexOf("Milepost") > 0) { name = "issues"; - } else if (url.indexOf("/pulls") > -1) { + } else if (url.indexOf("/pulls") > -1 || url.indexOf("/compare") > -1) { + // /pulls,合并请求除新建合并请求外, + // /compare,新建合并请求 name = "pulls" } else if (url.indexOf("/milestones") > -1) { name = "milestones" @@ -252,7 +257,11 @@ class Detail extends Component { open_devops: flag }) } - canvasChannel = () => { + /** + * + * @param {*} deleteFlag :同步镜像需要提示成功,且未成功的情况下不需要删除项目 + */ + canvasChannel = (deleteFlag) => { const name = window.location.hostname === "localhost" ? "testforgeplus.trustie.net" : window.location.hostname; const actioncable = require("actioncable"); var project = this.state.project; @@ -269,10 +278,15 @@ class Detail extends Component { console.log(`###### ---received data--- ######`); console.log(data); if (data) { - if ( data.project && data.project.mirror_status === 2) { - this.deleteProjectBack(); + if(deleteFlag){ + this.props.showNotification("镜像同步成功!"); + window.location.reload(); + }else{ + if (data.project && data.project.mirror_status === 2) { + this.deleteProjectBack(); + } + this.getDetail(); } - this.getDetail(); this.setState({ firstSync: false, secondSync: false @@ -306,6 +320,9 @@ class Detail extends Component { const url = `/${owner}/${projectsId}/detail.json`; axios.get(url).then((result) => { if (result && result.data) { + if (result.data.status === 404) { + this.props.history.push('/nopage'); + } this.setState({ projectDetail: result.data, project_id: result.data.project_id, @@ -425,8 +442,10 @@ class Detail extends Component { const url = `/${owner}/${projectsId}/sync_mirror.json`; axios.post(url).then(result => { if (result && result.data && result.data.status === 0) { - this.props.showNotification("镜像同步成功!"); - this.getProject(2); + this.setState({ + secondSync:true + }) + this.canvasChannel(true); } else { this.props.showNotification("镜像同步失败!"); } @@ -471,7 +490,7 @@ class Detail extends Component {
      - +
      @@ -491,7 +510,7 @@ class Detail extends Component { } { projectDetail && projectDetail.type && projectDetail.type !== 0 ? - 镜像自 {projectDetail.mirror_url} + 导入于 {projectDetail.mirror_url} : "" }
      @@ -504,7 +523,7 @@ class Detail extends Component { ((current_user && current_user.admin) || isManager) && (projectDetail && projectDetail.type && projectDetail.type === 2) ? 同步镜像 : "" } - - - + }
      @@ -667,18 +686,19 @@ class Detail extends Component { (props) => () } > - {/* 修改详情 */} - () } > - {/* 复制详情 */} + {/* 复制详情 copyetail*/} () + (props) => () } > + {/* 任务详情 */} {/* 新建合并请求 */} - () + } + > */} + () } > - () - } - > - () } > - () + } + > + () + } + > + () } @@ -780,4 +810,7 @@ class Detail extends Component { } } -export default Detail; +export default ImageLayerOfCommentHOC({ + imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget", + parentSelector: ".newContainer", +})(Detail); diff --git a/src/forge/Main/DetailAdaptor.js b/src/forge/Main/DetailAdaptor.js new file mode 100644 index 000000000..d65f3bc3c --- /dev/null +++ b/src/forge/Main/DetailAdaptor.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { Route, Switch } from "react-router-dom"; +import { withRouter } from "react-router"; +import Loadable from "react-loadable"; +import Loading from "../../Loading"; +import { SnackbarHOC } from "educoder"; +import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC"; +import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC"; + +// forge项目详情 +const ProjectDetail = Loadable({ + loader: () => import("../Main/Detail"), + loading: Loading, +}); +export default withRouter( + (CNotificationHOC()(SnackbarHOC()(TPMIndexHOC((props) => { + return ( + + ( + + )} + > + + ) + })))) +) \ No newline at end of file diff --git a/src/forge/Main/Diff.jsx b/src/forge/Main/Diff.jsx index 0daa63193..8dad59fba 100644 --- a/src/forge/Main/Diff.jsx +++ b/src/forge/Main/Diff.jsx @@ -1,35 +1,65 @@ import React, { useEffect, useState } from "react"; import styled from "styled-components"; import { Button ,Spin } from "antd"; -import { truncateCommitId } from '../common/util'; +import { timeFormat, truncateCommitId } from '../common/util'; import { getImageUrl } from 'educoder'; import Files from '../Merge/Files'; - +import Tree from "./img/tree.png"; import User from "../Component/User"; -import Keys from "../Component/Keys"; - +import RenderHtml from "../../components/render-html"; import axios from "axios"; +import { Link } from "react-router-dom"; const Infos = styled.div` - border: 1px solid #dddddd; + border: 1px solid #FAFCFF; margin-bottom:15px; & .commitinfos { background-color: #f1f8ff; - border-bottom: 1px solid #ddd; - padding: 20px; + border: 1px solid rgba(42, 97, 255, 0.23); + border-radius: 3px 3px 0px 0px; + padding: 10px 20px 10px 16px; + & .markdown-body table{ + background: #f1f8ff; + } + & .btnblue{ + margin-top: 12px; + } + & .task-hide{ + width: 65rem; + overflow:hidden; + white-space:normal; + word-break:break-all; + font-weight: bold; + color: #333333; + font-size: 16px; + } } & > .f-wrap-between { - padding: 10px 20px; + padding: 14px 20px 14px 16px; + border-radius: 3px 3px 0px 0px; + border: 1px solid #D0D0D0; + .df{ + align-items: center; + & .underline:hover{ + text-decoration: underline; + } + } } `; - -export default ({ match , history }) => { +function turnbar(str){ + if(str && str.length>0 && str.indexOf("/")>-1){ + return str.replaceAll('/','%2F'); + } + return str; +} +//提交详情页 +export default (props) => { + const {match , history } = props; const [data, setData] = useState({undefined}); const [commit, setCommit] = useState(undefined); const [parents, setParents] = useState(undefined); const [committer, setCommitter] = useState(undefined); const [isSpin, setIsSpin] = useState(true); - const { sha , projectsId, owner } = match.params; useEffect(() => { if (projectsId && owner && sha) { @@ -43,6 +73,7 @@ export default ({ match , history }) => { setParents(result.data.parents); setCommitter(result.data.committer || (result.data.commit && result.data.commit.committer)); setIsSpin(false); + } }) .catch(error => { @@ -56,29 +87,42 @@ export default ({ match , history }) => {
      - {commit && commit.message && -
      {commit.message}
      - } - +
      + {commit && commit.message && + + } + {data.branch} +
      +
        - {committer && committer.time_from_now &&
      • {committer.time_from_now}
      • } + {commit && commit.timestamp &&
      • 提交于{timeFormat(commit.timestamp)}
      • }
    • { parents && parents.length > 0 && parents.map((item,key)=>{ return( - +
      + + sha + {truncateCommitId(item.sha)} +
      ) }) } - +
      + + sha + {truncateCommitId(sha)} +
    • @@ -87,6 +131,7 @@ export default ({ match , history }) => { data={data} owner={owner} projectsId={projectsId} + parentsSha={parents && parents.length > 0 && parents[0].sha} />
      diff --git a/src/forge/Main/Index.js b/src/forge/Main/Index.js index d1725fc66..aff2638e8 100644 --- a/src/forge/Main/Index.js +++ b/src/forge/Main/Index.js @@ -5,7 +5,7 @@ import { getImageUrl } from 'educoder'; import "slick-carousel/slick/slick.css"; import "slick-carousel/slick/slick-theme.css"; import '../css/index.scss' -import './list.css'; +import './list.scss'; import './Index.scss'; import ListItem from './IndexItem' import axios from 'axios'; @@ -256,14 +256,14 @@ class Index extends Component { newItem = ()=>{ return( - - - {this.props.history.push('/projects/mirror/new')}}>新建镜像项目 - - - {this.props.history.push('/projects/deposit/new')}}>新建托管项目 - - +
        +
      • + {this.props.history.push('/projects/deposit/new')}}>新建项目 +
      • +
      • + {this.props.history.push('/projects/mirror/new')}}>导入项目 +
      • +
      ) } @@ -391,7 +391,13 @@ class Index extends Component {
      { current_user && current_user.login && - + 新建 diff --git a/src/forge/Main/Index.scss b/src/forge/Main/Index.scss index cb05f1cf2..5f3692929 100644 --- a/src/forge/Main/Index.scss +++ b/src/forge/Main/Index.scss @@ -13,6 +13,21 @@ } } } +.iconBtn{ + i{ + color: #666; + } + span{ + margin-left: 4px; + color: #333!important; + &:last-child{ + font-weight: 500; + } + } + &:hover span,&:hover i{ + color: #466AFF!important; + } +} /* recommandProjects */ .recommandProjects.slick-slider{ width: 1230px; @@ -108,63 +123,94 @@ margin: 0 auto; .panelmenu{ padding-top:30px; + .depotBtn{ + .mr-5{ + margin-right: -5px; + } + .ant-btn{ + height: 32px; + line-height: 32px; + width: 83px; + text-align: center; + padding:0px ; + font-weight: 500; + font-size: 14px; + } + .ant-btn-default{ + color: #333; + border-color: #d0d0d0; + &:hover{ + background: #F3F4F6; + } + } + .ant-btn-primary{ + color: #fff; + background-color: #466AFF; + border: none; + &:hover{ + background-color: rgba(70,106,255,0.85); + } + } + } } - .addOptionBtn{ - height: 32px; - line-height: 30px; + .depotBtn,.addOptionBtn{ display: flex; - border:1px solid #d9d9d9; - border-radius: 2px; a{ - padding:0px 13px; - color: rgba(0, 0, 0, 0.65); - cursor: pointer; - } - & > a:first-child{ - border-right: 1px solid #d9d9d9; - } - & > a:last-child{ - border-right: none; + color: #333!important; + font-weight: 500!important; + border-radius: 5px; + width: 83px; + height: 32px; + line-height: 30px; + background: #fff; + border: 1px solid #D0D0D0; + margin-right: 10px; + text-align: center; + &:hover,&:active{ + background: #F3F4F6; + } } } .infoCount{ display: inline-block; - padding:0px 5px; - height: 16px; - line-height: 16px; - background-color: #eee; - color:#999; + width: 24px; + text-align: center; + height: 24px; + line-height: 24px; + background-color:rgba(153, 153, 153, 0.13);; + color:#666; border-radius: 12px; - margin-left: 10px; + margin-left: 6px; font-size: 12px; } .attrPerson{ - padding-top: 15px; + padding-top: 12px; display: flex; flex-wrap: wrap; + padding-bottom: 2px; a{ - margin: 10px 10px 0px 0px; + margin: 0px 17px 10px 0px; img{ border-radius: 50%; - width: 35px; - height: 35px; + width: 40px; + height: 40px; } - &:nth-child(6){ + &:nth-child(5n){ margin-right: 0px; } } } .progress{ display: flex; - border-radius: 10px; - height: 7px; + border-radius: 2px; + height: 11px; margin-top: 12px; span{ &:first-child{ - border-radius: 10px 0px 0px 10px; + border-radius: 2px 0px 0px 2px; } &:last-child{ - border-radius: 0px 10px 10px 0px; + border-radius: 0px 2px 2px 0px; } } } @@ -178,16 +224,17 @@ height: 8px; width: 8px; left: 0px; - top:10px + top:8px; } &>span{ padding-left: 15px; position: relative; min-width: 33.5%; + font-size: 12px; + font-weight: 400; + color: #666; span{ - color: #666; &:last-child{ - color: #999; margin-left: 5px; } } @@ -195,18 +242,24 @@ } .listtable{ margin-top: 20px; - border:1px solid #d9d9d9; - border-radius: 4px; .listtablehead{ display: flex; justify-content: space-between; align-items: flex-start; border-bottom: 1px solid #d9d9d9; - padding:7px 20px; + padding:12px 20px 11px; border-radius: 4px 4px 0px 0px; - background-color: #FAFBFC; + border: 1px solid rgba(42, 97, 255, 0.23); + background-color: #FAFCFF; .ellipsistxt{ - margin-top: 6px; + &:hover .markdown-body{ + color: #466AFF; + & a{ + color: #466AFF; + } + } + margin-top: 2px; + // cursor: pointer; #ptxt{ margin-bottom: 0px; word-break: break-all; @@ -216,6 +269,27 @@ white-space:-pre-wrap; /* Opera 4-6 */ white-space:-o-pre-wrap; /* Opera 7 */ word-wrap:break-word; + .markdown-body{ + line-height: 10px; + font-size: 14px; + & p { + margin: 1px 0px 0px !important; + font-size: 14px !important; + } + & ol,ul{ + padding-bottom: 3px; + & li{ + min-height: 18px; + } + } + & table{ + line-height: 1; + background: #FAFCFF; + } + &:first-child { + margin-top: -1px !important; + } + } } margin-left: 13px; line-height:18px; @@ -223,36 +297,41 @@ width: 0; color: #666; &.hidetxt{ - height: 18px; + height: 24px; overflow: hidden; position: relative; padding-right:8px; - &::after{ - position: absolute; - right: 0px; - bottom: 0px; - content:"..."; - } + // &::after{ + // position: absolute; + // right: 0px; + // bottom: 0px; + // content:"..."; + // } } } .ellipsis{ margin-left: 8px; cursor: pointer; border-radius: 2px; - background-color: #c1c1c1; + height: 16px; + background: rgba(153, 153, 153, 0.2); + border-radius: 2px; padding:0px 4px; height: 14px; line-height: 14px; margin-top: 9px; i{ font-size: 15px!important; - color: #fff; + color: #333; height: 14px; line-height: 14px; } } } .listtablebody{ + border-radius:0px 0px 4px 4px ; + border: 1px solid #D0D0D0; + border-top: none; li.listtablepath{ a{color: #40a9ff;} p{ @@ -260,12 +339,15 @@ } } & > li{ - height: 42px; + height: 38px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #d9d9d9; padding:0px 20px 0px 24px; + &:hover{ + background-color: #F3F4F6; + } & > span:first-child{ width: 30%; overflow: hidden; @@ -292,8 +374,10 @@ .drawerBtn{ position: fixed; left: -13px; - border:1px solid rgb(207,205,223); - width: 34px; + width: 33px; + background: #FFFFFF; + box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.09); + border: 1px solid #666666; border-radius: 0px 12px 12px 0px; height: 70px; top:50%; @@ -301,36 +385,49 @@ cursor: pointer; display: flex; flex-direction: column; - align-items: flex-end; + align-items: center; justify-content: center; + padding-left: 7px; &:hover{ - box-shadow: 1px 0px 7px rgba(0,0,0,0.1); + box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.09); } span{ writing-mode: vertical-lr; - color: #202429; + color: #333; width: 25px; + font-size: 14px; } i{ - color: #24292e; - height: 18px; - line-height: 18px; - width: 18px; + color: #333; + height: 14px; + line-height: 14px; + width: 14px; + margin-left: 2px; + margin-bottom: 3px; } } .downMenu{ - width: 330px; - box-shadow: 0px 0px 9px rgba(134, 134, 134,0.4); + width: 329px; background-color: #fff; - .ant-menu-vertical .ant-menu-item:hover{ - background-color: #e6f7ff; + box-shadow: 0px 1px 8px 1px rgba(212, 212, 212, 0.5); + padding-bottom: 14px; + .ant-menu-item{ + height: 50px; + line-height: 50px; + } +} +.fileMenu{ + width: 83px; + li{ + padding:6px 0px!important; + text-align: center; + width: 100%; } } -.menuslist{ - max-height: 200px; - overflow-y: auto; - padding:10px 15px; +.catelogue{ + cursor: pointer; + background: #FAFBFC; border-radius: 4px; .ant-dropdown-menu-item{ border-radius: 8px; @@ -342,22 +439,19 @@ text-overflow: ellipsis; } } - .ant-dropdown-menu-item.active{ - background-color: #e6f7ff; - } -} -.catelogue{ - border:1px solid rgb(211, 211, 211); + border: 1px solid #D0D0D0; font-size: 15px; font-weight: normal; - border-radius: 5px; - margin-right: 10px; + margin-right: 12px; padding:0px 10px; height: 30px; line-height: 30px; color: #666!important; display: flex; align-items: center; + &:hover{ + background-color: #F3F4F6; + } span{ margin-top: 1px; } @@ -370,4 +464,48 @@ &:hover{ color: #05101a; } +} +.pinfos{ + i,a{color: #666;} + &:hover i,&:hover a{ + color: #2A61FF!important; + } +} +.graph{ + flex:1; + margin:0px 12px; + .ant-typography{ + white-space: pre-wrap; + margin-bottom: 0px; + } +} +.ant-anchor-wrapper{ + padding-left: 2px!important; + .ant-anchor-ink::before{ + background-color: #fff; + } +} +.coderSubPage{ + width: 1200px; + margin:0px auto; +} +.griditemAnchor{ + margin-left: 0px!important; + padding: 0px!important; + border-bottom: 1px solid #ddd; + .ant-anchor{ + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + } + .griditemCate{ + color: #333; + font-size: 16px; + display: flex; + align-items: center; + .catelogue{ + margin-left: 0px; + } + } } \ No newline at end of file diff --git a/src/forge/Main/IndexItem.js b/src/forge/Main/IndexItem.js index 2265ac20d..0cba9123e 100644 --- a/src/forge/Main/IndexItem.js +++ b/src/forge/Main/IndexItem.js @@ -5,7 +5,7 @@ import { AlignCenter } from '../Component/layout'; import { Link } from 'react-router-dom'; import '../css/index.scss'; import Nodata from '../Nodata'; -import './list.css'; +import './list.scss'; import img_parise from '../Images/parise.png'; class IndexItem extends Component { @@ -41,22 +41,22 @@ class IndexItem extends Component { { !item.is_public && 私有 } { item.forked_from_project_id ? - + - + : "" } { item.type && item.type === 2 ? - + :"" } { item.type && item.type === 1 ? - + - :"" + :"" } diff --git a/src/forge/Main/img/tree-black.png b/src/forge/Main/img/tree-black.png new file mode 100644 index 000000000..02aa71c3a Binary files /dev/null and b/src/forge/Main/img/tree-black.png differ diff --git a/src/forge/Main/img/tree.png b/src/forge/Main/img/tree.png new file mode 100644 index 000000000..56247256c Binary files /dev/null and b/src/forge/Main/img/tree.png differ diff --git a/src/forge/Main/list.css b/src/forge/Main/list.scss similarity index 75% rename from src/forge/Main/list.css rename to src/forge/Main/list.scss index 61b0e074a..3b8ccd546 100644 --- a/src/forge/Main/list.css +++ b/src/forge/Main/list.scss @@ -286,74 +286,92 @@ } /* -----------详情------------ */ .detailHeader-wrapper{ - background-color:#FAFBFC; - /* background: url(../Images/forgeBanner.jpg) no-repeat center; */ - /* background-size:cover; */ + background-color:#FBFCFF; + border-bottom:1px solid #e2e2e2; } .headerMenu-wrapper{ - font-size: 16px; display: flex; flex-direction: row; -} -.headerMenu-wrapper li{ - position: relative; - text-align: center; - height: 40px; - line-height: 28px; - margin-right: 40px; -} -.headerMenu-wrapper li a{ - color: #666; -} -.headerMenu-wrapper li a > img{ - margin-right: 8px; -} -.headerMenu-wrapper li a > span.num{ - height: 28px; - line-height: 29px; - margin-left: 8px; - font-size: 12px; - color: #2878FF; - float: right; -} -.headerMenu-wrapper li.active::after{ - position: absolute; - bottom:0px; - height:2px; - background-color: #5091FF; - content:''; - left: 0px; - width:100%; + cursor: pointer; + li{ + text-align: center; + padding:0px; + margin-right: 40px; + display: flex; + & > a{ + position: relative; + font-size: 14px; + height: 36px; + line-height: 24px; + display: block; + color: #000!important; + &> span.num{ + line-height: 24px; + margin-left: 5px; + font-size: 12px; + float: right; + color: #666!important; + background-color: rgba(153, 153, 153, 0.13);; + border-radius: 50%; + width: 24px; + height: 24px; + } + } + + &.active a::after,&:hover a::after{ + position: absolute; + bottom:0px; + height:2px; + background-color:rgba(153, 153, 153, 0.2); + content:''; + left: 0px; + width:100%; + } + &.active span{ + font-weight: 500; + } + &.active a::after{ + background-color: #466AFF; + } + } } .detail_tag_btn{ - height:26px; - line-height: 26px; + height:32px; + line-height: 32px; border-radius:5px; - border:1px solid #f1f1f1; + border:1px solid #D0D0D0; display: flex; align-items: center; - margin-left: 30px; + margin-left: 10px; padding:0px; - background-color: transparent; + background-color:#FAFBFC; box-shadow: none; + .detail_tag_btn_name{ + padding:0px 10px; + text-align: center; + height: 30px; + line-height: 30px; + border-radius:5px 0px 0px 5px; + &:hover + { + background-color: #F3F4F6; + } + span{ + color: #333!important; + } + } + .detail_tag_btn_count{ + width: 42px; + text-align: center; + background: #fff; + border-radius: 0px 4px 4px 0px; + height:100%; + border-left: 1px solid #D0D0D0; + } } .ant-tooltip { max-width: fit-content!important; } -.detail_tag_btn_name{ - padding:0px 10px; - color: #666!important; -} -.detail_tag_btn_name img{ - margin-right: 10px; -} -.detail_tag_btn_count{ - padding:0px 10px; - background: #fff; - border-radius: 0px 4px 4px 0px; - font-size: 12px; - height:100%; -} .files-md{ padding:20px; } @@ -404,6 +422,7 @@ .gitAddressClone{ + margin:14px 20px!important; display: flex; height: 40px; align-items: center; @@ -475,14 +494,15 @@ border-right: none; } .gitAddressClone > input{ - border:none; outline: none; - padding:0px 8px; - height: 40px; - line-height: 40px; - border-radius: 0px; - border: 1px solid #eee; - flex:1; + padding: 0px 8px; + height: 38px; + line-height: 38px; + border: none!important; + border-right: 1px solid #eee!important; + border-radius: 4px 0px 0px 4px; + flex: 1; + max-width: 249px; } .wrap-commit-table .ant-table-small > .ant-table-content > .ant-table-body{ margin:0px; @@ -567,7 +587,7 @@ } .addFile a{ display: block; - background-color: rgb(76, 172, 255,0.8); + background-color: rgba(76, 172, 255,0.8); color: #fff; cursor: pointer; height: 32px; @@ -582,7 +602,7 @@ border-left: 1px solid rgba(247, 247, 247, 0.3); } .addFile a:active{ - background-color: rgb(76, 172, 255,1); + background-color: rgba(76, 172, 255,1); } @@ -621,8 +641,11 @@ } .commonBox{ border:1px solid #ddd; - margin-top: 30px; + margin-top: 18px; border-radius: 4px; + .ant-anchor-wrapper{ + overflow: unset!important; + } } .commonBox .commonBox-title{ padding:0px 20px; @@ -635,14 +658,28 @@ border-bottom: 1px solid #d9d9d9; border-radius: 4px 4px 0px 0px; } +.readBox{ + border:none; + &.commonBox .commonBox-info{ + border:1px solid #D0D0D0; + border-top: none; + border-radius: 0px 0px 4px 4px; + padding:20px 38px; + } +} .commonBox .commonBox-title.boxTitle{ display: flex; justify-content: space-between; + height: 55px; + line-height: 55px; + background: #FAFCFF; + border-radius: 4px 4px 0px 0px; + border: 1px solid rgba(42, 97, 255, 0.23); } .synchronism{ display: block; - height: 26px; - line-height: 26px; + height: 34px; + line-height: 34px; padding:0px 15px; color: #fff!important; background-color: #28BD6C; @@ -651,10 +688,19 @@ .files_info{ cursor: pointer; } -.commonBox .commonBox-info{ - padding:20px 15px; +.commonBox { + .commonBox-info{ + padding:20px 15px; + } +} +.commonBox-title-read{ + vertical-align: middle; + color: #000; + font-size: 14px; + &:hover { + color: #466AFF; + } } -.commonBox-title-read{vertical-align: middle;color: #666;} @media screen and (max-width: 370px){ .p-r-tags,.p-r-btn{ @@ -695,9 +741,7 @@ .item:last-child{ border-bottom:none; } -.gitAddressClone{ - margin: 0 !important; -} + .item_title small{ font-weight: 400; margin-left: 10px; @@ -746,12 +790,84 @@ a.color-grey-ccc:hover{ padding:0px 30px; min-height: 400px; } -.commitList > div{ - border-bottom: 1px solid #EEEEEE; - padding:16px 0px; -} -.commitList > div:last-child{ - border-bottom: none; + +.main{ + margin: 30px auto; + .ant-timeline{ + margin-top: 28px; + .commitList-item{ + position: relative; + padding: 20px 20px; + background: #FAFCFF; + border: 1px solid rgba(42, 97, 255, 0.23); + border-radius: 4px; + margin-left: 16px; + & .treecopy{ + margin-top: 20px; + } + & .markdown-body table{ + background: #FAFCFF; + } + &:after,&:before{ + content: ""; + position: absolute; + left: -10px; + top: 10px; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-right: 10px solid rgba(42, 97, 255, 0.23); + } + &:after{ + left: -8px; + border-right: 10px solid #FAFCFF; + &:hover{ + border-right: 10px solid #EEF6FF; + } + } + &:hover{ + background: #EEF6FF; + border: 1px solid rgba(42, 97, 255, 0.58); + &:after{ + border-right: 10px solid #EEF6FF; + } + &:before{ + border-right: 10px solid rgba(42, 97, 255, 0.58); + } + & .markdown-body table{ + background: #EEF6FF; + } + } + .treecopy-cont{ + padding: 4px 15px; + } + .btn-83{ + margin-left: 20px; + } + } + .ant-timeline-item{ + padding: 8px 0 20px; + } + .ant-timeline-item-tail{ + height: calc(100% - 20px); + border-left: 2px solid #EEEEEE; + top: 12px; + &:after{ + content: ' '; + height: 0; + position: absolute; + width: 0; + border: 7px solid transparent; + border-top-color: #EEEEEE; + top: 100%; + left: 50%; + margin-left: -8px; + } + } + .ant-timeline-item-head-custom{ + top:20px; + padding: 0 1px; + } + } } @@ -800,4 +916,13 @@ a.color-grey-ccc:hover{ text-align: center; display: flex; justify-content: center; +} +.depotNum{ + color: #666!important; + span:last-child{ + color: #333; + } + &:hover span:last-child{ + color: #2A61FF; + } } \ No newline at end of file diff --git a/src/forge/Main/sub/DetailBanner.jsx b/src/forge/Main/sub/DetailBanner.jsx index 09902927d..803ad6aac 100644 --- a/src/forge/Main/sub/DetailBanner.jsx +++ b/src/forge/Main/sub/DetailBanner.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { Skeleton , Tooltip} from 'antd'; import { Link } from 'react-router-dom'; +import { numFormat } from 'educoder'; function DetailBanner({ history,list , owner , projectsId , isManager , url , pathname , state , urlFlag , projectDetail , platform ,open_devops }){ const [ menuName , setMenuName ] = useState(undefined); @@ -17,7 +18,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa } },[list]); return( -
      +
      { menuName && menuName.length> 0 && projectDetail ?
        @@ -29,39 +30,39 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa item.menu_name === "home" &&
      • - + 主页 - +
      • } { item.menu_name === "code" &&
      • - + 代码库 - +
      • } { item.menu_name === "issues" &&
      • - - - 易修(Issue) - {projectDetail && projectDetail.issues_count ? {projectDetail.issues_count} : ""} + + + 易修(Issue) + + {projectDetail && projectDetail.issues_count ? {numFormat(projectDetail.issues_count)} : ""} -
      • } { item.menu_name === "pulls" && projectDetail && parseInt(projectDetail.type) !== 2 && platform ?
      • - + 合并请求 - {projectDetail && projectDetail.pull_requests_count ? {projectDetail.pull_requests_count} : ""} + {projectDetail && projectDetail.pull_requests_count ? {numFormat(projectDetail.pull_requests_count)} : ""}
      • :"" } @@ -69,7 +70,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa item.menu_name === "wiki" &&
      • - + Wiki
      • @@ -79,29 +80,29 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
      • {/* */} - 工作流(beta版) + 工作流(beta版) {projectDetail && projectDetail.ops_count ? {projectDetail.ops_count} : ""}
      • :"" } - { - // item.menu_name === "resources" && - //
      • - // - // - // 资源库 - // {projectDetail && projectDetail.source_count ? {projectDetail.source_count} :""} - // - //
      • - } + {/* { + item.menu_name === "resources" && +
      • + + + 资源库 + {projectDetail && projectDetail.source_count ? {projectDetail.source_count} :""} + +
      • + } */} { item.menu_name === "versions" &&
      • - - 里程碑 - {projectDetail && projectDetail.versions_count ? {projectDetail.versions_count} :""} + + 里程碑 + {projectDetail && projectDetail.versions_count ? {numFormat(projectDetail.versions_count)} :""}
      • } @@ -109,7 +110,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa item.menu_name === "activity" &&
      • - + 动态
      • @@ -118,7 +119,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa item.menu_name === "settings" &&
      • - 0 ? "iconfont icon-cangku color-grey-3 mr5 font-14":"iconfont icon-cangku color-grey-6 font-14 mr5"}> + 0 ? "iconfont icon-cangkushezhiicon color-grey-3 mr5 font-14":"iconfont icon-cangkushezhiicon color-grey-6 font-14 mr5"}> 仓库设置
      • diff --git a/src/forge/Main/sub/Invite.jsx b/src/forge/Main/sub/Invite.jsx index 61fb40cfc..201cee6dd 100644 --- a/src/forge/Main/sub/Invite.jsx +++ b/src/forge/Main/sub/Invite.jsx @@ -6,7 +6,7 @@ function Invite({code,className}) { return(
        - 邀请码 + 邀请码
        可以通过邀请码邀请成员加入项目
        点击复制邀请码。

        } className="ml8 font-16" inputId="devitecode"/> diff --git a/src/forge/Main/sub/ReadmeCatelogue.jsx b/src/forge/Main/sub/ReadmeCatelogue.jsx new file mode 100644 index 000000000..6dab7d792 --- /dev/null +++ b/src/forge/Main/sub/ReadmeCatelogue.jsx @@ -0,0 +1,57 @@ +import React , { useState } from 'react'; +import { Anchor , Input } from 'antd'; +import './sub.scss'; +import { useEffect } from 'react'; + +const { Link } = Anchor; + +function ReadmeCatelogue({ menuList , hash }) { + const [ goHref , setGoHref ] = useState(""); + const [ value , setValue ] = useState(""); + const [ menu , setMenu] = useState(menuList); + + + function onChange(link){ + setGoHref(link); + }; + + function changeValue(e) { + setValue(e.target.value); + if(e.target.value){ + let m = menuList.filter(i=>i.text.toLowerCase().indexOf(e.target.value.toLowerCase())>-1); + setMenu(m); + }else{ + setMenu(menuList); + } + } + return( +
        +
        + }/> +
        + { + menu && menu.length>0? +
        + + { + menu.map((item,key)=>{ + return( +
        + +
        + ) + }) + } +
        +
        + :"" + } + +
        + ) +} +export default ReadmeCatelogue; \ No newline at end of file diff --git a/src/forge/Main/sub/SubMenu.jsx b/src/forge/Main/sub/SubMenu.jsx new file mode 100644 index 000000000..78f37b4de --- /dev/null +++ b/src/forge/Main/sub/SubMenu.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import'./sub.scss' + +function SubMenu({tab,owner,projectsId}) { + return( +
          + 标签 + 发行版 +
        + ) +} +export default SubMenu; \ No newline at end of file diff --git a/src/forge/Main/sub/UpdateDescModal.jsx b/src/forge/Main/sub/UpdateDescModal.jsx index 5039123a4..b0291f347 100644 --- a/src/forge/Main/sub/UpdateDescModal.jsx +++ b/src/forge/Main/sub/UpdateDescModal.jsx @@ -53,7 +53,7 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_u {getFieldDecorator("lesson_url",{ rules:[] })( - + )} diff --git a/src/forge/Main/sub/sub.scss b/src/forge/Main/sub/sub.scss index aa8739710..7716f0c50 100644 --- a/src/forge/Main/sub/sub.scss +++ b/src/forge/Main/sub/sub.scss @@ -24,7 +24,68 @@ } } -.detailsCode{ +.menuslist{ + z-index: 100; + width: 297px; + background: #FFFFFF; + box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5); + border-radius: 4px; + .searchBox{ + padding:15px; + border-bottom: 1px solid #eee; + } + .ant-anchor-wrapper{ + margin-left: 0px; + padding:5px 15px; + max-height: 255px!important; + .items{ + border-radius: 4px; + margin-bottom: 5px; + cursor: pointer; + .ant-anchor-link-title{ + color: #333333!important; + } + &:hover{ + background-color: #F3F4F6; + } + &.active{ + background-color: #2A61FF; + .ant-anchor-link-title{ + color: #fff!important; + } + } + } + .ant-anchor-link{ + padding:0px; + height: 30px; + line-height: 30px; + } + .ant-anchor-ink::before{ + background-color: #fff; + } + } +} +.subMenu{ display: flex; - justify-content: space-between; + padding-top: 30px; + a{ + width: 83px; + font-weight: 500; + line-height: 30px; + height: 32px; + color: #333333!important; + text-align: center; + border: 1px solid #D0D0D0; + border-radius: 0px 4px 4px 0px; + background: rgba(250, 251, 252, 0); + &:first-child{ + border-right: none; + border-radius: 4px 0px 0px 4px; + } + &.active{ + background-color: #466AFF; + color: #fff!important; + border-color: #466AFF; + } + } } \ No newline at end of file diff --git a/src/forge/Main/tag/Index.jsx b/src/forge/Main/tag/Index.jsx new file mode 100644 index 000000000..ff7648513 --- /dev/null +++ b/src/forge/Main/tag/Index.jsx @@ -0,0 +1,138 @@ +import React,{ useEffect , useState } from 'react'; +import SubMenu from '../sub/SubMenu'; +import { Table , Tooltip , Spin } from 'antd'; +import axios from 'axios'; +import { Link } from 'react-router-dom'; +import { truncateCommitId } from '../../common/util'; +import { getImageUrl } from 'educoder'; +import Nonedata from '../../Nodata'; +import './Index.scss'; +import Tree from '../img/tree.png' +import moment from 'moment'; + +function Tags(props) { + + const [ source , setSource ] = useState(undefined); + const [ isSpin , setIsSpin ] = useState(true); + + const { projectsId , owner } = props.match.params; + + useEffect(() => { + if (projectsId) { + const url = `/${owner}/${projectsId}/tags.json`; + axios.get(url).then((result) => { + if (result) { + setSource(result.data); + setIsSpin(false); + } + }).catch(error => {}) + } + }, [owner, projectsId]); + + const columns=[ + { + title:"标签名", + dataIndex:"name", + key:1, + ellipsis:true, + width:"200px", + render:(txt,item)=>{ + return( +
        + {item.name} +
        + ) + } + }, + { + title:"创建时间", + dataIndex:"time_ago", + key:2, + ellipsis:true, + render:(txt,item)=>{ + return ( + + { + item.tagger && + + { + item.tagger.id ? + + + + : + + + + } + + } + 创建于{txt} + + ) + } + }, + { + title:"提交ID", + dataIndex:"id", + key:3, + ellipsis:true, + render:(txt,item)=>{ + return ( + + 提交ID + {truncateCommitId(item.id)} + + ) + } + }, + { + title:"描述信息", + dataIndex:"message", + key:4, + ellipsis:true, + render:(txt,item)=>{ + return item.message || "--" + } + }, + { + title:"下载", + dataIndex:"stage_type", + key:5, + ellipsis:true, + align:"center", + width:"204px", + render:(txt,item)=>{ + return ( + + + TAR + + + ZIP + + + ) + } + } + ] + return( +
        + + +
        + { + source && source.length > 0 && +
        + } + { + source && source.length === 0 && + } +
        +
        +
        + ) +} +export default Tags; \ No newline at end of file diff --git a/src/forge/Main/tag/Index.scss b/src/forge/Main/tag/Index.scss new file mode 100644 index 000000000..6adba91e3 --- /dev/null +++ b/src/forge/Main/tag/Index.scss @@ -0,0 +1,58 @@ +.tagTable{ + margin-top: 30px; + thead{ + tr th{ + background-color: #fff; + padding:5px 0px; + width: 172px; + .ant-table-column-title{ + font-size: 16px; + font-weight: 500; + color: #333333; + } + } + } + tbody{ + .btn-83{ + margin:0px 8px; + } + tr{ + &:hover td{ + background-color: #fff!important; + } + td{ + padding:0px; + height: 69px; + line-height: 69px; + color:#333333; + div{ + font-weight: 500; + } + } + &:last-child{ + td{ + border-bottom: none!important; + } + } + } + } +} +.tagSpin{ + min-height: 300px; +} +.tagBranch{ + padding-right: 15px; + text-overflow: ellipsis; + overflow: hidden; + .tagClass{ + color:#333333; + } +} +.tagModel{ + font-weight: 400; + .tagModelImg img{ + width: 25px; + height: 25px; + border-radius: 50%; + } +} \ No newline at end of file diff --git a/src/forge/Main/tree/Index.jsx b/src/forge/Main/tree/Index.jsx new file mode 100644 index 000000000..2fc574349 --- /dev/null +++ b/src/forge/Main/tree/Index.jsx @@ -0,0 +1,114 @@ +import React , { useEffect , useState } from 'react'; +import CopyTool from '../../Component/CopyTool'; +import { truncateCommitId } from '../../common/util'; +import { Link } from 'react-router-dom'; +import { getImageUrl } from 'educoder'; +import { Dropdown , Menu , Spin } from 'antd'; +import './Index.scss'; + +import Tree from '../img/tree.png'; +import Axios from 'axios'; + +function turnbar(str){ + if(str && str.length>0 && str.indexOf("/")>-1){ + return str.replaceAll('/','%2F'); + } + return str; +} +function Index(props) { + const [ list , setList ] = useState([]); + const [ isSpin , setIsSpin ] = useState(true); + + const { projectsId , owner } = props.match.params; + const { isManager , isDeveloper , projectDetail } = props; + + useEffect(()=>{ + getList(); + },[]) + + + const menu =(zip_url,tar_url)=> ( + + ZIP + TAR.GZ + + ) + + function getList() { + const url = `/${owner}/${projectsId}/branches_slice.json`; + Axios.get(url).then(result=>{ + if(result){ + setList(result.data); + } + setIsSpin(false); + }).catch(error=>{setIsSpin(false);}) + } + + return( + +
        + { + list && list.length>0 && list.map((item,key)=>{ + return( + +

        {item.branch_type === "default" ? "默认分支" : item.branch_type==="protected"?"保护分支":"其它分支"}

        + { + item.list && item.list.length>0 && +
          + { + item.list.map((i,k)=>{ + let last_commit = i.last_commit; + return( +
        • +
          + {i.name} +
          + { + last_commit && last_commit.committer && last_commit.committer.id? + + + {last_commit && last_commit.committer && last_commit.committer.name} + + : + + + {last_commit && last_commit.committer && last_commit.committer.name} + + } + 更新于{last_commit && last_commit.time_from_now} +
          +
          +
          +
          + + sha + {truncateCommitId(last_commit && last_commit.sha)} + + + +
          +
          +
          + { + (isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && + + 合并请求 + } + + 下载 + +
          +
        • + ) + }) + } +
        + } +
        + ) + }) + } +
        +
        + ) +} +export default Index; \ No newline at end of file diff --git a/src/forge/Main/tree/Index.scss b/src/forge/Main/tree/Index.scss new file mode 100644 index 000000000..f65a1c68f --- /dev/null +++ b/src/forge/Main/tree/Index.scss @@ -0,0 +1,101 @@ +.branchSort{ + font-weight: 500; + color: #333333; + font-size: 15px; + height: 20px; + line-height: 20px; + padding-left: 10px; + margin-top: 20px; + margin-bottom: 6px!important; +} +.treeUl{ + background: #FAFCFF; + border-radius: 4px; + border: 1px solid rgba(42, 97, 255, 0.23); + li{ + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 20px; + border-bottom: 1px solid rgba(42, 97, 255, 0.23); + &:last-child{ + border-bottom: none; + } + .treeinfo{ + width: 399px; + flex:1; + flex-direction: column; + &>a{ + display: block; + width: 399px; + + } + a:hover{ + span{ + color: #466AFF!important; + } + } + img{ + height: 20px; + width: 20px; + margin-right: 5px; + } + } + .treeabout{ + flex:1; + text-align: right; + } + } +} +.treecopy{ + flex:1; + display: flex; + justify-content: center; + &>div{ + height: 32px; + background: #FAFBFC; + border-radius: 4px; + border: 1px solid #D0D0D0; + position: relative; + z-index: 1; + display: flex; + align-items: center; + &>span{ + padding:0px 15px; + border-right: 1px solid rgba(153, 153, 153, 0.4); + height: 100%; + img{ + margin-right: 4px; + } + a{ + color: #466AFF; + &:hover{ + text-decoration: underline; + } + } + } + &>i{ + margin:0px 12px; + color: #333!important; + } + input{ + position: absolute; + z-index: 0; + opacity: 0; + top: 32px; + } + } +} +.new-conmmit{ + width: 30px; + height: 18px; + line-height: 18px; + display: block; + background: #FF6832; + color: white; + font-size: 12px; + border-radius: 4px; +} +.icon-a-yuanquan2x{ + color: #466AFF; +} \ No newline at end of file diff --git a/src/forge/Main/version/Empty.jsx b/src/forge/Main/version/Empty.jsx new file mode 100644 index 000000000..3200d3e8f --- /dev/null +++ b/src/forge/Main/version/Empty.jsx @@ -0,0 +1,22 @@ +import { Button } from 'antd'; +import React from 'react'; +import './version.scss'; + +function Empty({operation,addFunc}) { + return( +
        + + 这里暂未发布过任何版本 + 发行版功能基于仓库中的历史标记
        建议使用类似 V1.0 的版本标记作为发布点
        +
        + { + operation ? + + : + 该项目暂时没有发布版本 + } +
        +
        + ) +} +export default Empty; \ No newline at end of file diff --git a/src/forge/Main/version/Index.jsx b/src/forge/Main/version/Index.jsx new file mode 100644 index 000000000..37cc8c128 --- /dev/null +++ b/src/forge/Main/version/Index.jsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Switch , Route } from 'react-router'; +import Loadable from 'react-loadable'; +import Loading from '../../../Loading'; +import SubMenu from '../sub/SubMenu'; +import "./version.scss"; + +const CoderRootVersion = Loadable({ + loader: () => import('./version'), + loading: Loading, +}) +const CoderRootVersionNew = Loadable({ + loader: () => import('./New'), + loading: Loading, +}) +function Index(props) { + const { projectsId , owner } = props.match.params; + return( +
        + + + () + } + > + () + } + > + () + } + > + +
        + ) +} +export default Index; \ No newline at end of file diff --git a/src/forge/Main/version/New.jsx b/src/forge/Main/version/New.jsx new file mode 100644 index 000000000..8adde9b01 --- /dev/null +++ b/src/forge/Main/version/New.jsx @@ -0,0 +1,266 @@ +import React, { useState, useEffect, forwardRef } from "react"; +import styled from "styled-components"; +import { AutoComplete , Input, Checkbox, Button, Form } from "antd"; +import SelectBranch from '../../Branch/Select'; + +import Editor from "../../../modules/tpm/challengesnew/tpm-md-editor"; +import Upload from "../../Upload/Index"; +import Attachments from "../../Upload/attachment"; +import axios from "axios"; +import "./version.scss"; +import { trim } from "lodash"; + +const { Option } = AutoComplete; + +const Span = styled.span` + margin: 0px 15px; + color: #bbb; + line-height: 35px; + font-size:16px; + font-weight:400; + color:#666; +`; +export default Form.create()( + forwardRef( + ( + { form, projectDetail , match, showNotification, history }, + ref + ) => { + const { getFieldDecorator, validateFields, setFieldsValue } = form; + const [tagList, setTagList] = useState(undefined); + const [desc, setDesc] = useState(null); + const [branch, setBranch ] = useState(null); + const [fileList, setFileList] = useState(undefined); + const [attachment, setAttachment] = useState(undefined); + const [options , setOptions] = useState(undefined); + const stable = history && history.location && history.location.state.stable; + const { projectsId, versionId , owner } = match.params; + + useEffect(()=>{ + if(projectDetail && projectDetail.default_branch){ + setBranch(projectDetail.default_branch); + } + },[projectDetail]) + + + useEffect(() => { + if (versionId) { + const url = `/${owner}/${projectsId}/releases/${versionId}/edit.json`; + axios.get(url).then(result => { + if (result) { + setFieldsValue(result.data); + setDesc(result.data.body); + setAttachment(result.data.attachments); + } + }); + } + }, [versionId]); + + useEffect(() => { + if (projectsId) { + const url = `/${owner}/${projectsId}/tags.json`; + axios + .get(url,{params:{ + limit:1000 + }}) + .then(result => { + if (result) { + setTagList(result.data); + setOptions(renderTagList(result.data)); + } + }) + .catch(error => { + console.log(error); + }); + } + }, [projectsId]); + + function renderTagList(list) { + if (list) { + let array = list.map((item, key) => { + return ( + + ); + }); + return array || undefined; + } + } + function submit() { + validateFields((err, value) => { + if(err)return; + if (versionId) { + let url = `/${owner}/${projectsId}/releases/${versionId}.json`; + axios + .put(url, { + ...value, + body: desc, + attachment_ids: fileList, + target_commitish:branch + }) + .then(result => { + if (result) { + showNotification("版本修改成功!"); + history.push(`/${owner}/${projectsId}/releases`); + } + }); + } else { + let url = `/${owner}/${projectsId}/releases.json`; + axios.post(url, { + ...value, + body: desc, + attachment_ids: fileList + }) + .then(result => { + if (result) { + showNotification("版本发布成功!"); + history.push(`/${owner}/${projectsId}/releases`); + } + }); + } + }); + } + // 输入标签名 + function changeAuto(value){ + let l = tagList.filter(item=>item.name.indexOf(value) > -1); + setOptions(renderTagList(l)); + } + + function changeBranch(params) { + setBranch(params); + } + + return ( +
        +
        +
        + + {getFieldDecorator("tag_name", + { rules:[ + { required: true, message: "请输入获取或选择一个标签" }, + { validator: (rule,val,callback) =>{ + if(val.length>30 || val.indexOf(' ')>0 || val.match(/^\s+$/) || trim(val).length!=val.length){ + callback('无效的标签名称,请参考右侧建议命名标签并确认长度在1~30个字符之间'); + }else{ + callback(); + } + }}], + validateFirst: true + })( + + {options} + + )} + + @ + +

        选择一个已经存在的标签,或者在发布时新建一个标签

        +
        + + {getFieldDecorator("name", + { rules:[ + { required: true, message: "请输入发行版的标题" }, + { validator: (rule,val,callback) =>{ + if(val.length>50){ + callback('标题长度在1~50个字符之间'); + }else{ + callback(); + } + }}], + validateFirst: true + })( + + )} + + + +
        + + } + size={100} + showNotification={showNotification} + /> + {versionId && attachment && attachment.length > 0 ? ( + + ) : ( + "" + )} +
        + + {getFieldDecorator("prerelease", + { rules:[], + validateFirst: true + })( + 这是一个预览版本 + )} + +

        + + +

        + +
        +
        +

        标签命名建议

        +

        + 通常的做法是在版本名称前加上字母 v 前缀, v1.0 或者 v2.3.4。 +

        +

        + 如果标签不适合在生产环境下使用,请在版本名称后添加预发行版本。例如:v0.2-alpha + 或者 v5.9-beta.3。 +

        +
        +
        +

        语义化版本

        +

        + 如果你是第一次发布版本,我们强烈建议你阅读语义化版本。 +

        +
        +
        +

        附件大小说明

        +

        + 单个附件不能超过 100M(GVP 项目200M),每个仓库总附件不可超过 + 1G(推荐项目不可超过 5G;GVP 项目不可超过 + 20G)。附件总容量统计包括仓库附件和发行版附件。 +

        +
        +
        +
        + ); + } + ) +); diff --git a/src/forge/Main/version/version.js b/src/forge/Main/version/version.js new file mode 100644 index 000000000..2896899fc --- /dev/null +++ b/src/forge/Main/version/version.js @@ -0,0 +1,150 @@ +import React, { useEffect , useState } from "react"; +import { Link } from 'react-router-dom'; +import { Spin , Button } from 'antd'; +import { getImageUrl } from 'educoder'; +import {truncateCommitId} from '../../common/util'; +import Empty from './Empty'; +import './version.scss'; +import axios from 'axios'; +import Tree from '../img/tree-black.png'; +import RenderHtml from '../../../components/render-html'; +import User from "../../Component/User"; + +function version(props) { + const [ data , setData ] = useState(undefined); + const [ releases , setReleases ] = useState(undefined); + const [ isSpin , setIsSpin ] = useState(true); + const { projectsId ,owner } = props.match.params; + const { location } = props; + const type = props.projectDetail && props.projectDetail.type; + const turnFromNew = location && location.query && location.query.turnFromNew; + useEffect(()=>{ + getIssueList(); + },[]) + // 获取列表数据 + function getIssueList(){ + const url = `/${owner}/${projectsId}/releases.json`; + axios.get(url).then((result) => { + if (result) { + setData(result.data); + const { releases = [] } = result.data; + //默认第一个展开(body参数) + releases.length && (releases[0].bodyshow = true); + setReleases(result.data.releases); + setIsSpin(false); + } + }).catch((error) => { + console.log(error); + }) + } + // 显示版本描述 + function showBody(key,flag){ + var lists = releases.concat(); + lists[key].bodyshow = !flag ? true : false; + lists.splice(); + setReleases(lists); + } + //删除 + function deleteRelease(releaseId) { + if(releaseId){ + axios.delete(`/${owner}/${projectsId}/releases/${releaseId}.json`).then((result)=>{ + if(result){ + getIssueList(); + } + }) + } + } + function release(item,key){ + return ( +
        + + {item.draft} + + + {item.tag_name} + + + + {truncateCommitId(item.sha)} + + +
        +
        + {item.name} + + {data && data.user_admin_permission && type !== 2 && } + {data && data.user_admin_permission && type !== 2 && {deleteRelease(item.version_id)}}>} + +
        + + showBody(key,item.bodyshow)}> + + 发布于{item.created_at} + + { + item.bodyshow && +
        + +
        + } + +

        + {/* 发行版附件下载 */} + {item.attachments && item.attachments.map((item)=>{ + return(下载 {item.title}) + })} + {/* 发行版下载包 */} + 下载 {item.tag_name}.TAR.gz + 下载 {item.tag_name}.ZIP +

        +
        +
        + ) + } + function renderList(releases){ + if (releases && releases.length > 0) { + return ( + + { + data && data.user_admin_permission && type !== 2 && +
        + +
        + } +
        + {!turnFromNew ? releases.map((item, key) => release(item,key)) : release(releases[0],0)} +
        +
        + ) + } else if (releases && releases.length === 0) { + return ( + + ) + } else{ + return (
        ) + } + } + + function addFunc(){ + props.history.push({pathname:`/${owner}/${projectsId}/releases/new`,state:{stable:true}}); + } + + return ( +
        +
        + + {renderList(releases)} + +
        +
        + ) +} +export default version; \ No newline at end of file diff --git a/src/forge/Main/version/version.scss b/src/forge/Main/version/version.scss new file mode 100644 index 000000000..7cd5ddb46 --- /dev/null +++ b/src/forge/Main/version/version.scss @@ -0,0 +1,357 @@ +.topWrapper { + padding: 20px 0; + box-sizing: border-box; + display: flex; + justify-content: space-between; + border-bottom: 1px solid #EEEEEE; + align-items: center; +} +.topWrapper_btn_new { + background: #fff; + color: #5091FF!important; + padding:0px 12px; + text-align: center; + height: 32px; + line-height: 32px; + border-radius: 4px; + border:1px solid #5091FF; +} +.versionInfo{ + display: flex; + width: 100%; +} +.versionInfo_left{ + display: flex; + width: 182px; + flex-direction: column; + align-items: flex-end; + padding-right: 15px; + &>.color-grey-3{ + max-width: 10rem; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } +} +.versionInfo_right{ + flex: 1; + display: flex; + flex-direction: column; + align-items: flex-start; + border-left: 1px solid #eee; + position: relative; + padding: 0px 30px 60px 24px; + &::before{ + position: absolute; + left: -4px; + top:0px; + content: ''; + width: 8px; + height: 8px; + background-color: #466AFF; + border-radius: 50%; + } + .sendAuthorImg{ + width: 20px; + height: 20px; + border-radius: 50%; + margin-right: 5px; + } + .body-show{ + padding: 5px 10px 10px 10px; + } + & .version-user>a>span{ + display: inline-block; + & img{ + width: 20px; + height: 20px; + } + } +} +.versionTag{ + display: inline; + padding:0px 9px; + color: #fff; + position: relative; + margin-top: -8px; + height: 22px; + line-height: 20px; + border-radius: 4px; + &::before{ + position: absolute; + content: ""; + width: 0; + height: 0px; + border-left: 4px solid #cccccc; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-right: 4px solid transparent; + z-index: 9; + top: 6px; + right: -9px; + } + &:after{ + width: 0px; + height: 0px; + top:6px; + right: -7px; + position: absolute; + border-left:4px solid #fff; + border-top:4px solid transparent; + border-bottom:4px solid transparent; + border-right:4px solid transparent; + content:''; + z-index:10; + } +} +.versionFile{ + margin-top: 5px; + padding-top: 20px; + border-top: 1px solid #eee; + // width: 100%; + a{ + display: block; + color: #333; + font-weight: 400; + height: 20px; + margin-bottom: 10px; + } +} +.versionTag.yellow{ + border: 1px solid #FBBC06; + color: #FBBC06; + &::before{ + border-left-color: #FBBC06; + } +} +.versionTag.green{ + border: 1px solid #2DB44D; + color: #2DB44D; + &::before{ + border-left-color: #2DB44D; + } +} +.versionTag.orange{ + border: 1px solid #FF6E23; + color: #FF6E23; + &::before{ + border-left-color: #FF6E23; + } +} +.addReleaseBtn{ + text-align: right; + margin-bottom: 30px; +} +.versionName{ + font-size: 16px; + color: #333; + margin-bottom: 15px; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + height: 18px; + line-height: 18px; + margin-top: -5px; +} + + +.versionmilepostleft{ + padding: 15px; + margin-right: 50px; + width: 80%; + } +.topWrapper_btn_close { + background: #504b4b; + color: #FFFFFF!important; + padding:0px 12px; + text-align: center; + height: 32px; + line-height: 32px; + border-radius: 4px; + } + + .topWrapper_btn_delete { + background: #da1010; + color: #FFFFFF!important; + padding:0px 12px; + text-align: center; + height: 32px; + line-height: 32px; + border-radius: 4px; + } + + .versionrighe{ + flex: 2; + } + .versionleft{ + flex: 1; + text-align: right; + display: flex; + justify-content: right; + } + + /* .version_line{ + display: flex; + height: 30px; + margin: auto; + border-left:1px solid #eee; + } */ + .version_line_one{ + display: flex; + height: 45px; + margin: auto; + border-left:1px solid #eee; + } + + .version_line_tpw{ + display: flex; + height: 80px; + margin: auto; + border-left:1px solid #eee; + } + + .versiondiv{ + display: flex; + } + .verwinth{ + width: 80%; + } + + /*开启中 关闭中*/ +.opendversionetail{ + display: inline-block; + background: #21ba45; + color: #ffffff!important; + padding:0px 5px; + text-align: center; + height: 25px; + /*width: 110px;*/ + border-radius: 4px; + line-height: 25px; +} +.closedversionetail{ + display: inline-block; + background: #e60b0b; + color: #ffffff!important; + padding:0px 5px; + text-align: center; + height: 25px; + /*width: 110px;*/ + border-radius: 4px; + line-height: 25px; +} +.versionrectangle { + width: 8px; + height: 8px; + border-radius: 100%; + margin-top: 15px; + margin-left: -4px; + margin-bottom: 10px; + background: rgb(83, 81, 81); + } +.ver-middle{ + vertical-align: middle; +} +/* new */ +.versionForm{ + flex:1; + padding-right: 40px; + box-sizing: border-box; + .ant-select-auto-complete.ant-select .ant-input:hover,.ant-input:hover { + border-color: rgba(153, 153, 153, 0.8); + } +} +.versionTips{ + width:268px; + box-sizing: border-box; +} +.infosTip{ + border-bottom: 1px solid #EEEEEE; + color: #333; + padding-bottom: 26px; + margin-bottom: 26px; + font-weight: 400; + text-align: justify; + &:last-child{ + border-bottom: none; + } +} +.dragBox{ + background: rgba(153, 153, 153, 0.04); + border-radius: 4px; + border: 1px dashed #d9d9d9; + padding:20px; + .versionStyle{ + border: none!important; + padding-bottom:20px; + .dragIcon{ + font-size: 40px!important; + color: #666!important; + line-height: 40px; + height: 40px; + margin-bottom: 14px; + display: block; + } + } + .ant-upload-list-item:hover .ant-upload-list-item-info { + background-color: rgba(239, 244, 255, 1); + } + .ant-upload-list-item-info{ + padding:0px 20px 0px 8px; + &>span{ + display: flex; + align-items: center; + } + } +} + +.set-ant-row .ant-row{ + display: flex; + height: 20px; + align-items: center; +} +.itemInline{ + display: flex; + align-items: flex-start; + position: relative; + &>p{ + position: absolute; + bottom: -5px; + } +} +.itemInline .ant-row{ + margin-bottom: 0px; +} +.prerelease{ + padding-top: 20px; + .ant-form-item-control{ + height: 20px; + line-height: 20px; + } +} +.releaseIndex{ + margin: 30px auto; + width: 1200px; +} +.emptyPanel{ + width: 100%; + background: #FAFCFF; + border-radius: 4px; + border: 1px solid rgba(42, 97, 255, 0.23); + min-height: 418px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + .operation{ + width: 400px; + border-top: 1px solid #eee; + padding-top: 34px; + text-align: center; + margin-top: 30px; + } +} +.ant-form-item-control{ + line-height: initial; +} \ No newline at end of file diff --git a/src/forge/Merge/CreateMerge.js b/src/forge/Merge/CreateMerge.js new file mode 100644 index 000000000..d7a320099 --- /dev/null +++ b/src/forge/Merge/CreateMerge.js @@ -0,0 +1,448 @@ +import React, { Component } from 'react'; +import { Input, Select, Spin, Alert } from 'antd'; +import axios from 'axios'; +import MergeForm from './merge_form'; +import MergeFooter from './merge_footer'; + +import '../Order/order.css'; +import './merge.css'; + +/** + * 根据url获取目标仓库、目标分支、源仓库、源分支 + * 路由规则:owner/projectId/compare/merge...pullowner:pullBranch + * 可能存在的情况 + * 1、代码库首页跳转,仓库相同,目标分支为默认分支,owner/projectId/compare/pullBranch + * 2、代码库分支列表,仓库相同,目标分支为默认分支,owner/projectId/compare/pullBranch + * 3、合并请求列表页(新建、无数据时的提示),仓库相同,源、目标都为默认分支,owner/projectId/compare + * 4、新建页面,切换分支、切换目标仓库、刷新页面等,存在所有可能情况 + */ +function getBranchParams(pathname) { + const result = { + // 目标仓库所有者 + mergeOwner: undefined, + // 目标分支 + mergeBranch: 'master', + // 源仓库所有者 + pullOwner: undefined, + // 源分支 + pullBranch: 'master', + // 仓库名称 + projectId: undefined, + }; + // 去掉第一个字符/ + const _pathname = pathname.slice(1); + const [ownerProject, branchUrl] = _pathname.split('/compare'); + const [mergeOwner, projectId] = ownerProject.split('/'); + // 同仓库时 + result.mergeOwner = mergeOwner; + result.pullOwner = mergeOwner; + result.projectId = projectId; + if (branchUrl) { + // 如果存在具体的分支 + const _branchUrl = branchUrl.slice(1); + if (_branchUrl.indexOf('...') > -1) { + // 存在源分支与目标分支 + const [mergeBranch, pullObj] = _branchUrl.split('...'); + result.mergeBranch = mergeBranch; + if (pullObj.indexOf(':') > -1) { + // 存在源仓库 + const [pullOwner, pullBranch] = pullObj.split(':'); + result.pullOwner = pullOwner; + result.pullBranch = pullBranch; + } else { + result.pullBranch = pullObj; + } + } else { + result.pullBranch = _branchUrl; + } + } + return result; +} + +const Option = Select.Option; + +class CreateMerge extends Component { + constructor(props) { + super(props); + const { pullBranch, mergeBranch } = getBranchParams( + this.props.location.pathname + ); + this.state = { + data: undefined, + pullBranches: undefined, + mergeBranches: undefined, + mergeProjects: undefined, + merge: mergeBranch || 'master', + pull: pullBranch || 'master', + id: undefined, + // isFork: false, + projects_names: undefined, + isSpin: true, + showMessage: false, + merge_head: false, // 是否向fork后的源项目发起合并请求 + defaultMessage: '必须选择不同的分支', + project_id: undefined, // 当前项目的id,也即开始发送合并请求的源项目id + merge_project_user: undefined, + comparesData: undefined, //提交和文件的内容,保存compare接口返回的数据 + // 比较分支时的加载效果 + isCompareSpin: true, + // 是否是初次加载,用这个字段来控制提示组件和文件组件的显示、隐藏比直接用isCompareSpin交互友好些 + isFirstLoading: true, + }; + } + + componentDidMount = () => { + // 初始化时根据url获取目标仓库、分支,源仓库、分支; + // 再获取对应的仓库列表、分支列表 + // 再调用比较接口 + const branchParams = getBranchParams(this.props.location.pathname); + this.getMergeInfo(branchParams); + }; + + componentDidUpdate = (preProps) => { + // url变化触发时(切换源分支、切换目标仓库、切换目标分支;回退) + const oldPathname = preProps.location.pathname; + const newPathname = this.props.location.pathname; + if (oldPathname !== newPathname) { + const branchParams = getBranchParams(newPathname); + this.getMergeInfo(branchParams); + } + }; + + //获取新建合并请求数据 + getMergeInfo = (branchParams) => { + this.setState({ isSpin: true }); + const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } = + branchParams; + const url = `/${pullOwner}/${projectId}/pulls/new.json`; + axios + .get(url) + .then((result) => { + if (result) { + // 如果url上的分支不存在,取默认值master + const noMergeBranch = + (result.data.branches || []).filter( + (branch) => branch.name === mergeBranch + ).length === 0; + const noPullBranch = + (result.data.branches || []).filter( + (branch) => branch.name === pullBranch + ).length === 0; + this.setState({ + // isFork: result.data.is_fork, + projects_names: result.data.projects_names, + mergeProjects: result.data.merge_projects, + pullBranches: result.data.branches, + mergeBranches: result.data.branches, + project_id: result.data.project_id, + id: result.data.id, + merge: mergeBranch, + pull: pullBranch, + }); + + //判断源分支是否存在 + if(noPullBranch){ + this.setState({ + showMessage: true, + defaultMessage:'源分支不存在', + isCompareSpin: false, + }); + }else{ + if(pullOwner === mergeOwner){ + if (!noMergeBranch) { + this.compareProject(true, branchParams); + } else { + this.setState({ + showMessage: true, + defaultMessage:'目标分支不存在', + isCompareSpin: false, + }); + } + }else{ + this.getBranchList(branchParams); + } + } + } + this.setState({ isSpin: false }); + }) + .catch((error) => { + this.setState({ isSpin: false }); + console.log(error); + }); + }; + + // compare接口,获取分支对比信息 + compareProject = (sameProject, branchParams) => { + // const { project } = this.props; + // const { owner, projectsId } = this.props.match.params; + const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } = + branchParams; + + let url = `/${mergeOwner}/${projectId}/compare`; + if (sameProject) { + url += `/${pullBranch}...${mergeBranch}.json`; + } else { + url += `/${mergeBranch}...${pullOwner}/${projectId}:${pullBranch}.json`; + } + this.setState({ isSpin: false, isCompareSpin: true }); + axios + .get(url) + .then((result) => { + if (result) { + if (result.data.status === 0) { + this.setState({ + showMessage: false, + }); + } else { + this.setState({ + showMessage: true, + defaultMessage: result.data.message, + }); + } + this.setState({ + comparesData: result.data, + }); + } + this.setState({ + isFirstLoading: false, + isSpin: false, + isCompareSpin: false, + }); + }) + .catch((error) => { + this.setState({ isSpin: false, isCompareSpin: false }); + }); + }; + + // 根据所有者、仓库名,获取分支列表,目前仅涉及目标仓库分支查询 + getBranchList = (branchParams) => { + const { mergeOwner, projectId, mergeBranch } = branchParams; + this.setState({ isSpin: true }); + const url = `/${mergeOwner}/${projectId}/pulls/get_branches.json`; + axios + .get(url) + .then((result) => { + if (result) { + const noMergeBranch = + (result.data || []).filter((branch) => branch.name === mergeBranch) + .length === 0; + this.setState({ + mergeBranches: result.data, + showMessage: noMergeBranch, + defaultMessage: '目标分支不存在', + isCompareSpin: false, + }); + !noMergeBranch && this.compareProject(false, branchParams); + } + this.setState({ isSpin: false }); + }) + .catch((error) => { + this.setState({ isSpin: false }); + console.log(error); + }); + }; + + // 切换分支事件 + selectBrach = (type, value) => { + const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } = + getBranchParams(this.props.location.pathname); + let _url = `/${mergeOwner}/${projectId}/compare/`; + // type为pull时,pullBranch取value,否则取原有值 + // type为pull时,mergeBranch取原有值,否则取value + let _pullBranch = type === 'pull' ? value : pullBranch; + let _mergeBranch = type === 'pull' ? mergeBranch : value; + if (pullOwner === mergeOwner) { + // 如果仓库相同, compare/目标分支...源分支 + _url += `${_mergeBranch}...${_pullBranch}`; + } else { + // 如果仓库不同, compare/目标分支...源分支 + _url += `${_mergeBranch}...${pullOwner}:${_pullBranch}`; + } + this.props.history.push(_url); + }; + + // 切换仓库响应事件,目前仅目标分支可切换仓库 + selectProjectName = (value) => { + const { projects_names, id } = this.state; + const { pullOwner, pullBranch } = getBranchParams( + this.props.location.pathname + ); + let arr = + projects_names && projects_names.filter((item) => item.id === value); + let identifier = arr && arr[0].project_id; + let login = arr && arr[0].project_user_login; + // 目标仓库与源仓库不是一个仓库 + let is_fork = parseInt(value, 10) !== parseInt(id, 10); + this.setState({ + isSpin: true, + // merge_head: is_fork, + data: { + is_original: is_fork, + fork_project_id: is_fork ? id : '', + merge_user_login: is_fork + ? projects_names[0].project_user_login + : undefined, + }, + }); + if (login === pullOwner) { + // 如果切换后, 仓库与源仓库一致了 + this.props.history.push( + `/${login}/${identifier}/compare/master...${pullBranch}` + ); + } else { + this.props.history.push( + `/${login}/${identifier}/compare/master...${pullOwner}:${pullBranch}` + ); + } + // this.newMergelist(login, identifier); + }; + + // 渲染分支列表 + renderBrances = (list) => { + if (list && list.length > 0) { + return list.map((item, key) => { + return ( + + ); + }); + } + }; + + // 渲染项目列表 + renderProjectNames = (list) => { + if (list && list.length > 0) { + return list.map((item, key) => { + return ( + + ); + }); + } + }; + + // 渲染html内容 + withHtml = (html) => { + return
        ; + }; + + render() { + const { + data, + pullBranches, + mergeBranches, + mergeProjects, + pull, + merge, + isSpin, + isCompareSpin, + isFirstLoading, + showMessage, + defaultMessage, + projects_names, + id, + comparesData, + } = this.state; + + let { project } = this.props; + + return ( +
        + +
        +
        +
        +
        源分支:
        + + + + +
        +
        + +
        +
        +
        +
        目标分支:
        + + + + +
        +
        +
        + {/* 非加载状态且有提示 */} + {!isCompareSpin && showMessage && ( +
        + +
        + )} + {/* 非加载状态且可以提交 */} + {!isCompareSpin && !showMessage && ( + + )} +
        + {!isFirstLoading && ( + + )} +
        +
        + ); + } +} + +export default CreateMerge; diff --git a/src/forge/Merge/Files.jsx b/src/forge/Merge/Files.jsx index 82a8638c6..b4cf5b488 100644 --- a/src/forge/Merge/Files.jsx +++ b/src/forge/Merge/Files.jsx @@ -1,17 +1,24 @@ import React ,{useEffect,useState } from 'react'; import { truncateCommitId } from '../common/util'; import { AlignCenter , FlexAJ } from '../Component/layout'; -import { Button } from 'antd'; +import { Tooltip,Progress } from 'antd'; import './merge.css'; +import './Index.scss'; -function Files({data,history,owner,projectsId}){ +function Files({ data,history,owner,projectsId , parentsSha }){ const [ files , setFiles ] = useState(data && data.files); - + const [ copyfileTipTitle, setCopyfileTipTitle] = useState("复制文件路径"); + const [ isOpen, setIsOpen] = useState(false); + useEffect(()=>{ if(data){ setFiles(data.files); } - },[data]) + },[data]); + + useEffect(()=>{ + document.addEventListener('click',()=>{setIsOpen(false)}) + }) function showDown(flag,index,isBin){ if(!isBin){ @@ -22,35 +29,86 @@ function Files({data,history,owner,projectsId}){ } } + function copyFileName(fileName){ + var copyCont = document.createElement('input'); + copyCont.defaultValue = fileName; + document.body.appendChild(copyCont); + copyCont.select(); // 选择对象 + document.execCommand("Copy"); // 执行浏览器复制命令 + copyCont.className = 'copyCont'; + copyCont.style.display='none'; + setCopyfileTipTitle("复制成功"); + } + + const folderOpen = ( + + ) + return( -
        - - - - 共有 {data && data.files_count} 个文件被更改,包括 +
        {e.nativeEvent.stopImmediatePropagation()}}> + +
        {setIsOpen(!isOpen)}}> + + + 共有 {data && data.files_count} 个文件 被更改,包括 { data && data.total_addition ? {data && data.total_addition} 次插入:"" } { data && data.total_addition && data.total_deletion ? " 和 ":""} { data && data.total_deletion ? {data && data.total_deletion} 次删除:""} - + +
        + {isOpen && folderOpen}
        { files && files.length>0 && -
        +
        { files.map((item,key)=>{ return(
        - showDown(item.flag,key,item.isBin)}> + + - {!item.isBin ? :""} - - {item.name} + {!item.isBin ? showDown(item.flag,key,item.isBin)}>:""} + showDown(item.flag,key,item.isBin)}>{item.name} + setCopyfileTipTitle("复制文件路径")} + > + copyFileName(item.name)}> + - - - +{item.addition} - -{item.deletion} - +
        + 0 ? ":":""} ${item.addition > 0 ? item.addition + "处添加" : ""}${item.addition > 0 && item.deletion > 0 ? "和" : ""}${item.deletion > 0 ? item.deletion + "处删除" : ""}`}> + + {item.addition+item.deletion}处 + + { + !item.isSubmodule && + {history.push(`/${owner}/${projectsId}${item.isDeleted ? `/commits/${truncateCommitId(parentsSha)}`:`/tree/${truncateCommitId(item.sha)}/${item.name}`}`)}}>查看文件 + } +
        { item.sections && item.sections.length >= 1 && !item.flag && diff --git a/src/forge/Merge/Index.scss b/src/forge/Merge/Index.scss index 936b868f8..16cc95632 100644 --- a/src/forge/Merge/Index.scss +++ b/src/forge/Merge/Index.scss @@ -18,4 +18,86 @@ .pr_tags_closed{ border:1px solid #FA6400; color: #FA6400; +} +.update-file-count{ + cursor: pointer; + & .color-grey-3{ + font-weight: bold; + } +} +.fileList{ + .sc-bxivhb{ + width: 55rem; + overflow: hidden; + white-space: normal; + word-break: break-all; + } + .see-file{ + width: 14rem; + .ml10{ + display: inline-block; + width: 4.5rem; + cursor: default; + } + span{ + width: 7%; + } + } + .anchorPoint{ + position: relative; + top: -5rem; + display: block; + height: 0; + } +} +.filesInfo{ + background: #FAFCFF; + border-color:rgba(42, 97, 255, 0.23); + .ant-progress-line { + width: 5rem; + } + .ant-progress-inner{ + background-color: #D14A4A; + } +} +.folders,.ant-anchor{ + margin-left: 0; + padding-left: 0; +} +.folders{ + position: absolute; + z-index: 2; + left: 0px; + top: 37px; + color: #333333; + width: 75rem; + box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5); + border-radius: 4px; + border: 1px solid rgba(153, 153, 153, 0.32); + .ant-anchor-link-active > .ant-anchor-link-title { + color: #466AFF; + } + .ant-anchor-link { + padding: 0px; + } + .folderList{ + max-height: 275px; + overflow:auto; + .files{ + border: 0px; + } + .filesInfo { + padding: 10px 18px 10px 15px; + height: 55px; + background: #FFF; + border-bottom: 1px solid #EEEEEE; + &:hover{ + background: #F3F4F6; + } + .color-green,.color-red{ + width: 3%; + text-align: right; + } + } + } } \ No newline at end of file diff --git a/src/forge/Merge/MergeDetail.js b/src/forge/Merge/MergeDetail.js index 76c31ba0a..11bd3858b 100644 --- a/src/forge/Merge/MergeDetail.js +++ b/src/forge/Merge/MergeDetail.js @@ -228,7 +228,7 @@ class MergeDetail extends Component {
        { data && data.issue.user_permission ? - 编辑 + 编辑 : '' }
        diff --git a/src/forge/Merge/MergeItem.js b/src/forge/Merge/MergeItem.js index 884c50feb..61477c142 100644 --- a/src/forge/Merge/MergeItem.js +++ b/src/forge/Merge/MergeItem.js @@ -62,9 +62,9 @@ class MergeItem extends Component {

        {item.name} @@ -110,7 +110,7 @@ class MergeItem extends Component { {item.is_original ? item.fork_project_user @@ -134,7 +134,7 @@ class MergeItem extends Component { {/* {item.is_fork ? item.pull_request_base : `${item.author_name}:${item.pull_request_base}`} */} {project_author_name}:{item.pull_request_base} @@ -175,7 +175,7 @@ class MergeItem extends Component { {item.journals_count ? ( {item.journals_count} @@ -196,7 +196,7 @@ class MergeItem extends Component { >

        diff --git a/src/forge/Merge/MergeLinkFooter.jsx b/src/forge/Merge/MergeLinkFooter.jsx new file mode 100644 index 000000000..2c9aa4814 --- /dev/null +++ b/src/forge/Merge/MergeLinkFooter.jsx @@ -0,0 +1,210 @@ +import React, { Component } from 'react'; +import { Tabs, Spin } from 'antd'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; + +import Commits from './Commits'; +import Comments from '../comments/comments'; +import Files from './Files'; + +import '../Order/order.css'; +import './merge.css'; + +const { TabPane } = Tabs; + +class MergeFooter extends Component { + constructor(props) { + super(props); + this.state = { + commitsData: [], + filesData: undefined, + isSpin: false, + activeKey: '1', + commitCount: 0, + filesCount: 0, + // 总评论数量,包含回复 + commentsTotalCount: 0, + }; + } + componentDidMount() { + this.Init(); + // 为父组件绑定当前,以方便调用方法 + this.props.bindFootRef && this.props.bindFootRef(this); + } + + componentDidUpdate(prevProps) { + // 解决切换tab后浏览器回退不刷新的问题、点击tab后url变化但tab未切换的问题 + const newPathname = this.props.location.pathname; + const prevPathname = prevProps.location.pathname; + if (newPathname !== prevPathname) { + this.Init(true); + } + } + + Init = (isTabChange) => { + const { data, location, match } = this.props; + const { pathname } = location; + const { projectsId, owner, mergeId } = match.params; + + let activeKey = '1'; + if (pathname.indexOf('commits') > -1) { + activeKey = '2'; + this.getCommit(owner, projectsId, mergeId); + } else if (pathname.indexOf('files') > -1) { + activeKey = '3'; + this.getFile(owner, projectsId, mergeId); + } + if (isTabChange && activeKey === '1') { + this.refreshComment(); + } + this.setState({ + activeKey: activeKey, + commitCount: data && data.commits_count, + filesCount: data && data.files_count, + }); + }; + + bindCommentRef = (commentRef) => { + this.childComment = commentRef; + } + + refreshComment = () => { + this.childComment && this.childComment.getjournalslist(); + } + + getCommit = (owner, projectsId, mergeId) => { + this.setState({ isSpin: true }); + const url = `/${owner}/${projectsId}/pulls/${mergeId}/commits.json`; + axios + .get(url) + .then((result) => { + if (result) { + this.setState({ + commitsData: result.data.commits, + commitCount: result.data.commits_count, + }); + } + this.setState({ isSpin: false }); + }) + .catch((error) => { + this.setState({ isSpin: false }); + }); + }; + + getFile = (owner, projectsId, mergeId) => { + this.setState({ isSpin: true }); + const url = `/${owner}/${projectsId}/pulls/${mergeId}/files.json`; + axios + .get(url) + .then((result) => { + if (result) { + this.setState({ + filesData: result.data, + filesCount: result.data.files_count, + }); + } + this.setState({ isSpin: false }); + }) + .catch((error) => { + this.setState({ isSpin: false }); + }); + }; + + render() { + const { projectsId, owner, mergeId } = this.props.match.params; + + const { order_id, data = {} } = this.props; + const { + isSpin, + activeKey, + filesCount, + commitCount, + filesData, + commitsData = [], + } = this.state; + + // 评论数量优先取Comment组件中列表接口返回的,其次取合并请求详情接口中的,都没有取默认值0 + const commentsTotalCount = parseInt( + this.state.commentsTotalCount || data.comments_total_count || 0, + 10 + ); + + return ( +
        + + + + 评论 + {commentsTotalCount > 0 && ( + {commentsTotalCount} + )} + + } + key="1" + > + { + this.setState({ commentsTotalCount: commentsCount || 0 }); + }} + {...this.props} + bindCommentRef={this.bindCommentRef} + /> + + {commitCount > 0 && ( + + 提交 + {commitCount > 0 && ( + {commitCount} + )} + + } + key="2" + > + {commitsData.length > 0 && ( + + )} + + )} + {filesCount > 0 && ( + + 文件 + {filesCount > 0 && ( + {filesCount} + )} + + } + key="3" + > + + + )} + + +
        + ); + } +} +export default MergeFooter; diff --git a/src/forge/Merge/MessageCount.js b/src/forge/Merge/MessageCount.js index d1b46b865..6d01e8a37 100644 --- a/src/forge/Merge/MessageCount.js +++ b/src/forge/Merge/MessageCount.js @@ -1,7 +1,6 @@ import React, { Component } from "react"; import { Tabs } from 'antd'; import { Link } from "react-router-dom"; -import { AlignCenter } from '../Component/layout'; import axios from "axios"; import { getImageUrl } from "educoder"; import { @@ -11,7 +10,6 @@ import { Dropdown, Icon, Menu, - Select, Tag, Button, Alert, @@ -19,9 +17,8 @@ import { import "./merge.css"; import RenderHtml from "../../components/render-html"; import "../Order/order.css"; -import MergeFooter from "./merge_footer"; +import MergeLinkFooter from "./MergeLinkFooter"; -const Option = Select.Option; const TextArea = Input.TextArea; function turnbar(str){ @@ -61,6 +58,11 @@ class MessageCount extends Component { // this.clickBody(); }; + + bindFootRef = (footRef) => { + this.footRef = footRef; + } + clickBody=()=>{ document.body.addEventListener('click', e => { let name = e.target.className; @@ -150,6 +152,8 @@ class MessageCount extends Component { }); const { getDetail } = this.props; getDetail && getDetail(); + // 调用子组件的方法刷新评论列表 + this.footRef && this.footRef.refreshComment(); } else { this.setState({ SpinMerge: false }); } @@ -283,13 +287,13 @@ class MessageCount extends Component { conflict_files && conflict_files.length>0 &&

        如下文件有代码冲突:

        -

        +

        { conflict_files.map((i,k)=>{ - return

        {i}

        + return

        {i}

        }) } -

        +
        }
        @@ -336,11 +340,11 @@ class MessageCount extends Component {
        -
        -
        +
        +
        - + {data.issue.subject} @@ -362,9 +366,9 @@ class MessageCount extends Component { - {data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}: {turnbar(data.pull_request && data.pull_request.head)} + {data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}: {data.pull_request && data.pull_request.head} @@ -377,7 +381,7 @@ class MessageCount extends Component { {data.issue.project_author_name}:{data.pull_request.base} @@ -424,7 +428,7 @@ class MessageCount extends Component { - 标签: + 标记: {data.issue.issue_tags && data.issue.issue_tags.length > 0 @@ -441,14 +445,13 @@ class MessageCount extends Component {
        -
        -
        +
        {operate && ( @@ -465,7 +468,6 @@ class MessageCount extends Component { )}
        -
        { data.issue.description ? @@ -543,7 +545,7 @@ class MessageCount extends Component { onChange={this.changbodypr} />
        -

        @@ -558,19 +560,19 @@ class MessageCount extends Component { 取消 -

        +
        )}
        - + bindFootRef={this.bindFootRef} + >
        ) : ( "" diff --git a/src/forge/Merge/NewMerge.js b/src/forge/Merge/NewMerge.js index 956be0006..900dea824 100644 --- a/src/forge/Merge/NewMerge.js +++ b/src/forge/Merge/NewMerge.js @@ -6,6 +6,11 @@ import "./merge.css"; import MergeForm from "./merge_form"; import MergeFooter from "./merge_footer"; const Option = Select.Option; +/** + * 此文件已废弃,新文件为CreateMerge.js + * 2021.10.12 + */ + class NewMerge extends Component { constructor(props) { super(props); @@ -48,8 +53,10 @@ class NewMerge extends Component { this.compareProject(this.state.id,pull,"master"); } } + // 页面销毁取消监听 componentWillUnmount () { + console.log('----------destoyed---------'); window.removeEventListener('popstate', this.handleBack, false); }; @@ -60,8 +67,11 @@ class NewMerge extends Component { //获取新建分支数据 getmergelist = (projectsId) => { - this.setState({isSpin: true}) - const { owner } = this.props.match.params; + this.setState({isSpin: true}); + // const { owner } = this.props.match.params; + let owner =this.props.history.location.pathname.split('/')[1]; + console.log('owner:'+owner); + console.log(this.props); const url = `/${owner}/${projectsId}/pulls/new.json`; axios .get(url) @@ -105,6 +115,7 @@ class NewMerge extends Component { const { author , identifier } =oldProject; url += `/${mergeBranch}...${author && author.login}/${identifier}:${localBranch}.json`; } + this.setState({isSpin: true}); axios.get(url).then(result=>{ if(result){ if (result.data.status === 0) { @@ -123,7 +134,9 @@ class NewMerge extends Component { comparesData:result.data }) } - }).catch(error=>{}) + }).catch(error=>{ + this.setState({isSpin: false}); + }) } } @@ -186,7 +199,7 @@ class NewMerge extends Component { // this.ischeckmerge(); let { id ,merge , pull } = this.state; if(type==="pull"){ - this.props.history.push(`/${owner}/${projectsId}/pulls/new/${pull}`) + this.props.history.push(`/${owner}/${projectsId}/compare/${pull}`) this.compareProject(id,value,merge); }else{ this.compareProject(id,pull,value); @@ -208,7 +221,7 @@ class NewMerge extends Component { merge_user_login: is_fork_id ? projects_names[0].project_user_login : undefined } }) - this.props.history.push(`/${login}/${identifier}/pulls/new`); + this.props.history.push(`/${login}/${identifier}/compare`); this.newMergelist(login,identifier); }; @@ -264,7 +277,9 @@ class NewMerge extends Component { show_message, default_message, merge_head, - projects_names,id,comparesData + projects_names, + id, + comparesData } = this.state; const renderBrances = (list, type) => { @@ -298,6 +313,7 @@ class NewMerge extends Component { return
        ; }; let { project } = this.props; + return (
        diff --git a/src/forge/Merge/UpdateMerge.js b/src/forge/Merge/UpdateMerge.js index 8ed8034af..8918db6eb 100644 --- a/src/forge/Merge/UpdateMerge.js +++ b/src/forge/Merge/UpdateMerge.js @@ -64,12 +64,12 @@ class UpdateMerge extends Component {
        源分支:
        - {" "} {" "} @@ -83,12 +83,12 @@ class UpdateMerge extends Component {
        目标分支 :
        {" "} - {" "} {" "} diff --git a/src/forge/Merge/merge.css b/src/forge/Merge/merge.css index 6b50eeb10..d61543b49 100644 --- a/src/forge/Merge/merge.css +++ b/src/forge/Merge/merge.css @@ -40,6 +40,7 @@ form .ant-cascader-picker, form .ant-select { } .merge-header-button{ background:rgba(241,248,255,1); + text-align: left; } .width70{ width:70%; @@ -152,13 +153,23 @@ form .ant-cascader-picker, form .ant-select { margin-top: 15px; border-radius: 2px; } +.see-file-btn{ + color: #466AFF; + cursor: pointer; +} .filesInfo{ padding:10px 15px; background-color: #fafafa; } +.filesInfo .cursor-pointer{ + cursor: pointer; +} .filesContent{ border-top: 1px solid #ddd; } +.icon-fuzhiicon:hover{ + color: #466AFF; +} .linesContent{ display: flex; min-height: 30px; @@ -198,4 +209,17 @@ form .ant-cascader-picker, form .ant-select { } .linesContent.add{ background: rgba(48, 232, 132, 0.15); -} \ No newline at end of file +} + +.mergeRequest .folders{ + /* width: 72rem; */ + width: 100%; + } + + .matchwidth .ant-select-selection__rendered{ + width: 200px; + } + .overlihide li{ + max-width: 450px; + + } \ No newline at end of file diff --git a/src/forge/Merge/merge.js b/src/forge/Merge/merge.js index 1e1285e4e..8a0eaedea 100644 --- a/src/forge/Merge/merge.js +++ b/src/forge/Merge/merge.js @@ -43,7 +43,7 @@ class merge extends Component { //设置选择高亮 openselect: 1, closeselect: undefined, - issue_tag_ids: "标签", + issue_tag_ids: "标记", fixed_version_ids: "里程碑", assigned_to_ids: "审查人员", paix: "排序", @@ -198,7 +198,7 @@ class merge extends Component { }); this.setState({ status_type: type, - issue_tag_ids: "标签", + issue_tag_ids: "标记", fixed_version_ids: "里程碑", assigned_to_ids: "审查人员", paix: "排序", @@ -213,7 +213,7 @@ class merge extends Component { checkOperation() { const { projectsId,owner } = this.props.match.params; - this.props.history.push(`/${owner}/${projectsId}/pulls/new`); + this.props.history.push(`/${owner}/${projectsId}/compare/master...master`); } render() { const { projectsId , owner } = this.props.match.params; @@ -321,7 +321,7 @@ class merge extends Component { className="topWrapperSelect" overlay={this.renderMenu( issue_chosen && issue_chosen.issue_tag, - "标签", + "标记", "issue_tag_id" )} trigger={["click"]} diff --git a/src/forge/Merge/merge_footer.js b/src/forge/Merge/merge_footer.js index 6502827cd..868e736fe 100644 --- a/src/forge/Merge/merge_footer.js +++ b/src/forge/Merge/merge_footer.js @@ -1,169 +1,84 @@ -import React, { Component } from "react"; -import { Tabs, Spin } from "antd"; -import "../Order/order.css"; -import "./merge.css"; -import Commits from "./Commits"; -import Comments from "../comments/comments"; -import Files from "./Files"; -import axios from 'axios'; +import React, { Component } from 'react'; +import { Tabs } from 'antd'; +import Commits from './Commits'; +import Files from './Files'; + +import '../Order/order.css'; +import './merge.css'; + const { TabPane } = Tabs; class MergeFooter extends Component { - constructor(props){ + constructor(props) { super(props); - this.state={ - pageData:undefined, - commitsData:undefined, - filesData:undefined, - isSpin:false, - activeKey:"1", - commitCount:0, - filesCount:0 - } - } - componentDidMount=()=>{ - const { footer_type ,data } = this.props; - if(footer_type){ - const { projectsId , owner , mergeId } = this.props.match.params; - this.getCommit(owner,projectsId,mergeId); - this.getFile(owner,projectsId,mergeId); - } - this.setState({ - activeKey:footer_type ? "1" : "2", - commitCount:data && data.commits_count, - filesCount:data && data.files_count - }) - } - componentDidUpdate=(prevProps)=>{ - const { comparesData } = this.props; - const { footer_type } = this.props; - if(footer_type){ - const { data } = this.props; - if(data !== prevProps.data){ - this.setState({ - commitCount:data && data.commits_count, - filesCount:data && data.files_count - }) - } - } - if(comparesData !== prevProps.comparesData){ - this.setState({ - activeKey:footer_type ? "1" : "2" - }) - this.changeTab(footer_type ? "1" : "2"); - } + this.state = { + activeKey: '1', + }; } - changeTab=(index)=>{ + changeTab = (index) => { this.setState({ - isSpin:true - }) - this.setState({ - activeKey:index - }) - const { footer_type , comparesData } = this.props; - const { projectsId , owner , mergeId } = this.props.match.params; - - if(footer_type){ - if(index === "2"){ - this.getCommit(owner,projectsId,mergeId); - }else if(index === "3"){ - this.getFile(owner,projectsId,mergeId); - }else{ - this.setState({ - isSpin:false - }) - } - }else{ - this.setState({ - commitsData:comparesData.commits, - filesData:comparesData.diff, - commitCount:comparesData.commits_count, - filesCount:comparesData.diff && comparesData.diff.files_count, - isSpin:false - }) - } - } - - getCommit =(owner,projectsId,mergeId)=>{ - const url = `/${owner}/${projectsId}/pulls/${mergeId}/commits.json`; - axios.get(url).then(result=>{ - if(result){ - this.setState({ - commitsData:result.data.commits, - isSpin:false, - commitCount:result.data.commits_count - }) - } - }).catch(error=>{}) - } - - getFile =(owner,projectsId,mergeId)=>{ - const url = `/${owner}/${projectsId}/pulls/${mergeId}/files.json`; - axios.get(url).then(result=>{ - if(result){ - this.setState({ - filesData:result.data, - isSpin:false, - filesCount:result.data.files_count, - }) - } - }).catch(error=>{}) - } + activeKey: index, + }); + }; render() { - const { projectsId , owner } = this.props.match.params; + const { projectsId, owner } = this.props.match.params; + const { comparesData = {} } = this.props; + const { commits, diff, commits_count } = comparesData; + const { activeKey } = this.state; - const { footer_type, order_id, data , comparesData } = this.props; - let { isSpin , activeKey , filesCount, commitCount , filesData , commitsData } = this.state; - - return ( - !footer_type && !comparesData || (comparesData && ((comparesData.commits && comparesData.commits.length===0)||(comparesData && !comparesData.diff)) )?"": -
        - - - { - footer_type && - 评论 - {data && parseInt(data.comments_count) > 0 && {data.comments_count}} - - } key="1"> - - - } - { - commitsData && commitsData.length > 0 && - 提交 - {commitCount > 0 && {commitCount}} - } key="2"> - - - } - { - filesData && filesData.files && filesData.files.length>0 && - 文件 - {filesCount > 0 && {filesCount}} - - } key="3"> - - - } - - - + return (commits && commits.length === 0) || !diff ? ( + '' + ) : ( +
        + + {commits && commits.length > 0 && ( + + 提交 + {commits_count > 0 && ( + {commits_count} + )} + + } + key="1" + > + + + )} + {diff && diff.files && diff.files.length > 0 && ( + + 文件 + {diff.files_count > 0 && ( + {diff.files_count} + )} + + } + key="3" + > + + + )} +
        ); } diff --git a/src/forge/Merge/merge_form.js b/src/forge/Merge/merge_form.js index 9332d9b85..e63781391 100644 --- a/src/forge/Merge/merge_form.js +++ b/src/forge/Merge/merge_form.js @@ -165,7 +165,8 @@ class MergeForm extends Component { this.setState({ isSpin: false, }); - this.props.history.push(`/${owner}/${projectsId}/pulls`); + const { pull_request_id } = result.data; + this.props.history.push(`/${owner}/${projectsId}/pulls/${pull_request_id}`); const { getDetail } = this.props; getDetail && getDetail(); } else { @@ -195,7 +196,7 @@ class MergeForm extends Component { isSpin: false, }); this.props.history.push( - `/${owner}/${projectsId}/pulls/${mergeId}/Messagecount` + `/${owner}/${projectsId}/pulls/${mergeId}` ); } else { this.setState({ @@ -264,7 +265,7 @@ class MergeForm extends Component { }, ], initialValue: title, - })()} + })()} { - this.props.history.push(merge_type === "new" ? `/${owner}/${projectsId}/pulls` : `/${owner}/${projectsId}/pulls/${mergeId}/detail`) + this.props.history.push(merge_type === "new" ? `/${owner}/${projectsId}/pulls` : `/${owner}/${projectsId}/pulls/${mergeId}`) }} > 取消 @@ -300,7 +301,8 @@ class MergeForm extends Component { {getFieldDecorator("assigned_to_id", { initialValue: assigned_to_id, })( - + {this.renderSelect(members)} )} @@ -311,12 +313,11 @@ class MergeForm extends Component { })( )} @@ -327,12 +328,11 @@ class MergeForm extends Component { })( )} diff --git a/src/forge/Merge/no_data.js b/src/forge/Merge/no_data.js index d5584797f..d5e4ca89b 100644 --- a/src/forge/Merge/no_data.js +++ b/src/forge/Merge/no_data.js @@ -12,7 +12,7 @@ class Nodata extends Component{

        欢迎使用合并请求!

        - 合并请求可以帮助您与他人协作编写代码。在使用之前,请先创建一个 合并请求 + 合并请求可以帮助您与他人协作编写代码。在使用之前,请先创建一个 合并请求
        diff --git a/src/forge/New/Index.js b/src/forge/New/Index.js index fe7748b70..e9aa1f952 100644 --- a/src/forge/New/Index.js +++ b/src/forge/New/Index.js @@ -2,9 +2,10 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; import { Input , Form , Select , Checkbox , Button , Spin , AutoComplete, Modal } from 'antd'; import { Base64 } from 'js-base64'; +import { AlignCenter } from '../Component/layout'; import '../css/index.scss'; -import './new.css' +import './new.scss' import axios from 'axios'; const Option = Select.Option; @@ -44,7 +45,12 @@ class Index extends Component { project_category_name: undefined, license_name: undefined, ignore_name: undefined, - descNum:0 + descNum:0, + + categoreFlag:false, + languageFlag:false, + ignoreFlag:false, + licenseFlag:false, } } componentDidMount = () => { @@ -149,7 +155,7 @@ class Index extends Component { if (mirror_status === 2 && sessionStorage.newProjectValue) { Modal.warning({ title: '警告', - content: '镜像项目创建失败!请按操作规范重新创建项目!', + content: '项目导入失败!请按操作规范重新导入项目!', }); let newProjectValue = JSON.parse(sessionStorage.newProjectValue); if (newProjectValue) { @@ -189,12 +195,16 @@ class Index extends Component { subMitFrom = () => { this.props.form.validateFieldsAndScroll((err, values) => { + console.log(values); if (!err) { this.setState({ isSpin: true }) const { projectsType } = this.props.match.params; - const { project_language_id, project_category_id, license_id, ignore_id , owners_id , owners_name } = this.state; + const { + project_language_id, project_category_id, license_id, ignore_id , owners_id , + ignoreFlag,licenseFlag,categoreFlag,languageFlag + } = this.state; const decoderPass = Base64.encode(values.password); const url = (projectsType && projectsType === "mirror") ? "/projects/migrate.json" : "/projects.json"; // 新建项目的时候,暂存数据,如果失败,返回的时候可以重新赋值 @@ -202,17 +212,14 @@ class Index extends Component { axios.post(url, { ...values, auth_password:decoderPass, - project_language_id, - project_category_id, - license_id, - ignore_id, + project_language_id:languageFlag ? project_language_id : undefined, + project_category_id:categoreFlag ? project_category_id : undefined, + license_id:licenseFlag ? license_id : undefined, + ignore_id:ignoreFlag ? ignore_id : undefined, user_id:owners_id }).then((result) => { if (result && result.data.id) { - this.setState({ - isSpin: false - }) - projectsType && projectsType !== "mirror" && this.props.showNotification(`托管项目创建成功!`); + projectsType && projectsType !== "mirror" && this.props.showNotification(`项目创建成功!`); this.props.history.push(`/${result.data.login}/${result.data.identifier}`); } this.setState({ @@ -278,12 +285,16 @@ class Index extends Component { if(value.indexOf("/") > -1){ let arr = value.split("/"); let first = arr[arr.length-1]; - if(first.indexOf(".git") > -1){ + if(first.indexOf(".") > -1){ let second = first.split('.')[0]; if(!second)return; this.props.form.setFieldsValue({ repository_name:second }) + }else{ + this.props.form.setFieldsValue({ + repository_name:first + }) } } } @@ -316,48 +327,35 @@ class Index extends Component { mirrorCheck, - descNum + descNum, + + ignoreFlag, + licenseFlag, + languageFlag, + categoreFlag } = this.state; return (
        -
        创建{projectsType && projectsType === "mirror" ? "镜像" : "托管"}项目
        +
        {projectsType && projectsType === "mirror" ? "导入" : "新建"}项目
        - - {getFieldDecorator('user_id', { - rules: [{ - required: true, message: '请选择拥有者' - },{ - validator:(rule, value, callback) => this.checkId(rule, value, callback, OwnerList, '拥有者') - }], - })( - this.ChangePlatform(value, e, 'owners', OwnerList)} - className="plateAutoComplete" - onBlur={(value) => this.blurCategory(value, OwnerList, "owners")} - > - {owners_list} - - )} - - { + + { projectsType && projectsType === "mirror" && {getFieldDecorator('clone_addr', { rules: [{ required: true, message: '请填写镜像版本库地址' }], })( - + )}

        示例:https://github.com/facebook/reack.git

        @@ -365,14 +363,16 @@ class Index extends Component { } { projectsType && projectsType === "mirror" && - +

        - 需要授权验证 - 如果源项目为公有仓库,禁止填写用户名密码。如果源项目为私有仓库,则必须填写正确的用户名和密码!

        + 需要授权验证 + 如果导入项目为私有仓库,则必须填写相应平台正确的用户名和密码 +

        { mirrorCheck && -
        +
        用户名 + 密码 {getFieldDecorator('password', { rules: [], })( - + )}
        } - +
        } + + + {getFieldDecorator('user_id', { + rules: [{ + required: true, message: '请选择拥有者' + },{ + validator:(rule, value, callback) => this.checkId(rule, value, callback, OwnerList, '拥有者') + }], + })( + this.ChangePlatform(value, e, 'owners', OwnerList)} + className="plateAutoComplete" + onBlur={(value) => this.blurCategory(value, OwnerList, "owners")} + > + {owners_list} + + )} + + / + + {getFieldDecorator('name', { + rules: [{ + required: true, message: '请填写项目名称' + }], + })( + + )} + + 项目标识 (项目url标识部分)} + colon={false} > - {getFieldDecorator('name', { + {getFieldDecorator('repository_name', { rules: [{ - required: true, message: '请填写项目名称' + required: true, message: '请填写项目标识' }], })( - + )}
        {descNum}/200 {getFieldDecorator('description', { - rules: [{ - required: true, message: '请填写项目简介' - }], + rules: [], })( )}
        - - {getFieldDecorator('repository_name', { - rules: [{ - required: true, message: '请填写仓库名称' - }], - })( - - )} - - - {getFieldDecorator('project_category', { - rules: [{ - required: true, message: '请选择大类别', - }, { - validator: (rule, value, callback) => this.checkId(rule, value, callback, CategoryList, '项目类别') - }], - })( - this.ChangePlatform(value, e, 'project_category', CategoryList)} - className="plateAutoComplete" - onBlur={(value) => this.blurCategory(value, CategoryList, "project_category")} - > - {project_category_list} - - )} - - - {getFieldDecorator('project_language', { - rules: [{ - required: true, message: '请选择项目语言' - }, { - validator: (rule, value, callback) => this.checkId(rule, value, callback, LanguageList, '项目语言') - }], - })( - this.ChangePlatform(value, e, 'project_language', LanguageList)} - className="plateAutoComplete" - onBlur={(value) => this.blurCategory(value, LanguageList, "project_language")} - > - {project_language_list} - - )} - + { (projectsType === "deposit" || !projectsType) && - {getFieldDecorator('ignore', { - rules: [{ - required: true, message: '请选择gitignore' - }, { - validator: (rule, value, callback) => this.checkId(rule, value, callback, GitignoreList, 'gitignore') - }], - })( - this.ChangePlatform(value, e, 'ignore', GitignoreList)} - className="plateAutoComplete" - onBlur={(value) => this.blurCategory(value, GitignoreList, "ignore")} - > - {ignore_list} - + {getFieldDecorator('ignoreFlag')( + this.setState({ignoreFlag:e.target.checked})}>.gitignore )} + { ignoreFlag && + + {getFieldDecorator('ignore', { + rules: [{ + required: ignoreFlag, message: '请选择gitignore' + }, { + validator: (rule, value, callback) => this.checkId(rule, value, callback, GitignoreList, 'gitignore') + }], + })( + this.ChangePlatform(value, e, 'ignore', GitignoreList)} + className="plateAutoComplete" + onBlur={(value) => this.blurCategory(value, GitignoreList, "ignore")} + > + {ignore_list} + + )} + + } - {getFieldDecorator('license', { - rules: [{ - required: true, message: '请选择开源许可证' - }, { - validator: (rule, value, callback) => this.checkId(rule, value, callback, LicensesList, '开源许可证') - }], - })( - this.ChangePlatform(value, e, 'license', LicensesList)} - className="plateAutoComplete" - onBlur={(value) => this.blurCategory(value, LicensesList, "license")} - > - {license_list} - + {getFieldDecorator('licenseFlag')( + this.setState({licenseFlag:e.target.checked})}>开源许可证 )} + { licenseFlag && + + {getFieldDecorator('license', { + rules: [{ + required: licenseFlag, message: '请选择开源许可证' + }, { + validator: (rule, value, callback) => this.checkId(rule, value, callback, LicensesList, '开源许可证') + }], + })( + this.ChangePlatform(value, e, 'license', LicensesList)} + className="plateAutoComplete" + onBlur={(value) => this.blurCategory(value, LicensesList, "license")} + > + {license_list} + + )} + + } } {getFieldDecorator('private')( - 将项目设为私有(只有项目所有人或拥有权限的项目成员才能看到) + 将项目设为私有(只有项目所有人或拥有权限的项目成员才能看到) )} { projectsType && projectsType === "mirror" && {getFieldDecorator('is_mirror')( @@ -540,11 +540,68 @@ class Index extends Component { )} } -
        + + {getFieldDecorator('categoreFlag')( + this.setState({categoreFlag:e.target.checked})}>项目类别 + )} + + {categoreFlag && + + {getFieldDecorator('project_category', { + rules: [{ + required: categoreFlag, message: '请选择项目类别', + }, { + validator: (rule, value, callback) => this.checkId(rule, value, callback, CategoryList, '项目类别') + }], + })( + this.ChangePlatform(value, e, 'project_category', CategoryList)} + className="plateAutoComplete" + onBlur={(value) => this.blurCategory(value, CategoryList, "project_category")} + > + {project_category_list} + + )} + + } + + {getFieldDecorator('languageFlag')( + this.setState({languageFlag:e.target.checked})}>项目语言 + )} + + {languageFlag && + + {getFieldDecorator('project_language', { + rules: [{ + required: languageFlag, message: '请选择项目语言' + }, { + validator: (rule, value, callback) => this.checkId(rule, value, callback, LanguageList, '项目语言') + }], + })( + this.ChangePlatform(value, e, 'project_language', LanguageList)} + className="plateAutoComplete" + onBlur={(value) => this.blurCategory(value, LanguageList, "project_language")} + > + {project_language_list} + + )} + + } +
        注: 为必填项,否则为选填
        - + 取消
        diff --git a/src/forge/New/new.css b/src/forge/New/new.scss similarity index 72% rename from src/forge/New/new.css rename to src/forge/New/new.scss index fcb6fa8cf..15aca70b2 100644 --- a/src/forge/New/new.css +++ b/src/forge/New/new.scss @@ -12,7 +12,13 @@ border-bottom: 1px solid #f0f0f0 } .newPanel_content{ - padding:1rem 2rem; + padding:2rem; +} +.newPanel_content form .ant-row.ant-form-item{ + margin-bottom: 25px; +} +.newPanel_content .ant-form-item-label label{ + font-size: 16px; } .newPanel_content .ant-form-item-control-wrapper{ flex: 1; @@ -25,24 +31,35 @@ height: 35px; line-height: 35px; } - .newContent_inline{ display: flex; flex-wrap: wrap; justify-content: space-between; align-items:flex-end } +.explainPos{ + .ant-form-explain{ + position: absolute; + } +} .newContent_inline > .ant-form-item:nth-child(2){ margin-left: 20px; } -.newPanel_content .privatePart .ant-form-item-label{ - margin-left: 0px; +.privatePart{ + margin-bottom: 0px!important; + .ant-form-item-label{ + margin-left: 0px; + } } .newPanel_content .ant-form-item-label{ line-height: 25px; height: 25px; - margin-left: -0.8rem; +} +.plateAutoComplete{ + .ant-input{ + height: 34px!important; + } } @media screen and (max-width: 750px){ .newPanel_content{ diff --git a/src/forge/Newfile/Index.js b/src/forge/Newfile/Index.js index 9b7769552..953f50720 100644 --- a/src/forge/Newfile/Index.js +++ b/src/forge/Newfile/Index.js @@ -65,16 +65,18 @@ class Index extends Component {
        - +
        + +
        diff --git a/src/forge/Newfile/UserSubmitComponent.js b/src/forge/Newfile/UserSubmitComponent.js index 65fa7ec24..aa4bba6e9 100644 --- a/src/forge/Newfile/UserSubmitComponent.js +++ b/src/forge/Newfile/UserSubmitComponent.js @@ -83,8 +83,8 @@ class UserSubmitComponent extends Component { if (result.data && result.data.name) { this.props.showNotification("文件新建成功!"); if(submitType === "1"){ - const { getTopCount } = this.props; - getTopCount && getTopCount(values.branchname); + const { getDetail } = this.props; + getDetail && getDetail(); } let url = `/${owner}/${projectsId}${values.branchname ? `/tree/${turnbar(values.branchname)}`: (branch ? `/tree/${turnbar(branch)}` : "")}`; this.props.history.push(url); @@ -147,6 +147,7 @@ class UserSubmitComponent extends Component { const { current_user, filepath, projectDetail , currentBranch } = this.props; const { editor_type } = this.props; let b = currentBranch || branch; + return (
        diff --git a/src/forge/Newfile/index.css b/src/forge/Newfile/index.css index 242fd4516..746febe55 100644 --- a/src/forge/Newfile/index.css +++ b/src/forge/Newfile/index.css @@ -8,7 +8,13 @@ border: 1px solid #d9d9d9!important; border-right: none!important; } - +.editorBorder .editorBorderBox{ + border:1px solid #eee; + border-radius: 2px; +} +.editorBorder .editorBorderSubmitBox{ + padding:20px 0px!important; +} .userScrew{ margin:20px 0px; border:1px solid #f4f4f4; @@ -39,7 +45,7 @@ z-index: 1; } .ant-input-group .ant-input:focus{ - border-right: 1px solid #d9d9d9!important; + border-right: 1px solid rgba(70, 106, 255, 1)!important; } .ant-btn-primary.grey{ border:1px solid #BBBBBB; diff --git a/src/forge/Newfile/m_editor.js b/src/forge/Newfile/m_editor.js index 05a7b2ac0..13d2ec466 100644 --- a/src/forge/Newfile/m_editor.js +++ b/src/forge/Newfile/m_editor.js @@ -1,5 +1,7 @@ import React, { Component } from "react"; import Editor from "react-monaco-editor"; +// import {UnControlled as CodeMirror} from 'react-codemirror2' + import UserSubmitComponent from "./UserSubmitComponent"; import "./index.css"; @@ -10,6 +12,7 @@ class m_editor extends Component { super(props); this.state = { editorValue: this.props.content, + prevHeight:0 }; } componentDidUpdate=(prevProps)=>{ @@ -41,42 +44,92 @@ class m_editor extends Component { folding: true, foldingStrategy: "indentation", // 代码可分小段折叠 automaticLayout: true, // 自适应布局 - // overviewRulerBorder: false, // 不要滚动条的边框 - // scrollBeyondLastLine: false, // 取消代码后面一大段空白 + overviewRulerBorder: false, // 不要滚动条的边框 + scrollBeyondLastLine: false, // 取消代码后面一大段空白 minimap: { // 不要小地图 enabled: false, }, }; + + const handleEditorMount = (editor, monaco) => { + editor.onDidChangeModelDecorations(() => { + requestAnimationFrame(updateEditorHeight); // folding + }); + + const updateEditorHeight = () => { + const editorElement = editor.getDomNode(); + + if (!editorElement) { + return; + } + + const padding = 40; + + const lineHeight = editor.getOption( + monaco.editor.EditorOption.lineHeight + ); + + const lineCount = editor.getModel().getLineCount() || 1; + let height = + editor.getTopForLineNumber(lineCount + 1) + + lineHeight + + padding ; + + if(height<400){height = 400;} + + if (this.state.prevHeight !== height) { + this.setState({ + prevHeight:height + }) + // setPrevHeight(height); + editorElement.style.height = `${height}px`; + editor.layout(); + } + }; + + updateEditorHeight(); // typing + }; + + return ( -
        -
        - -
        - {!readOnly && ( -
        - -
        - )} -
        +
        + + {/* */} +
        + {!readOnly && ( +
        + +
        + )}
        ); } diff --git a/src/forge/Nodata.js b/src/forge/Nodata.js index 3147e5601..ccc74ac54 100644 --- a/src/forge/Nodata.js +++ b/src/forge/Nodata.js @@ -1,7 +1,7 @@ import React , { Component } from 'react'; import nodata from './Images/nodata.png'; - +import './css/index.scss'; class Nodata extends Component{ render(){ const { _html , small } = this.props; diff --git a/src/forge/Notice/Index.jsx b/src/forge/Notice/Index.jsx index ae6f8e1f0..8d3c0540e 100644 --- a/src/forge/Notice/Index.jsx +++ b/src/forge/Notice/Index.jsx @@ -10,10 +10,6 @@ const Apply = Loadable({ loader: () => import("./Apply"), loading: Loading, }); -const Notify = Loadable({ - loader: () => import("./Notify"), - loading: Loading, -}); const UndoEvent = Loadable({ loader: () => import("./UndoEvent"), loading: Loading, @@ -24,7 +20,7 @@ function Index(props){ const user = props.user; const undo_messages = props.undo_messages; - const [ menu , setMenu ] = useState("notify"); + const [ menu , setMenu ] = useState("undo"); const [ messagesCount , setMessagesCount ] = useState(0); const [ transferCount , setTransferCount ] = useState(0); const [ applyCount , setApplyCount ] = useState(0); @@ -49,10 +45,6 @@ function Index(props){ useEffect(()=>{ if(pathname && username){ if(pathname === `/${username}/notice`){ - setMenu("notify"); - changeNum(user.undo_messages); - } - if(pathname === `/${username}/notice/undo`){ setMenu("undo"); } if(pathname === `/${username}/notice/apply`){ @@ -84,14 +76,8 @@ function Index(props){ return (
          -
        • - - 通知 - {messagesCount ? {messagesCount}:""} - -
        • - + 接收仓库 {transferCount ? {transferCount}:""} @@ -110,16 +96,10 @@ function Index(props){ return ; }} > - { - return ; - }} - > { - return ; + return ; }} > diff --git a/src/forge/Notice/UndoEvent.jsx b/src/forge/Notice/UndoEvent.jsx index 11cb308aa..93b4eb5ab 100644 --- a/src/forge/Notice/UndoEvent.jsx +++ b/src/forge/Notice/UndoEvent.jsx @@ -62,7 +62,7 @@ function UndoEvent(props){ return(
          -
          +
          { list && list.length > 0 ?
            diff --git a/src/forge/Order/CopyDetail.js b/src/forge/Order/CopyDetail.js index 1f61e7369..906de4be7 100644 --- a/src/forge/Order/CopyDetail.js +++ b/src/forge/Order/CopyDetail.js @@ -1,11 +1,13 @@ -import React, { Component } from "react"; +import React from "react"; import OrderItem from './order_form' -class CopyDetail extends Component { - render() { - return ( - - ) - } + + +function CopyDetail(props){ + const operate = props.match.params.operate; + console.log(operate); + return( + + ) } -export default CopyDetail; +export default CopyDetail; \ No newline at end of file diff --git a/src/forge/Order/Detail.js b/src/forge/Order/Detail.js index 8805004b9..4d1b47cfc 100644 --- a/src/forge/Order/Detail.js +++ b/src/forge/Order/Detail.js @@ -123,22 +123,25 @@ class Detail extends Component { //复制 copydetail = () => { const { projectsId, orderId, owner } = this.props.match.params; - const url = `/${owner}/${projectsId}/issues/${orderId}/copy.json`; - axios - .post(url, { - project_id: projectsId, - id: orderId, - }) - .then((result) => { - if (result) { - this.props.history.push( - `/${owner}/${projectsId}/issues/${result.data.issue_id}/copyetail` - ); - } - }) - .catch((error) => { - console.log(error); - }); + // const url = `/${owner}/${projectsId}/issues/${orderId}/copy.json`; + // axios + // .post(url, { + // project_id: projectsId, + // id: orderId, + // }) + // .then((result) => { + // if (result) { + // this.props.history.push( + // `/${owner}/${projectsId}/issues/${result.data.issue_id}/copyetail` + // ); + // } + // }) + // .catch((error) => { + // console.log(error); + // }); + this.props.history.push( + `/${owner}/${projectsId}/issues/${orderId}/copyetail` + ); }; // 翻页 @@ -230,7 +233,7 @@ class Detail extends Component { : "合并请求"} 】 - {data && data.subject} + {data && data.subject} {data && data.priority && ( @@ -315,7 +318,7 @@ class Detail extends Component {

            - 标签: + 标记: {data && data.issue_tags ? ( diff --git a/src/forge/Order/Milepost.js b/src/forge/Order/Milepost.js index 5f6efeeb9..48fc286ba 100644 --- a/src/forge/Order/Milepost.js +++ b/src/forge/Order/Milepost.js @@ -119,11 +119,12 @@ class Milepost extends Component { } ChangePage = (page) => { + document.body.scrollIntoView(); this.setState({ page }) - - this.getList(page); + const { status } = this.state; + this.getList( page , status ); } // 排序 @@ -260,7 +261,7 @@ class Milepost extends Component { { data && data.versions_count > limit ?

            - +
            : "" }
          diff --git a/src/forge/Order/MilepostDetail.js b/src/forge/Order/MilepostDetail.js index c1c8bdd06..0f5d191e0 100644 --- a/src/forge/Order/MilepostDetail.js +++ b/src/forge/Order/MilepostDetail.js @@ -40,7 +40,7 @@ class MilepostDetail extends Component { issue_type: undefined, status_type: '1', //设置选择高亮 - issue_tag_ids: '标签', + issue_tag_ids: '标记', tracker_ids: '类型', author_ids: '发布人', assigned_to_ids: '负责人', @@ -194,7 +194,7 @@ class MilepostDetail extends Component { done_ratio : undefined, status_id: undefined, assigned_to_id: undefined, - issue_tag_ids: '标签', + issue_tag_ids: '标记', tracker_ids: '类型', author_ids: '发布人', assigned_to_ids: '负责人', @@ -254,7 +254,7 @@ class MilepostDetail extends Component {
        • - + {this.state.issue_tag_ids}
        • diff --git a/src/forge/Order/Nav.js b/src/forge/Order/Nav.js index b5d196d61..3f5a20c59 100644 --- a/src/forge/Order/Nav.js +++ b/src/forge/Order/Nav.js @@ -7,7 +7,7 @@ class Nav extends Component{ const { projectsId , owner } = this.props.match.params; return(

          - 标签 + 标记 里程碑

          ) diff --git a/src/forge/Order/Tags.js b/src/forge/Order/Tags.js index 174891aa6..eeff8db28 100644 --- a/src/forge/Order/Tags.js +++ b/src/forge/Order/Tags.js @@ -324,11 +324,11 @@ class Tags extends Component { data && data.issue_tags && data.issue_tags.length > 0 ?
          - 共{data && data.issue_tags_count}个标签 + 共{data && data.issue_tags_count}个标记
          • - 标签 + 标记
          diff --git a/src/forge/Order/UpdateDetail.js b/src/forge/Order/UpdateDetail.js index 9cbb3cd64..0bf4f0f13 100644 --- a/src/forge/Order/UpdateDetail.js +++ b/src/forge/Order/UpdateDetail.js @@ -1,11 +1,11 @@ -import React, { Component } from "react"; +import React from "react"; import OrderForm from './order_form' -class UpdateDetail extends Component { - render() { - return ( - - ) - } + +function CopyDetail(props){ + const operateName = props.match.params.operateName; + return( + + ) } -export default UpdateDetail; +export default CopyDetail; diff --git a/src/forge/Order/order.js b/src/forge/Order/order.js index 290bb03e3..7275c759a 100644 --- a/src/forge/Order/order.js +++ b/src/forge/Order/order.js @@ -39,7 +39,7 @@ class order extends Component { search_count: undefined, issue_type: undefined, status_type: "1", // 默认显示开启中的 - issue_tag_ids: "标签", + issue_tag_ids: "标记", tracker_ids: "类型", author_ids: "发布人", assigned_to_ids: "负责人", @@ -58,7 +58,7 @@ class order extends Component { select_params: { assigned_to_id: undefined, // 负责人 author_id: undefined, // 发布人 - issue_tag_id: undefined, // 标签 + issue_tag_id: undefined, // 标记 tracker_id: undefined, //类型 done_ratio: undefined, // 完成度 status_id: undefined, // 优先级 @@ -257,7 +257,7 @@ class order extends Component { author_id: undefined, assigned_to_id: undefined, status_type: type, - issue_tag_ids: "标签", + issue_tag_ids: "标记", tracker_ids: "类型", author_ids: "发布人", assigned_to_ids: "负责人", @@ -677,7 +677,7 @@ class order extends Component { className="topWrapperSelect" overlay={this.renderMenu( issue_chosen && issue_chosen.issue_tag, - "标签", + "标记", "issue_tag_id" )} trigger={["click"]} diff --git a/src/forge/Order/order_form.js b/src/forge/Order/order_form.js index 6515dde86..3399a7feb 100644 --- a/src/forge/Order/order_form.js +++ b/src/forge/Order/order_form.js @@ -153,7 +153,7 @@ class order_form extends Component { values.issue_tag_ids = [values.issue_tag_ids]; } const { description, start_date, due_date, issue_type } = this.state; - if (form_type === "new") { + if (form_type !== "edit") { const url = `/${owner}/${projectsId}/issues.json`; axios.post(url, { ...values, @@ -178,7 +178,6 @@ class order_form extends Component { this.setState({ isSpin: false, }); - console.log(error); }); } else { const url = `/${owner}/${projectsId}/issues/${orderId}.json`; @@ -321,7 +320,7 @@ class order_form extends Component { message: "请填写易修标题", }, ] - })()} + })()}
          )} - + {getFieldDecorator("issue_tag_ids", {rules: []})( diff --git a/src/forge/SecuritySetting/Index.jsx b/src/forge/SecuritySetting/Index.jsx index d9d4e49c2..d9fa1bfac 100644 --- a/src/forge/SecuritySetting/Index.jsx +++ b/src/forge/SecuritySetting/Index.jsx @@ -14,6 +14,16 @@ import { Link } from 'react-router-dom'; import './Index.scss'; +const MyNoticeIndex = Loadable({ + loader: () => import("./notice/myNotice/Index"), + loading: Loading, +}); + +const NoticeManager = Loadable({ + loader: () => import("./notice/manager/Index"), + loading: Loading, +}); + const SSHNew = Loadable({ loader: () => import("./sub/New"), loading: Loading, @@ -26,9 +36,16 @@ const SSHIndex = Loadable({ loader: () => import("./sub/SSH"), loading: Loading, }); + +const PrivateLetter = Loadable({ + loader: () => import("./notice/privateLetter/Index"), + loading: Loading, +}); + function Index(props){ - const { current_user } = props; + const { current_user,mygetHelmetapi } = props; const { pathname } = props.location; + const notice_url = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice; return(
          @@ -39,18 +56,41 @@ function Index(props){ {current_user && current_user.username}
          -
            +
            • 个人信息
            • -
            • -1 ?"active":""}>基本资料
            • +
            • -1 ?"active":""}>基本资料
            + {notice_url &&
              +
            • 消息通知
            • +
            • -1 && pathname.indexOf("/settings/notice/config") == -1) || pathname.indexOf("/settings/notice/privateLetter")>-1 ?"active":""}>我的通知
            • +
            • -1 ?"active":""}>通知管理
            • +
            }
            • 安全设置
            • -
            • -1 ?"active":""}>SSH密钥
            • +
            • -1 ?"active":""}>SSH密钥
          + ( + + )} + > + ( + + )} + > + ( + + )} + > ( @@ -74,7 +114,6 @@ function Index(props){
          -
          ) } diff --git a/src/forge/SecuritySetting/Index.scss b/src/forge/SecuritySetting/Index.scss index f533f0530..ede91c86d 100644 --- a/src/forge/SecuritySetting/Index.scss +++ b/src/forge/SecuritySetting/Index.scss @@ -8,42 +8,44 @@ width: 198px; border: 1px solid rgba(153, 153, 153, 0.22); border-radius: 4px; - min-height: 400px; + // min-height: 400px; margin-bottom: 30px; .userDetail{ background: rgba(153, 153, 153, 0.05); border-radius: 4px 4px 0px 0px; padding:20px 25px; - display: flex; - align-items: center; - justify-content: flex-start; + text-align: center; + height: 105px; img{ height: 48px; width: 48px; border-radius: 50%; - margin-right: 12px; } span{ font-size: 16px; color: #333; - max-width: 90px; display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; + font-weight: 500; } } .securityUl{ - padding:20px 16px; + padding-left: 17px; color: #333; margin-bottom: 0px; - padding-bottom: 0px; + padding-bottom: 12px; + padding-top: 5px; li{ + font-size: 14px; + margin-top: 10px; margin-bottom: 10px; height: 27px; line-height: 27px; position: relative; - cursor: pointer; + cursor: default; + a{ color: #666; &:hover{ @@ -52,32 +54,57 @@ } &.active a{ color: #333; - } - &:first-child{ - font-size: 16px; + .text-shodow-bold{ + font-weight: 400; + text-shadow: 0.5px 0 #333; + } } &.active::before{ position: absolute; - left: -16px; - top:0px; - height: 100%; - width: 2px; + left: -18px; + top:6px; + height: 15px; + width: 3px; + border-radius: 2px; content: ""; - background-color: #2A61FF; + background-color: #466AFF; + } + + i{ + font-size: 14px !important; + margin-right: 8px; } } } + .ul-border-buttom{ + border-bottom: 1px solid rgba(153, 153, 153, 0.22); + padding-bottom: 5px; + } } .sshHead{ display: flex; align-items: center; - padding:15px 20px; + padding:6px 0px; + margin-bottom: 15px; justify-content: space-between; border-bottom: 1px solid #EEEEEE; - &>span{ + &>.text-shadow07{ font-size: 18px; + font-weight: 400; + color: #333333; + text-shadow: 0.5px 0 #333; + } + &>.add-SSH-title{ + font-size: 16px; + font-weight: 400; + color: #333333; + text-shadow: 0.5px 0 #333; + } + & .but25{ + padding:0 12px; } } + .ant-list-item{ padding:20px; border-bottom: 1px solid #eee!important; @@ -109,7 +136,7 @@ .questionLink{ padding:15px 20px; a{ - color: #4B7AFF; + color: #466AFF; &:hover{ text-decoration: underline; } @@ -147,4 +174,23 @@ flex:1; } } +} +.but25{ + margin-bottom: 5px; + background-color: #466AFF; + color: #fff; + border-color: #466AFF; + &:hover{ + opacity: 0.8; + background-color: #466AFF; + border-color: #466AFF; + } + &:active{ + opacity: 1; + background-color: #466AFF; + border-color: #466AFF; + } +} +.blue-Purple{ + color: #466AFF !important; } \ No newline at end of file diff --git a/src/forge/SecuritySetting/img/miyao_middle_icon.png b/src/forge/SecuritySetting/img/miyao_middle_icon.png index 03584cfe3..f69cba0e8 100644 Binary files a/src/forge/SecuritySetting/img/miyao_middle_icon.png and b/src/forge/SecuritySetting/img/miyao_middle_icon.png differ diff --git a/src/forge/SecuritySetting/notice/api.js b/src/forge/SecuritySetting/notice/api.js new file mode 100644 index 000000000..07fc1838a --- /dev/null +++ b/src/forge/SecuritySetting/notice/api.js @@ -0,0 +1,46 @@ +import fetch from './fetch'; + +// 获取消息列表 +export function noticePages(params) { + return fetch({ + url: '/api/notice/noticePages', + method: 'get', + params + }); +} + +// 获取单个notice +export function getNotice(params) { + return fetch({ + url: '/api/notice/getNotice', + method: 'get', + params + }); +} + +//新增notice +export function addNotice(data) { + return fetch({ + url: '/api/notice/createNotice', + method: 'post', + data: data + }); +} + +//更新notice +export function updateNotice(data) { + return fetch({ + url: '/api/notice/updateNotice', + method: 'PUT', + data: data + }); +} + +//删除notice +export function deleteNotice(data) { + return fetch({ + url: '/api/notice/deleteNotice', + method: 'DELETE', + data, + }); +} diff --git a/src/forge/SecuritySetting/notice/fetch.js b/src/forge/SecuritySetting/notice/fetch.js new file mode 100644 index 000000000..dba00b140 --- /dev/null +++ b/src/forge/SecuritySetting/notice/fetch.js @@ -0,0 +1,10 @@ + +import javaFetch from '../../javaFetch'; + +const developUrl = "https://test-search.trustie.net"; +const testUrl = "https://test-search.trustie.net"; +const productUrl = "https://wiki-api.trustie.net"; + +const { service, actionUrl } = javaFetch(productUrl, testUrl, developUrl); +export const httpUrl = actionUrl; +export default service; \ No newline at end of file diff --git a/src/forge/SecuritySetting/notice/manager/Index.jsx b/src/forge/SecuritySetting/notice/manager/Index.jsx new file mode 100644 index 000000000..5ba82d60c --- /dev/null +++ b/src/forge/SecuritySetting/notice/manager/Index.jsx @@ -0,0 +1,79 @@ +import { Checkbox } from "antd"; +import React, { useEffect, useState } from "react"; +import axios from 'axios'; +import './Index.scss'; + +function NoticeManager(props){ + const {current_user} = props; + + const [settingTypes, setSettingTypes] = useState(); + const [userNotification, setUserNotification] = useState(); + const [userEmail, setUserEmail] = useState(); + + function onChange(type,e,setting){ + let notification_body = userNotification; + let email_body = userEmail; + if(type){//站内信 + notification_body[setting] = e.target.checked; + }else{//邮件 + email_body[setting] = e.target.checked; + } + axios.post(`/users/${current_user.login}/template_message_settings/update_setting.json`,{ + setting:{ + notification_body:notification_body, + email_body:email_body + } + }).then(response=>{ + if(response && response.status === 0){ + getUserSettings(); + } + }) + } + + function getUserSettings(){ + axios.get(`/users/${current_user.login}/template_message_settings.json`).then((response)=>{ + if(response && response.status === 200 ){ + setUserEmail(response.data.email_body); + setUserNotification(response.data.notification_body); + } + }) + } + + useEffect(()=>{ + axios.get("/template_message_settings.json").then(response => { + if (response && response.status === 200) { + setSettingTypes(response.data.setting_types); + } + }) + getUserSettings(); + },[]) + + return( +
          +
          + 通知管理 +
          +
          + 您可以通过通知管理来选择接受通知的方式 + {settingTypes && userNotification && userEmail && settingTypes.map((item,key)=>{ + return( + item.type_name &&
          +
          {item.type_name}
          + {item.settings.map((i, k) => { + const setting = item.type.substring(item.type.indexOf("::")+2)+"::"+i.key; + return ( +
          +
          {i.name}
          + {onChange(true,e,setting)}}>站内信 + {onChange(false,e,setting)}}>邮件 +
          + ) + })} +
          + ) + })} +
          +
          + ) +} +export default NoticeManager; \ No newline at end of file diff --git a/src/forge/SecuritySetting/notice/manager/Index.scss b/src/forge/SecuritySetting/notice/manager/Index.scss new file mode 100644 index 000000000..87a77a758 --- /dev/null +++ b/src/forge/SecuritySetting/notice/manager/Index.scss @@ -0,0 +1,87 @@ +.notice-manager-tip{ + font-size:16px; + font-weight:400; +} +.manager-cont-top{ + font-size: 14px; + font-weight: 600; + height: 44px; + padding-left: 20px; + background: #FAFCFF; + border: 1px solid #89a4f7; + line-height: 44px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + margin-top: 25px; +} + +.manager-cont{ + padding: 8px 20px 6px; + display: flex; + align-items: center; + font-size: 14px; + + .manager-cont-title{ + width: 320px; + } +} + +.notice01{ + .ant-checkbox-disabled.ant-checkbox-checked .ant-checkbox-inner::after { + border-color: white; + } + .ant-checkbox-disabled .ant-checkbox-inner { + background-color: #999999 !important; + border-color: #999999 !important; + } + + .ant-checkbox-checked .ant-checkbox-inner { + background-color: #466AFF; + border: #466AFF; + } + + .ant-checkbox-checked::after{ + border: 1px solid #466AFF; + } + .ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover .ant-checkbox-inner, .ant-checkbox-input:focus + .ant-checkbox-inner,.ant-radio-checked .ant-radio-inner,.ant-radio-wrapper:hover .ant-radio, .ant-radio:hover .ant-radio-inner, .ant-radio-input:focus + .ant-radio-inner { + border-color: #466AFF; + } + + .ant-checkbox + span,.manager-cont-title { + color: #000000; + } + + .ant-radio-inner::after{ + background-color:#466AFF; + } + + .but25{ + background-color: #466AFF; + color: #fff; + border-color: #466AFF; + &:hover{ + opacity: 0.8; + background-color: #466AFF; + border-color: #466AFF; + } + &:active{ + opacity: 1; + background-color: #466AFF; + border-color: #466AFF; + } + } + + ::-webkit-scrollbar { + width: 5px; /*对垂直流动条有效*/ + } + /*定义滑块颜色、内阴影及圆角*/ + ::-webkit-scrollbar-thumb { + border-radius: 6px; + box-shadow: inset 0 0 6px #FFF; + background-color: #D4D4D4; + } + ::-webkit-scrollbar-track { + box-shadow: inset 0 0 6px #fff; + background-color: #FFF; + } +} \ No newline at end of file diff --git a/src/forge/SecuritySetting/notice/myNotice/Index.jsx b/src/forge/SecuritySetting/notice/myNotice/Index.jsx new file mode 100644 index 000000000..226606919 --- /dev/null +++ b/src/forge/SecuritySetting/notice/myNotice/Index.jsx @@ -0,0 +1,274 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; +import { Badge, Button, Checkbox, Menu, Pagination } from 'antd'; +import DelModal from '../../../Component/ModalFun'; +import NoneData from '../../../Nodata.js'; +import { noticeSourceType } from '../../../common/static'; +import './Index.scss'; +import '../manager/Index.scss' + +function MyNotice(props) { + let { current_user, resetUserInfo, location, mygetHelmetapi, history}= props; + //消息悬停框选择tab + let popover = location && location.query && location.query.noticeType; + let pageSize = 15; + const [noticeType, setNoticeType] = useState(popover==="atme"?"2":"0");//消息类别tab栏选择 + const [selectedNum, setSelectedNum] = useState(0);//@我批量删除选择消息条数 + const [isBatchDelete, setIsBatchDelete] = useState(false);//@我是否批量删除 + const [batchDeleteCheckedAll, setBatchDeleteCheckAll] = useState(false);//@我批量删除--全选 + + const [noticeUnreadCount, setNoticeUnreadCount] = useState();//未读系统通知数量 + // const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量 + const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量 + const [messageList, setMessageList] = useState([]); + const [messTotalCount, setMessTotalCount] = useState();//消息总数 + const [currentPage, setCurrentPage] = useState(1);//当前页数 + const [onlyUnread, setOnlyUnread] = useState(); + + //登录情况下,通过地址访问,直接跳转:/settings/profile + //未登录情况下,通过地址访问,直接跳转:/explore + useEffect(()=>{ + let notice = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice; + let login = current_user && current_user.login; + if(!login){ + history.push(`/explore`); + }else if(!notice){ + history.push(`/settings/profile`); + } + },[mygetHelmetapi]) + + useEffect(()=>{ + popover==="atme" ? setNoticeType("2"):setNoticeType("0"); + },[popover]) + + useEffect(()=>{ + resetUserInfo(); + },[noticeUnreadCount,atUnreadCount]) + + useEffect(() => { + getMessageList(); + }, [noticeType, onlyUnread, currentPage, current_user]) + + function getMessageList() { + const params = { + type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "", + status: onlyUnread ? onlyUnread : "", + limit: pageSize, + page: currentPage, + }; + current_user && axios.get(`/users/${current_user.login}/messages.json`, { + params: params, + }).then((response) => { + if(response && response.data){ + setNoticeUnreadCount(response.data.unread_notification); + setAtUnreadCount(response.data.unread_atme); + setMessageList(response.data.messages); + setMessTotalCount(response.data.total_count); + } + }); + } + + function readNotice(id){ + if(id && current_user){ + const params = { + type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "", + ids:id, + }; + axios.post(`/users/${current_user.login}/messages/read.json`,params).then((response)=>{ + if(response.status === 200){ + getMessageList(); + //已读当前页码最后一条数据时跳转到前一页 + let totlaPage = Math.ceil((messTotalCount-1)/pageSize); + setCurrentPage(currentPage>=totlaPage? totlaPage : currentPage); + } + }); + } + } + + function handleClick(e) { + setNoticeType(e.key); + setCurrentPage(1); + setOnlyUnread(); + if (e.key != "2") { + setIsBatchDelete(false); + } + } + + function onChange(e) { + var checkboxNum = 0; + let messageListNew=messageList.slice(); + messageListNew.map((item)=>{ + if(item.id===e.target.value){ + item.checkedBatch = e.target.checked; + } + item.checkedBatch?checkboxNum++:""; + }); + setMessageList(messageListNew); + setSelectedNum(checkboxNum); + setBatchDeleteCheckAll(checkboxNum === messageList.length); + } + + + function onChangeAll(e) { + setBatchDeleteCheckAll(e.target.checked); + setSelectedNum(e.target.checked?messageList.length:0); + let messageListNew=messageList.slice(); + messageListNew.map((item)=>{ + item.checkedBatch = e.target.checked; + }); + setMessageList(messageListNew); + } + + function deleteNotice(id) { + const ids = []; + if(!id){ + messageList.map(item=>{ + item.checkedBatch && ids.push(item.id); + }); + } + DelModal({ + title: noticeType === "1" ? '删除私信用户' : id ? '删除消息' : '批量删除', + contentTitle: noticeType === "1" ? '您确定要删除与 xxx 的聊天吗?' : id ? '您确定要删除这条@我消息吗?' : '您确定要删除选中的' + selectedNum + '条消息吗?', + content: noticeType === "1" ? '此操作将删除与xxx的聊天框和xxx的所有聊天记录,请进行确认以防数据的丢失' : id ? '此操作将删除这条消息,请进行确认以防数据的丢失' : '此操作将删除选中的' + selectedNum + '条消息,请进行确认以防数据的丢失', + onOk: () => { + const params = { + type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "", + ids:id?id:ids, + }; + axios.delete(`/users/${current_user.login}/messages.json`,{ + data:params, + }).then((response)=>{ + if(response.status === 200){ + getMessageList(); + //删除当前页码最后一条数据时跳转到前一页 + let totlaPage = Math.ceil((messTotalCount-1)/pageSize); + setCurrentPage(currentPage>=totlaPage? totlaPage : currentPage); + setSelectedNum(0); + setBatchDeleteCheckAll(false); + } + }); + } + }); + } + + function cancelBatchDelete(){ + setIsBatchDelete(false); + setSelectedNum(0); + //取消选中效果 + let messageListNew=messageList.slice(); + messageListNew.map((item)=>{ + item.checkedBatch = false; + }); + setMessageList(messageListNew); + setBatchDeleteCheckAll(false); + } + + //跳转到消息详情页面 + function turnToMess(item){ + if(item.notification_url){ + window.open(`${item.notification_url}`); + readNotice([item.id]); + } + } + + return ( +
          +
          + + 系统通知 + {/* 私信 */} + @我 + + {(noticeType==="0" && noticeUnreadCount>0) || (noticeType==="2"&& atUnreadCount>0) ? :""} +
          + +
          +
          + {onlyUnread===1 || messageList && messageList.length>0 ? e.target.checked ? setOnlyUnread(1) : setOnlyUnread()}>仅看未读{noticeType === "1" ? `私信(12)` : noticeType === "0" ? `消息(${noticeUnreadCount})` : `消息(${atUnreadCount})`}:""} +
          + {noticeType === "2" && messageList && messageList.length > 0 ? : ""} +
          + + {messageList && messageList.length===0 ? :""} + + {messageList && messageList.length>0 &&
          +
          + 全选 +    已选择 {selectedNum} 项 +
          +
          +      + +
          +
          } + + {messageList && messageList.map(item => { + // 系统消息 + if (noticeType === "0") { + // 消息类别 + return ( +
          +
          + {item.status === 1 ? : } + + {turnToMess(item)}} dangerouslySetInnerHTML={{__html: item.content}}> +
          +
          + {item.time_ago} + {item.status === 1 && readNotice([item.id])}>标记为已读} +
          +
          + ) + } else if (noticeType === "2") { + //@我 + return ( +
          +
          + + {item.sender && {window.open(`/${item.sender && item.sender.login}`);}}/>} +
          {turnToMess(item)}}> + {item.status === 1 ? : } + {item.sender && " + item.sender.name+ " "+ item.content +" 中@我"}}>} +
          +
          +
          + {item.time_ago} + {!isBatchDelete && item.status === 1 && readNotice([item.id])}>标记为已读}    + {!isBatchDelete && deleteNotice([item.id])}>删除} +
          +
          + ) + } else{ + //私信 + {/*
          + +
          +
          + 蒋宇航 + 4分钟前 + 删除 +
          +
          props.history.push('/settings/notice/privateLetter')}> + 最好的OpenStack控制台,对标OpenStack社区Horizon项目,最好的OpenStack控制台,对标OpenStack社区Horizon项目,在易用性、页面性能等方面进行深度优化,提供简单控制台。 +
          +
          +
          */} + } + })} + + {/* 分页 */} + {!isBatchDelete &&
          + {setCurrentPage(page)}} + total = {messTotalCount} + hideOnSinglePage + > +
          } +
          + ) +} +export default MyNotice; \ No newline at end of file diff --git a/src/forge/SecuritySetting/notice/myNotice/Index.scss b/src/forge/SecuritySetting/notice/myNotice/Index.scss new file mode 100644 index 000000000..8eab84c0c --- /dev/null +++ b/src/forge/SecuritySetting/notice/myNotice/Index.scss @@ -0,0 +1,259 @@ +.whiteBack .boies .sshHead{ + padding:0 10px 0px 0px; +} +.sshHead{ + .ant-badge{ + font-size: 16px; + color: #333333; + } + .ant-menu-item{ + padding:0px; + margin-right:34px!important; + height: 34px; + width: 64px; + text-align: center; + line-height: 0px; + position: relative; + } + + li.ant-menu-item, .ant-menu-horizontal > .ant-menu-item { + border-bottom: 0px; + } + + & .ant-menu-item-selected{ + color: #333333; + font-weight: 400; + text-shadow: 0.5px 0 #333; + border-bottom: 2px solid #2A61FF !important; + } + + .ant-badge-count, .ant-badge-dot, .ant-badge .ant-scroll-number-custom-component { + right: -6px; + -webkit-box-shadow: 0 0 0 0; + box-shadow: 0 0 0 0; + } + .ant-badge-multiple-words { + padding: 0 0px; + } + + .ant-menu-horizontal { + border-bottom: 0px solid #e8e8e8; + } + + button{ + padding:0 5px; + } +} + +button { + color: #333333; + background: #FAFBFC; + border: 1px solid #D0D0D0; + border-radius: 4px; + height: 32px; +} +button:hover { + background: #F3F4F6; +} +button:active { + background: #EBECF0; +} + +.deleteBut{ + color: #DF0002; +} + +.deleteBut:hover{ + background: #DF0002; + border: 1px solid #DF0002; + color:#FFFFFF; +} + +.deleteBut:active{ + background: #CE0002; + border: 1px solid #CE0002; + color:#FFFFFF; +} + +.mynotice-content { + justify-content: space-between; + padding: 15px 0 15px 10px; + border-bottom: 1px solid #EEEEEE; + color: #333333; + &:hover{ + background: #F3F4F6; + } + + & img{ + cursor: pointer; + } + + & b{ + font-weight: 400; + text-shadow: 0.5px 0 #333; + } + + & .invisable-read{ + display: none; + } + + &:hover .invisable-read{ + display: block; + color: #466AFF; + opacity: 0.6; + cursor: pointer; + + &:hover{ + opacity: 1; + } + } + + &:hover .timeSpan{ + display: none; + } + + i{ + font-size: 16px !important; + margin-right: 5px; + } + + .boldSpan{ + font-weight: 400; + text-shadow: 0.5px 0 0 #333; + margin: 0 8px; + } + & .currentImg{ + width: 40px; + height: 40px; + margin-left: 0px; + } + + & .private-letter-img + .ant-badge-count{ + top: 2px; + right: 5px; + height: 18px; + min-width: 18px; + line-height: 18px; + padding: 0 0; + } + + & .highlightSpan:hover{ + color: #466AFF; + cursor: pointer; + } + + .mynotice-cont{ + padding:0; + cursor: default; + & .visible-checkbox{ + margin-right: 10px; + } + & .invisible-checkbox{ + display: none; + } + .atme-notice-text{ + margin-left: 6px; + + & .atme-notice-name{ + margin: 0 0 ; + } + + & .atme-length{ + max-width: 48rem; + word-break: break-all; + } + } + } + + & .ant-badge-count, .ant-badge-dot, .ant-badge .ant-scroll-number-custom-component { + -webkit-box-shadow: 0 0 0 0; + box-shadow: 0 0 0 0; + top: 3px; + right: 4px; + min-width: 8px; + height: 8px; + } + + & .system-notice-blank{ + margin-right: 14px; + } +} + +.batchDel{ + & .currentImg, & .atme-notice-text{ + pointer-events: none; + } +} + +.baselineDiv{ + align-items: baseline; +} + +.invisible { + display: none; +} + +.visible { + display: flex; + justify-content: space-between; + height: 30px; + padding: 0 10px; + color: #333333; + margin-bottom: 5px; + button{ + padding:0px 12px; + } + .batchDeleteBut{ + border:1px solid #466AFF; + color: #466AFF; + } +} + +.private-letter-right { + flex: auto; + margin: 0px 10px 0 16px; + & div{ + display: flex; + justify-content: space-between; + } +} + +.letter-length-limit{ + max-width: 50rem; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.sysNotice-length{ + max-width: 52rem; + word-break: break-all; +} + + +.numberSpan{ + color: #466AFF; +} + +.vertical-center-style{ + display: flex; + align-items: center; +} + +.stretch-style{ + display: flex; + align-items: baseline; +} + +.float-left-little{ + margin-right: 10px; +} + +.float-right-little{ + margin-left: 12px; +} + +.paging{ + text-align: center; + margin: 12px; +} \ No newline at end of file diff --git a/src/forge/SecuritySetting/notice/privateLetter/Index.jsx b/src/forge/SecuritySetting/notice/privateLetter/Index.jsx new file mode 100644 index 000000000..517583770 --- /dev/null +++ b/src/forge/SecuritySetting/notice/privateLetter/Index.jsx @@ -0,0 +1,200 @@ +import React, { useState, useEffect } from 'react'; +import './Index.scss' +import '../manager/Index.scss' +import { Button, Input, Icon, Badge } from 'antd'; +import { Link } from 'react-router-dom'; + +function PrivateLetter(props){ + + const { TextArea,Search } = Input; + + function deleteNotice(){ + alert("删除消息"); + } + + return( +
          +
          +
          + + 蒋宇航 +
          +
          +
          + +
          +
          + 嗨在吗?哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 + 删除 +
          + 2021-08-29 11:59 +
          + +
          + +
          +
          + 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 + 删除 +
          + 2021-08-29 11:59 +
          + +
          +
          +
          + +