diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index 914c0ce7c..1ecdba721 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -206,6 +206,12 @@ module.exports = { { loader: require.resolve("sass-loader"), }, + { + loader: 'sass-resources-loader', + options: { + resources: ['src/global.scss'] + } + } ], }, // "file" loader makes sure those assets get served by WebpackDevServer. diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index 4a9d94652..5e3c1fdcc 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -208,6 +208,12 @@ module.exports = { { loader: require.resolve("sass-loader"), }, + { + loader: 'sass-resources-loader', + options: { + resources: ['src/global.scss'] + } + } ], }, // "file" loader makes sure assets end up in the `build` folder. diff --git a/package-lock.json b/package-lock.json index e7d231ebc..58f8442e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14158,6 +14158,27 @@ "object-assign": "^4.1.1" } }, + "react-copy-to-clipboard": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", + "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "requires": { + "copy-to-clipboard": "^3.3.1", + "prop-types": "^15.8.1" + }, + "dependencies": { + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + } + } + }, "react-countup": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/react-countup/-/react-countup-6.1.0.tgz", @@ -15874,7 +15895,7 @@ }, "sass-loader": { "version": "7.3.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-7.3.1.tgz", "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", "requires": { "clone-deep": "^4.0.1", @@ -15890,9 +15911,85 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "sass-resources-loader": { + "version": "2.2.5", + "resolved": "https://registry.npmmirror.com/sass-resources-loader/-/sass-resources-loader-2.2.5.tgz", + "integrity": "sha512-po8rfETH9cOQACWxubT/1CCu77KjxwRtCDm6QAXZH99aUHBydwSoxdIjC40SGp/dcS/FkSNJl0j1VEojGZqlvQ==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.1.0", + "glob": "^7.1.6", + "loader-utils": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } } } }, diff --git a/package.json b/package.json index 3b421e973..005b45bf2 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "react-color": "^2.18.0", "react-content-loader": "^3.1.1", "react-cookies": "^0.1.1", + "react-copy-to-clipboard": "^5.1.0", "react-countup": "^6.1.0", "react-cropper": "^2.1.8", "react-datepicker": "^2.14.1", @@ -105,7 +106,8 @@ "redux": "^4.0.5", "redux-thunk": "2.3.0", "rsuite": "^4.3.4", - "sass-loader": "7.3.1", + "sass-loader": "^7.3.1", + "sass-resources-loader": "^2.2.5", "save-dev": "0.0.1-security", "scroll-into-view": "^1.14.2", "showdown": "^1.9.1", diff --git a/public/css/edu-purge.css b/public/css/edu-purge.css index 4c467e53f..0f26bfe99 100644 --- a/public/css/edu-purge.css +++ b/public/css/edu-purge.css @@ -2466,7 +2466,7 @@ a.hoverLine:hover{ } a:hover{ - color: #466AFF !important; + color: #466AFF; } .color-grey-98 { diff --git a/public/css/iconfont.css b/public/css/iconfont.css index 39ebe18fe..d63d65407 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=1653550272457') format('woff2'), - url('iconfont.woff?t=1653550272457') format('woff'), - url('iconfont.ttf?t=1653550272457') format('truetype'); + src: url('iconfont.woff2?t=1693564942420') format('woff2'), + url('iconfont.woff?t=1693564942420') format('woff'), + url('iconfont.ttf?t=1693564942420') format('truetype'); } .iconfont { @@ -13,6 +13,230 @@ -moz-osx-font-smoothing: grayscale; } +.icon-liulan1:before { + content: "\e96e"; +} + +.icon-a-zu2044:before { + content: "\e96d"; +} + +.icon-cuo:before { + content: "\e96c"; +} + +.icon-dui:before { + content: "\e96b"; +} + +.icon-zanting:before { + content: "\e96a"; +} + +.icon-cangkuyuyanicon:before { + content: "\e962"; +} + +.icon-dongtaiicon1:before { + content: "\e963"; +} + +.icon-jingxuanxiangmu:before { + content: "\e964"; +} + +.icon-zuzhichengyuan1:before { + content: "\e965"; +} + +.icon-zuzhijieshao:before { + content: "\e966"; +} + +.icon-zuzhituandui:before { + content: "\e967"; +} + +.icon-shezhi6:before { + content: "\e968"; +} + +.icon-zidingyi:before { + content: "\e969"; +} + +.icon-a-zu37:before { + content: "\e961"; +} + +.icon-guanzhuicon:before { + content: "\e95b"; +} + +.icon-yidianzan_icon:before { + content: "\e95c"; +} + +.icon-morenfuke_icon1:before { + content: "\e95d"; +} + +.icon-yiguanzhuicon:before { + content: "\e95e"; +} + +.icon-yifuke_icon:before { + content: "\e95f"; +} + +.icon-xingzhuang:before { + content: "\e960"; +} + +.icon-kaiqi:before { + content: "\e95a"; +} + +.icon-a-31shijian:before { + content: "\e944"; +} + +.icon-chengyuan2:before { + content: "\e945"; +} + +.icon-a-bianji12:before { + content: "\e946"; +} + +.icon-fuzhi-shanchu:before { + content: "\e947"; +} + +.icon-chuangjianqianbao:before { + content: "\e948"; +} + +.icon-biaoji2:before { + content: "\e949"; +} + +.icon-lichengbeiicon1:before { + content: "\e94a"; +} + +.icon-biaoji:before { + content: "\e94b"; +} + +.icon-fenzhi3:before { + content: "\e94c"; +} + +.icon-riqi:before { + content: "\e94d"; +} + +.icon-ruliriqixiao:before { + content: "\e94e"; +} + +.icon-shanchu8:before { + content: "\e94f"; +} + +.icon-sousuo6:before { + content: "\e950"; +} + +.icon-quanxian:before { + content: "\e951"; +} + +.icon-lichengbeiicon2:before { + content: "\e952"; +} + +.icon-xinjian3:before { + content: "\e953"; +} + +.icon-xuanzhong4:before { + content: "\e954"; +} + +.icon-youxian:before { + content: "\e955"; +} + +.icon-a-xiaoxi1:before { + content: "\e956"; +} + +.icon-yixiuicon2:before { + content: "\e957"; +} + +.icon-xiugaibaobiaomoban:before { + content: "\e958"; +} + +.icon-a-zu1614:before { + content: "\e959"; +} + +.icon-BOTpeizhi:before { + content: "\e942"; +} + +.icon-kaifabot:before { + content: "\e943"; +} + +.icon-jiqiren:before { + content: "\e940"; +} + +.icon-a-jiqirenzhineng-02:before { + content: "\e941"; +} + +.icon-yuyue-lishi-shijian:before { + content: "\e93f"; +} + +.icon-a-duobianxing1:before { + content: "\e93c"; +} + +.icon-a-duobianxing2:before { + content: "\e93d"; +} + +.icon-sousuo5:before { + content: "\e93e"; +} + +.icon-baogaoppt:before { + content: "\e93b"; +} + +.icon-gailan2:before { + content: "\e93a"; +} + +.icon-bianji7:before { + content: "\e939"; +} + +.icon-ROBOT:before { + content: "\e937"; +} + +.icon-a-zu1404:before { + content: "\e938"; +} + .icon-tuichuicon:before { content: "\e936"; } diff --git a/public/css/iconfont.js b/public/css/iconfont.js index 0b03f5640..ae98dc07b 100644 --- a/public/css/iconfont.js +++ b/public/css/iconfont.js @@ -1 +1 @@ -!function(a){var l,c,h,i,o,z='',t=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss"),p=function(a,l){l.parentNode.insertBefore(a,l)};if(t&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function v(){o||(o=!0,h())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}v()}l=function(){var a,l=document.createElement("div");l.innerHTML=z,z=null,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?p(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(c=function(){document.removeEventListener("DOMContentLoaded",c,!1),l()},document.addEventListener("DOMContentLoaded",c,!1)):document.attachEvent&&(h=l,i=a.document,o=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,v())})}(window); \ No newline at end of file +window._iconfont_svg_string_2340181='',function(c){var a=(a=document.getElementsByTagName("script"))[a.length-1],l=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var h,i,o,z,t,p=function(a,l){l.parentNode.insertBefore(a,l)};if(l&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,l=document.createElement("div");l.innerHTML=c._iconfont_svg_string_2340181,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?p(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(i=function(){document.removeEventListener("DOMContentLoaded",i,!1),h()},document.addEventListener("DOMContentLoaded",i,!1)):document.attachEvent&&(o=h,z=c.document,t=!1,m(),z.onreadystatechange=function(){"complete"==z.readyState&&(z.onreadystatechange=null,v())})}function v(){t||(t=!0,o())}function m(){try{z.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}v()}}(window); \ No newline at end of file diff --git a/public/css/iconfont.json b/public/css/iconfont.json index 9dc3ea5e5..be2bdae45 100644 --- a/public/css/iconfont.json +++ b/public/css/iconfont.json @@ -5,6 +5,398 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "36898536", + "name": "浏览", + "font_class": "liulan1", + "unicode": "e96e", + "unicode_decimal": 59758 + }, + { + "icon_id": "36020261", + "name": "组 2044", + "font_class": "a-zu2044", + "unicode": "e96d", + "unicode_decimal": 59757 + }, + { + "icon_id": "5287658", + "name": "错", + "font_class": "cuo", + "unicode": "e96c", + "unicode_decimal": 59756 + }, + { + "icon_id": "8802648", + "name": "对", + "font_class": "dui", + "unicode": "e96b", + "unicode_decimal": 59755 + }, + { + "icon_id": "887857", + "name": "暂停", + "font_class": "zanting", + "unicode": "e96a", + "unicode_decimal": 59754 + }, + { + "icon_id": "34965902", + "name": "仓库语言icon", + "font_class": "cangkuyuyanicon", + "unicode": "e962", + "unicode_decimal": 59746 + }, + { + "icon_id": "34965903", + "name": "动态icon", + "font_class": "dongtaiicon1", + "unicode": "e963", + "unicode_decimal": 59747 + }, + { + "icon_id": "34965904", + "name": "精选项目", + "font_class": "jingxuanxiangmu", + "unicode": "e964", + "unicode_decimal": 59748 + }, + { + "icon_id": "34965905", + "name": "组织成员", + "font_class": "zuzhichengyuan1", + "unicode": "e965", + "unicode_decimal": 59749 + }, + { + "icon_id": "34965906", + "name": "组织介绍", + "font_class": "zuzhijieshao", + "unicode": "e966", + "unicode_decimal": 59750 + }, + { + "icon_id": "34965907", + "name": "组织团队", + "font_class": "zuzhituandui", + "unicode": "e967", + "unicode_decimal": 59751 + }, + { + "icon_id": "34965908", + "name": "设置", + "font_class": "shezhi6", + "unicode": "e968", + "unicode_decimal": 59752 + }, + { + "icon_id": "34965909", + "name": "自定义", + "font_class": "zidingyi", + "unicode": "e969", + "unicode_decimal": 59753 + }, + { + "icon_id": "34875122", + "name": "组 37", + "font_class": "a-zu37", + "unicode": "e961", + "unicode_decimal": 59745 + }, + { + "icon_id": "34819652", + "name": "关注icon", + "font_class": "guanzhuicon", + "unicode": "e95b", + "unicode_decimal": 59739 + }, + { + "icon_id": "34819653", + "name": "已点赞_icon", + "font_class": "yidianzan_icon", + "unicode": "e95c", + "unicode_decimal": 59740 + }, + { + "icon_id": "34819654", + "name": "默认复刻_icon", + "font_class": "morenfuke_icon1", + "unicode": "e95d", + "unicode_decimal": 59741 + }, + { + "icon_id": "34819655", + "name": "已关注icon", + "font_class": "yiguanzhuicon", + "unicode": "e95e", + "unicode_decimal": 59742 + }, + { + "icon_id": "34819656", + "name": "已复刻_icon", + "font_class": "yifuke_icon", + "unicode": "e95f", + "unicode_decimal": 59743 + }, + { + "icon_id": "34820092", + "name": "形状", + "font_class": "xingzhuang", + "unicode": "e960", + "unicode_decimal": 59744 + }, + { + "icon_id": "34197190", + "name": "开启", + "font_class": "kaiqi", + "unicode": "e95a", + "unicode_decimal": 59738 + }, + { + "icon_id": "33949760", + "name": "3.1 时间", + "font_class": "a-31shijian", + "unicode": "e944", + "unicode_decimal": 59716 + }, + { + "icon_id": "33949761", + "name": "成员", + "font_class": "chengyuan2", + "unicode": "e945", + "unicode_decimal": 59717 + }, + { + "icon_id": "33949762", + "name": "编辑 (1)", + "font_class": "a-bianji12", + "unicode": "e946", + "unicode_decimal": 59718 + }, + { + "icon_id": "33949763", + "name": "复制-删除", + "font_class": "fuzhi-shanchu", + "unicode": "e947", + "unicode_decimal": 59719 + }, + { + "icon_id": "33949764", + "name": "创建钱包", + "font_class": "chuangjianqianbao", + "unicode": "e948", + "unicode_decimal": 59720 + }, + { + "icon_id": "33949765", + "name": "标记2", + "font_class": "biaoji2", + "unicode": "e949", + "unicode_decimal": 59721 + }, + { + "icon_id": "33949766", + "name": "里程碑icon", + "font_class": "lichengbeiicon1", + "unicode": "e94a", + "unicode_decimal": 59722 + }, + { + "icon_id": "33949767", + "name": "标记", + "font_class": "biaoji", + "unicode": "e94b", + "unicode_decimal": 59723 + }, + { + "icon_id": "33949768", + "name": "分支", + "font_class": "fenzhi3", + "unicode": "e94c", + "unicode_decimal": 59724 + }, + { + "icon_id": "33949769", + "name": "日期", + "font_class": "riqi", + "unicode": "e94d", + "unicode_decimal": 59725 + }, + { + "icon_id": "33949770", + "name": "入离日期小", + "font_class": "ruliriqixiao", + "unicode": "e94e", + "unicode_decimal": 59726 + }, + { + "icon_id": "33949771", + "name": "删除", + "font_class": "shanchu8", + "unicode": "e94f", + "unicode_decimal": 59727 + }, + { + "icon_id": "33949772", + "name": "搜索", + "font_class": "sousuo6", + "unicode": "e950", + "unicode_decimal": 59728 + }, + { + "icon_id": "33949773", + "name": "权限", + "font_class": "quanxian", + "unicode": "e951", + "unicode_decimal": 59729 + }, + { + "icon_id": "33949774", + "name": "里程碑icon2", + "font_class": "lichengbeiicon2", + "unicode": "e952", + "unicode_decimal": 59730 + }, + { + "icon_id": "33949775", + "name": "新建", + "font_class": "xinjian3", + "unicode": "e953", + "unicode_decimal": 59731 + }, + { + "icon_id": "33949776", + "name": "选中", + "font_class": "xuanzhong4", + "unicode": "e954", + "unicode_decimal": 59732 + }, + { + "icon_id": "33949777", + "name": "优先", + "font_class": "youxian", + "unicode": "e955", + "unicode_decimal": 59733 + }, + { + "icon_id": "33949778", + "name": "消息 (1)", + "font_class": "a-xiaoxi1", + "unicode": "e956", + "unicode_decimal": 59734 + }, + { + "icon_id": "33949779", + "name": "易修icon", + "font_class": "yixiuicon2", + "unicode": "e957", + "unicode_decimal": 59735 + }, + { + "icon_id": "33949780", + "name": "修改报表模版", + "font_class": "xiugaibaobiaomoban", + "unicode": "e958", + "unicode_decimal": 59736 + }, + { + "icon_id": "33949781", + "name": "组 1614", + "font_class": "a-zu1614", + "unicode": "e959", + "unicode_decimal": 59737 + }, + { + "icon_id": "33731642", + "name": "BOT配置", + "font_class": "BOTpeizhi", + "unicode": "e942", + "unicode_decimal": 59714 + }, + { + "icon_id": "33731643", + "name": "开发bot", + "font_class": "kaifabot", + "unicode": "e943", + "unicode_decimal": 59715 + }, + { + "icon_id": "19591156", + "name": "机器人", + "font_class": "jiqiren", + "unicode": "e940", + "unicode_decimal": 59712 + }, + { + "icon_id": "25885545", + "name": "机器人、智能-02", + "font_class": "a-jiqirenzhineng-02", + "unicode": "e941", + "unicode_decimal": 59713 + }, + { + "icon_id": "6978097", + "name": "预约-历史-时间", + "font_class": "yuyue-lishi-shijian", + "unicode": "e93f", + "unicode_decimal": 59711 + }, + { + "icon_id": "31659662", + "name": "多边形 1", + "font_class": "a-duobianxing1", + "unicode": "e93c", + "unicode_decimal": 59708 + }, + { + "icon_id": "31659664", + "name": "多边形 2", + "font_class": "a-duobianxing2", + "unicode": "e93d", + "unicode_decimal": 59709 + }, + { + "icon_id": "31659665", + "name": "搜索", + "font_class": "sousuo5", + "unicode": "e93e", + "unicode_decimal": 59710 + }, + { + "icon_id": "31424249", + "name": "报告ppt", + "font_class": "baogaoppt", + "unicode": "e93b", + "unicode_decimal": 59707 + }, + { + "icon_id": "31318120", + "name": "概览", + "font_class": "gailan2", + "unicode": "e93a", + "unicode_decimal": 59706 + }, + { + "icon_id": "31275425", + "name": "编辑", + "font_class": "bianji7", + "unicode": "e939", + "unicode_decimal": 59705 + }, + { + "icon_id": "30382389", + "name": "ROBOT", + "font_class": "ROBOT", + "unicode": "e937", + "unicode_decimal": 59703 + }, + { + "icon_id": "30382697", + "name": "组 1404", + "font_class": "a-zu1404", + "unicode": "e938", + "unicode_decimal": 59704 + }, { "icon_id": "29934961", "name": "退出icon", diff --git a/public/css/iconfont.ttf b/public/css/iconfont.ttf index 5ea5e9206..b94d6dcee 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 6b8a4f2ee..cbcd0bb08 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 ce5758a2e..3b6d1ad48 100644 Binary files a/public/css/iconfont.woff2 and b/public/css/iconfont.woff2 differ diff --git a/src/AppConfig.js b/src/AppConfig.js index 1a241cead..190f9c411 100644 --- a/src/AppConfig.js +++ b/src/AppConfig.js @@ -32,7 +32,7 @@ export function initAxiosInterceptors(props) { // 判断网络是否连接 initOnlineOfflineListener(); - var proxy = "https://testforgeplus.trustie.net"; + var proxy = "https://testforgeplus.trustie.net/"; //响应前的设置 axios.interceptors.request.use( config => { diff --git a/src/Loading.js b/src/Loading.js index 8175b7df8..fa9b2d155 100644 --- a/src/Loading.js +++ b/src/Loading.js @@ -2,14 +2,9 @@ import React, { Component } from 'react'; import { Spin } from 'antd'; class Loading extends Component { - componentDidUpdate(prevProps, prevState) { - if (!prevProps.error && this.props.error) { - console.log(this.props.error) - } - } render() { - return "" + return
} } diff --git a/src/common/UrlTool.js b/src/common/UrlTool.js index ee104fc81..0e092b7ad 100644 --- a/src/common/UrlTool.js +++ b/src/common/UrlTool.js @@ -10,14 +10,14 @@ export const TEST_HOST = window.location.hostname ==="testforgeplus.educoder.net export function getImageUrl(path) { const local = 'https://testforgeplus.educoder.net'; - const normal = TEST_HOST ? "https://new-testali-cdn.educoder.net/images" : 'https://ali-cdn.educoder.net/images'; - const normalend = TEST_HOST ? 'https://new-testali-cdn.educoder.net' :'https://ali-cdn.educoder.net'; + // const normal = TEST_HOST ? "https://new-testali-cdn.educoder.net/images" : 'https://ali-cdn.educoder.net/images'; + const normalend = TEST_HOST ? 'https://testforgeplus.educoder.net/' :'https://ali-cdn.educoder.net'; if (isDev) { return `${local}/${path}`; } if(path.substr(0, 7) !== "/system"){ if(path.substr(0, 7) !== "/images"){ - return `${normal}${path}`; + return `/${path}`; }else{ return `${normalend}${path}`; } diff --git a/src/forge/Component/AddMember.jsx b/src/forge/Component/AddMember.jsx index 426088edc..097dc2080 100644 --- a/src/forge/Component/AddMember.jsx +++ b/src/forge/Component/AddMember.jsx @@ -140,7 +140,7 @@ import debounce from 'lodash/debounce'; import { Select, Spin , Button , Icon } from 'antd'; import axios from 'axios'; import { getImageUrl } from 'educoder'; -function DebounceSelect({ fetchOptions, debounceTimeout = 600, ...props }) { +function DebounceSelect({ fetchOptions, debounceTimeout = 600,login, ...props }) { const [fetching, setFetching] = useState(false); const [options, setOptions] = useState([]); const fetchRef = useRef(0); @@ -182,7 +182,7 @@ function DebounceSelect({ fetchOptions, debounceTimeout = 600, ...props }) { options && options.length >0 && options.map((i)=>{ return( - + {/* { { diff --git a/src/forge/Component/ProfileModal/Profile.jsx b/src/forge/Component/ProfileModal/Profile.jsx index 2ae769ebe..5df2b39de 100644 --- a/src/forge/Component/ProfileModal/Profile.jsx +++ b/src/forge/Component/ProfileModal/Profile.jsx @@ -14,7 +14,7 @@ function Profile({ function checkProfile() { if(checkIfLogin()){ - if(!current_user.has_gitea_user || (current_user.has_gitea_user && !current_user.is_sync_pwd)){ + if(!current_user.has_gitea_user){ setGiteaVisible(true); }else{ if(!completeProfile){ diff --git a/src/forge/Head/Header.js b/src/forge/Head/Header.js index d09cad7d4..814c7b4b3 100644 --- a/src/forge/Head/Header.js +++ b/src/forge/Head/Header.js @@ -63,7 +63,7 @@ class NewHeader extends Component { } getEducoderSetting=()=>{ - const url = `https://data.educoder.net/api/setting.json`; + const url = `/setting.json`; axios.get(url).then(result=>{ if(result && result.data){ this.setState({ diff --git a/src/forge/Issues/Component/addTagsBox.jsx b/src/forge/Issues/Component/addTagsBox.jsx new file mode 100644 index 000000000..6b0c72108 --- /dev/null +++ b/src/forge/Issues/Component/addTagsBox.jsx @@ -0,0 +1,65 @@ +import React , { useState , useEffect } from 'react'; +import { Form , Input, Button } from 'antd'; +import ColorCard from './colorCard'; +import axios from 'axios'; + +function AddTagsBox({onSuccess , form ,owner , projectsId,onCancel}){ + const { getFieldDecorator , getFieldsValue , validateFields } = form; + const [ colors , setColors ] = useState(undefined); + const [ defaultColor , setDefaultColor ]= useState(undefined); + + useEffect(()=>{ + const c = Math.random().toString(16).substr(-6); + c && setDefaultColor(`#${c}`); + },[]) + + function saveFunc(){ + validateFields((error,values)=>{ + if(!error){ + const { desc , name } = getFieldsValue(); + const url = `/v1/${owner}/${projectsId}/issue_tags`; + axios.post(url,{ + name,description:desc,color:colors || defaultColor + }).then(result=>{ + result && onSuccess(); + }).catch(error=>{}) + } + }) + } + + function getColor(colors){ + setColors(colors); + } + + return( +
+

创建标记

+
+ + {getFieldDecorator('name',{ + rules:[ + { + required:true, + message:"请输入标记名称" + } + ], + validateTrigger:"onInput", + })()} + + + + +
+ + {getFieldDecorator('desc',{rules:[], + validateTrigger:"onInput", + })()} + +
+ + +
+ + ) +} +export default Form.create({ name: 'Bind' })(AddTagsBox); \ No newline at end of file diff --git a/src/forge/Issues/Component/allMenus.jsx b/src/forge/Issues/Component/allMenus.jsx new file mode 100644 index 000000000..3a80aebb6 --- /dev/null +++ b/src/forge/Issues/Component/allMenus.jsx @@ -0,0 +1,255 @@ +import React,{ useState , useEffect , forwardRef ,useImperativeHandle , useRef } from 'react'; +import Menus from './menus'; +import axios from 'axios'; + +const array =[ + {id:1,name:"最新创建"}, + {id:2,name:"最早创建"}, + {id:3,name:"最新更新"}, + {id:4,name:"最早更新"}, + {id:5,name:"高优先级"}, + {id:6,name:"低优先级"}, + {id:7,name:"高悬赏金额"}, + {id:8,name:"低悬赏金额"} +] +const array1 =[ + {id:1,name:"最新创建"}, + {id:2,name:"最早创建"}, + {id:3,name:"最新更新"}, + {id:4,name:"最早更新"}, + {id:5,name:"高优先级"}, + {id:6,name:"低优先级"}, +] + +function AllMenus({owner,projectsId,chooseFunc,update,defaultNames,defaultIds,open_blockchain},ref){ + // 列表右侧所有筛选项的值 + const [ authorList , setAuthorList ] = useState(undefined); + const [ author , setAuthor ] = useState(undefined); + const [ tagList , setTagList ] = useState(undefined); + const [ tag , setTag ] = useState(undefined); + const [ cases , setCases ] = useState(undefined); + const [ millstoneList , setMillstoneList ] = useState(undefined); + const [ millstone , setMillstone ] = useState(undefined); + const [ chargeList , setChargeList ] = useState(undefined); + const [ caseList , setCaseList ] = useState(undefined); + const [ charge , setCharge ] = useState(undefined); + const [ statusList , setStatusList ] = useState(undefined); + const [ prioritiesList , setPrioritiesList ] = useState(undefined); + const [ ids , setIds ] = useState({author_id:undefined,issue_priorities_id:undefined,issue_tag_ids:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined,test_case:undefined}); + const [ names , setNames ] = useState({author_name:undefined,issue_priorities_name:undefined,issue_tag_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined,test_case:undefined}); + + useEffect(()=>{ + if(defaultNames){ + setNames(defaultNames); + } + },[defaultNames]) + + useEffect(()=>{ + if(defaultIds){ + setIds(defaultIds); + } + },[defaultIds]) + + useImperativeHandle(ref, () => ({ + clearChoose: () => { + // 父组件按钮:清除筛选条件,将Ids\names里的字段全部改为undefined + setIds({author_id:undefined,issue_tag_ids:undefined,issue_priorities_id:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined}); + setNames({author_name:undefined,issue_tag_name:undefined,issue_priorities_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined}); + } + })) + // 获取发布人列表 + useEffect(()=>{ + getSendPerson(); + },[author]) + + function getSendPerson(){ + const url = `/v1/${owner}/${projectsId}/issue_authors`; + axios.get(url,{params:{keyword:author,only_name:true}}).then(result=>{ + if(result && result.data){ + setAuthorList(result.data.authors); + } + }) + } + // 获取标记列表 + useEffect(()=>{ + getSign(); + },[tag]) + function getSign(){ + const url = `/v1/${owner}/${projectsId}/issue_tags`; + axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{ + if(result && result.data){ + setTagList(result.data.issue_tags); + } + }) + } + // 获取测试用例 + useEffect(()=>{ + getCase(); + },[cases]) + + function getCase(){ + const url = `/v1/${owner}/${projectsId}/issues/test_cases`; + axios.get(url,{ + params:{ + keyword:cases + } + }).then(result=>{ + if(result && result.data){ + let menu = result.data.data; + if(menu && menu.length>0){ + const l = menu.map(i=>{ + return {name:i.test_case,id:i.test_case} + }) + setCaseList(l); + }else{ + setCaseList([]); + } + } + }) + } + // 获取里程碑列表 + useEffect(()=>{ + getMillstone(); + },[millstone]) + + function getMillstone(){ + const url = `/v1/${owner}/${projectsId}/milestones`; + axios.get(url,{params:{keyword:millstone,only_name:true}}).then(result=>{ + if(result && result.data){ + setMillstoneList(result.data.milestones); + } + }) + } + + // 获取负责人列表 + useEffect(()=>{ + getCharge(); + },[charge,update]) + + function getCharge(){ + if(update){ + const url = `/v1/${owner}/${projectsId}/collaborators`; + axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{ + if(result && result.data){ + setChargeList(result.data.collaborators); + } + }) + }else{ + const url = `/v1/${owner}/${projectsId}/issue_assigners`; + axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{ + if(result && result.data){ + setChargeList(result.data.assigners); + } + }) + } + } + // 获取优先级列表 + useEffect(()=>{ + getPriorities(); + },[]) + + function getPriorities(){ + const url = `/v1/${owner}/${projectsId}/issue_priorities`; + axios.get(url).then(result=>{ + if(result && result.data){ + setPrioritiesList(result.data.priorities); + } + }) + } + // 获取状态列表 + useEffect(()=>{ + getStatus(); + },[]) + + function getStatus(){ + const url = `/v1/${owner}/${projectsId}/issue_statues`; + axios.get(url).then(result=>{ + if(result && result.data){ + setStatusList(result.data.statues); + } + }) + } + + function choose(id,name){ + let copy = {...ids,author_id:id && id.length>0 ? id.join(","):undefined}; + let copyname = { ...names,author_name:name}; + setIds(copy);setNames(copyname); + chooseFunc(copy,copyname); + } + + return( +
    + { !update && setAuthor(value)} + chooseFunc={(id,name)=>choose(id,name,'author_name')} + /> + } + {update && {let copy = {...ids,issue_priorities_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,issue_priorities_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}} + /> + } + setTag(value)} + chooseFunc={(id,name)=>{let copy = {...ids,issue_tag_ids:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,issue_tag_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}} + /> + setMillstone(value)} + chooseFunc={(id,name)=>{let copy = {...ids,milestone_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,milestone_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}} + /> + setCases(value)} + double={false} + chooseFunc={(id,name)=>{let copy = {...ids,test_case:name};let copyname = { ...names,test_case:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}} + /> + setCharge(value)} + double={update ? true : false } + chooseFunc={(id,name)=>{let copy = {...ids,assigner_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,assigner_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}} + /> + {let copy = {...ids,status_id:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,status_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}} + /> + { !update && {let copy = {...ids,sort_by:id && id.length>0 ? id.join(","):undefined};let copyname = { ...names,sortby_name:name};setNames(copyname);setIds(copy);chooseFunc(copy,copyname)}} + /> + } +
+ ) +} +export default forwardRef(AllMenus); \ No newline at end of file diff --git a/src/forge/Issues/Component/caseDrop.jsx b/src/forge/Issues/Component/caseDrop.jsx new file mode 100644 index 000000000..c88175d57 --- /dev/null +++ b/src/forge/Issues/Component/caseDrop.jsx @@ -0,0 +1,65 @@ +import React , { useEffect , useState } from 'react'; +import { AutoComplete } from 'antd'; +import axios from 'axios'; +import '../index.scss'; + +const { Option } = AutoComplete; + +function CaseDrop({ + name , owner , projectsId , changeValue , value , onBlur +}){ + const [ filterUnitList , setFilterUnitList ] = useState([]); + const [ v , setV ] = useState(value); + + useEffect(()=>{ + getCaseList(value); + },[]) + + useEffect(()=>{ + setV(value); + },[value]) + + useEffect(()=>{ + changeValue(v); + getCaseList(v); + },[v]) + + async function getCaseList(value){ + setV(value); + const url = `/v1/${owner}/${projectsId}/issues/test_cases`; + await axios.get(url,{ + params:{ + keyword:value + } + }).then(response=>{ + if(response && response.data){ + setFilterUnitList(response.data.data); + } + }).catch(error=>{}) + } + + return( +
  • + {name} + {if(onBlur){onBlur(v)}}} + > + { + filterUnitList && filterUnitList.length > 0 && + filterUnitList.map(i=>{ + return( + + ) + }) + } + +
  • + ) +} +export default CaseDrop; \ No newline at end of file diff --git a/src/forge/Issues/Component/chooseMenu.jsx b/src/forge/Issues/Component/chooseMenu.jsx new file mode 100644 index 000000000..4da6c5e4d --- /dev/null +++ b/src/forge/Issues/Component/chooseMenu.jsx @@ -0,0 +1,305 @@ +import React ,{ useState , useEffect , useRef , forwardRef } from 'react'; +import { findDOMNode } from 'react-dom'; +import { Dropdown , Menu , Input , Button, Form , Tooltip } from 'antd'; +import { getImageUrl } from 'educoder'; +import AddTagsBox from './addTagsBox'; + +const { Search } = Input; +/** + * + * @param {placeholder} placeholder:默认显示内容(为空时) + * @param {editFlag} editFlag:是否有编辑权限 + * @param {searchFlag} searchFlag:是否显示搜索框 + * @param {selectValueList} selectValueList:将选中的list保存 + * @param {searchFunc} searchFunc:搜索方法 + * @param {onAdd} onAdd:标记管理 + * @param {headImg} headImg:是否显示头像 + * @param {menus} menus:下拉列表 + * @param {chooseFunc} chooseFunc:选择下拉列表 + * @param {double} double:undefined为单选,有值就是最多选的数量 + * @param {colorFlag} colorFlag:选中值需根据颜色显示背景 + * @param {mustFlag} mustFlag:必须选择一个 + * @param {removeFlag} removeFlag:移除按钮 + * @returns + */ +function ChooseMenu({ + placeholder ="未设置", + editFlag, + searchFlag, + selectValueList, + searchFunc, + onAdd, + headImg, + menus, + auto, + chooseFunc,double,colorFlag,mustFlag,removeFlag,owner,projectsId +}){ + const [ visible , setVisible ] = useState(false); + const [ searchValue , setSearchValue ]= useState(undefined); + const [ valuesId , setValuesId ] = useState([]); + const [ count , setCount ] = useState(double); + const [ content , setContent ] = useState(false); + + const [ saveList , setSaveList ] = useState(undefined); + + const refFa = useRef(null); + const refBox = useRef(null); + + useEffect(() => { + document.addEventListener('mousedown', clickMe , false); + return () => { + window.removeEventListener("mousedown", 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); + const arr = ["removeicon","iconfont icon-guanbi font-12 color-white","iconfont icon-guanbi font-12 color-blue","icon-a-bianji12","color-blue font-15 tagManage","iconfont icon-xiangzuojiantou font-15 color-grey mr5 cursor","cover","ant-btn cancelTags"]; + const pointer = arr.includes(target.className); + if(!isChild && !isBox && !pointer){ + setVisible(false); + setContent(false); + } + } + } + + // 根据选中的列表循环出id数组 + useEffect(()=>{ + if(selectValueList && selectValueList.length > 0 && visible ){ + renderSelectList(selectValueList); + setSaveList(selectValueList); + setCount(selectValueList.length); + }else{ + setValuesId([]); + setCount(0); + setSaveList(undefined); + } + if(!visible){ + setSearchValue(undefined); + searchFunc(undefined); + } + },[selectValueList,visible]) + + function renderSelectList(list){ + let a = list && list.length > 0 && list.map((i,k)=>{ + return i.id ? i.id.toString() : i.name + }) + setValuesId(a); + } + + // 搜索 + function changeSearchvalue(e){ + setSearchValue(e.target.value); + searchFunc(e.target.value); + } + function renderNames(nameArrs){ + return + { + nameArrs && nameArrs.length>0? + nameArrs.map((i,k)=>{ + return( +

    + {i.image_url && } + { + colorFlag ? + ( + colorFlag === "2" ? + + {i.name} + + : + {i.name} + ) + : + {i.name} + } + { !colorFlag && removeFlag && removeValueFunc(i.id)}>} + { colorFlag && removeFlag && removeValueFunc(i.id)} style={{display:'block',right:"19px",position:"absolute"}}>} +

    + ) + }) + :{placeholder} + } +
    + } + + // 选择项 + function chooseMenu(i){ + let list = saveList && saveList.length > 0 ? [...saveList]:[] ; + let relist = []; + relist = list && list.length > 0 ? list:[]; + let filter = []; + if(i.id){ + filter = list.filter(j=>j.id === i.id); + }else{ + filter = list.filter(j=>j.name === i.name); + } + if(filter && filter.length > 0){ + if(i.id){ + relist = list.filter(j=>j.id !== i.id); + }else{ + relist = list.filter(j=>j.name !== i.name); + } + }else{ + if(double && (saveList && saveList.length >= double)){ + setCount(-1); + return; + } + if(double){ + relist.push(i); + }else{ + relist = [i]; + } + } + renderSelectList(relist); + setSaveList(relist); + setCount(relist ? relist.length :0); + if(!double){ + setVisible(false); + setContent(false); + chooseFunc(relist); + } + } + + // 确认按钮 + function onSureFunc(){ + setVisible(false); + setContent(false); + chooseFunc(saveList); + } + // 移除选择的项 + function removeSaveFunc(id){ + let list = [...saveList]; + let filter = list.filter(j=>j.id !== id); + setSaveList(filter); + renderSelectList(filter); + setCount(filter ? filter.length :0); + } + + // 移除后保存 + function removeValueFunc(id){ + let list = [...selectValueList]; + let filter = list.filter(j=>j.id !== id); + setSaveList(filter); + renderSelectList(filter); + setCount(filter ? filter.length :0); + chooseFunc(filter); + } + + function onSuccess(){ + setContent(false); + onAdd(); + } + return( +
  • + + {placeholder} + { + !editFlag && + + { + !content ? +
    + { + double && saveList && saveList.length>0? +
      + { + saveList.map((i,k)=>{ + return( +
    • + {i.name} + removeSaveFunc(i.id)}> +
    • + ) + }) + } +
    + :"" + } + { searchFunc && +
    + +
    + } + { + menus && menus.length >0? + + { + menus.map((i,k)=>{ + return( + chooseMenu(i)}> + { + auto ? {i.name} + : + {i.name} + } + + ) + }) + } + + : +
    +

    {searchValue ? 暂无{placeholder}“{searchValue}”: `暂无${placeholder}`}

    +
    + } +
    + + { + double && (count<0 ? +

    最多添加{double}个{placeholder}!

    + : +

    还可添加{double - count}个{placeholder}!

    + ) + } +
    + { + double && +
    + + +
    + } +
    + : + setContent(false)} + onSuccess={onSuccess} /> + } + + }> + setVisible(visible ? false : true)}> + + +
    + } +
    +
    0 ? "operatevalue color-grey-3":"operatevalue"} style={{display:colorFlag?"flex":"block"}}>{renderNames(selectValueList)}
    +
  • + ) +} +export default Form.create()(forwardRef(ChooseMenu)); \ No newline at end of file diff --git a/src/forge/Issues/Component/colorCard.jsx b/src/forge/Issues/Component/colorCard.jsx new file mode 100644 index 000000000..d63260035 --- /dev/null +++ b/src/forge/Issues/Component/colorCard.jsx @@ -0,0 +1,47 @@ +import React,{ useState , useEffect } from 'react'; +import { SketchPicker } from 'react-color'; + +function ColorCard({getColor,defaultColor}){ + const [ displayColorPicker , setDisplayColorPicker ] = useState(false); + const [ textcolor , setTextColor ] = useState("#F17013"); + + + useEffect(()=>{ + if(defaultColor){ + setTextColor(defaultColor); + } + },[defaultColor]) + + // 切换色卡 + function handleChange(color){ + setTextColor(color.hex); + getColor(color ? color.hex : defaultColor); + } + + // 隐藏色卡 + function handleClick(){ + setDisplayColorPicker(!displayColorPicker); + // reduction(); + } + + // 还原色卡 + function reduction(){ + setTextColor("#F17013"); + // setColor({r: '241',g: '112',b: '19',a: '1',}); + } + return( +
    +
    +
    +

    {textcolor}

    +
    + {displayColorPicker ? ( +
    +
    {setDisplayColorPicker(false)}} /> + +
    + ) : null} +
    + ) +} +export default ColorCard; \ No newline at end of file diff --git a/src/forge/Issues/Component/comments/editComment.jsx b/src/forge/Issues/Component/comments/editComment.jsx new file mode 100644 index 000000000..a0083d2fb --- /dev/null +++ b/src/forge/Issues/Component/comments/editComment.jsx @@ -0,0 +1,165 @@ +import React from 'react'; +import { Button, message } from 'antd'; +import { getImageUrl } from 'educoder'; +import MDEditor from "../../../../modules/tpm/challengesnew/tpm-md-editor"; +import Upload from "../../../../forge/Upload/Index"; +import Attachments from "../../../../forge/Upload/attachment"; +import UploadImg from "../../../../forge/Images/upload.png"; +import './list.scss'; +import '../../../../forge/Order/order.scss'; +import { useState } from 'react'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; + +function EditComment(props){ + const {owner, projectsId, index} = props.match.params; + const {current_user, current_user:{login}, showLoginDialog, showNotification, reloadComment, cancelMd, parentId, replyId, updateId, showUserImg=true} = props; + const [content, setContent] = useState(props.content); + const [quillFlag, setQuillFlag] = useState(false); + // 确认评论按钮loading效果 + const [journalSpin, setJournalSpin] = useState(false); + const [atWhoLoginList, setAtWhoLoginList] = useState(undefined); + const [fileList, setFileList] = useState(undefined); + const [attachmentClean, setAttachmentClean] = useState(true); + const [ save , setSave ] = useState(undefined); + + useState(()=>{ + setSave(props.defaultFileList); + },[props.defaultFileList]) + + // 评论点击函数 + function addJournals(){ + if(!content){ + setQuillFlag(true); + return + } + setJournalSpin(true); + if(updateId){ + // 修改评论 + axios.patch(`/v1/${owner}/${projectsId}/issues/${index}/journals/${updateId}`,{ + notes: content, + attachment_ids: fileList, + receivers_login:atWhoLoginList + }).then(res=>{ + if(res && res.data){ + // 刷新评论列表 + reloadComment(); + message.success('评论成功'); + cancelMd(); + setContent(undefined); + } + setJournalSpin(false); + }) + }else{ + // 新建评论 + const params = { + parent_id: parentId, + reply_id: replyId, + notes: content, + attachment_ids: fileList, + receivers_login:atWhoLoginList + } + axios.post(`/v1/${owner}/${projectsId}/issues/${index}/journals`, params).then(res=>{ + if(res && res.data){ + // 刷新评论列表 + reloadComment(); + message.success('评论成功'); + cancelMd(); + setContent(undefined); + } + setJournalSpin(false); + }) + } + }; + function deleteLoad(id){ + let arr = []; + let list = save; + if(list && list.length>0 ){ + arr = list.filter(i=>i.id !== id); + } + setFileList(arr.map(i=>{return i.id})); + setSave(arr); + } + function UploadFunc(f){ + let list = []; + let arr = []; + if(save && save.length>0 ){ + arr = save.map(i=>{return i.id}); + } + list = arr && arr.length>0 ? f.concat(arr) : f ; + setFileList(list); + }; + + return( +
    + + + +
    + {setQuillFlag(false);setContent(value);}} + isCanAtme = {true} + isQuoteIssue={true} + changeAtWhoLoginList = {(loginList)=>{setAtWhoLoginList(loginList); setAttachmentClean(true)}} + owner = {owner} + projectsId = {projectsId} + > +

    + {quillFlag && 请输入评论内容} +

    + + } + size={100} + showNotification={showNotification} + // defaultFileList={props.defaultFileList} + /> + {props.defaultFileList && props.defaultFileList.length > 0 && + + } +

    + + +

    +
    +
    + ) +} +export default EditComment; \ No newline at end of file diff --git a/src/forge/Issues/Component/comments/list.jsx b/src/forge/Issues/Component/comments/list.jsx new file mode 100644 index 000000000..afabf21a2 --- /dev/null +++ b/src/forge/Issues/Component/comments/list.jsx @@ -0,0 +1,242 @@ +import React, { useEffect, useState } from 'react'; +import { Button, Popconfirm, Radio, Input, message, Tooltip, Spin, Pagination } from 'antd'; +import axios from 'axios'; +import { getImageUrl, timeAgo } from 'educoder'; +import RenderHtml from '../../../../components/render-html'; +import Attachment from '../../../Upload/attachment'; +import EditComment from './editComment'; +import Nodata from '../../../Nodata'; +import CheckProfile from '../../../Component/ProfileModal/Profile'; +import './list.scss'; +import { Link } from 'react-router-dom'; + +function IssueCommentList(props){ + const{history, history: {location}, reload, reloadComment, showNotification, current_user:{login, admin, image_url, user_id}, isManager, showLoginDialog, issueInfo:{author}} = props; + const {owner, projectsId, index} = props.match.params; + const [category, setCategory] = useState('comment'); + const [journals, setJournals] = useState(undefined); + // 是否展示新建/编辑评论markdown部分新建评论1 编辑父级评论2 回复评论3 编辑回复内容4 回复子机评论5 + const [showEdit, setShowEdit] = useState(false); + const [parentId, setParentId] = useState(undefined); + const [replyId, setReplyId] = useState(undefined); + // 修改评论id + const [updateId, setUpdateId] = useState(undefined); + // 操作日志展开效果 + const [open, setOpen] = useState(undefined); + // 加载中效果(缓冲css 线条效果) + const [spin, setSpin] = useState(false); + // issue 评论总数 + const [journalsCount, setJournalsCount] = useState(undefined); + // 分页 + const limit = 50; + const [page, setPage] = useState(1); + const [totalCount, setTotalCount] = useState(undefined); + // 操作记录 icon + const journalsIcon ={ + 'issue': 'icon-chuangjianqianbao', + 'branch_name': 'icon-fenzhi3', + 'assigner': 'icon-chengyuan2', + 'status_id': 'icon-xiugaibaobiaomoban', + 'fixed_version_id': 'icon-lichengbeiicon2', + 'due_date': 'icon-riqi', + 'issue_tag': 'icon-biaoji2', + 'description': 'icon-xiugaibaobiaomoban', + 'subject': 'icon-xiugaibaobiaomoban', + 'start_date': 'icon-riqi', + 'priority_id': 'icon-youxian', + 'attachment': 'icon-xiugaibaobiaomoban' + } + + useEffect(()=>{ + setSpin(true); + axios.get(`/v1/${owner}/${projectsId}/issues/${index}/journals`,{params:{ + category, + page, + limit + }}).then(res=>{ + if(res && res.data){ + const {journals, total_comment_journals_count, total_count} = res.data; + if(category === 'all'){ + const array = []; + let start = undefined; + let isJournalCount = 0; + journals.map((item, index)=>{ + !isJournalCount && (start = index) + item.is_journal_detail && (isJournalCount++) + if(!item.is_journal_detail && isJournalCount < 10){ + isJournalCount = 0; + return + } + if(isJournalCount >= 10 && (!item.is_journal_detail || journals.length-1 === index)){ + array.push({start, count: isJournalCount}); + isJournalCount = 0; + } + }) + array.map((item, i)=>{ + journals[item.start].numCount = item.count-1; + for (let index = 0; index < item.count; index++) { + journals[item.start+index].start = journals[item.start].id; + journals[item.start+index].closeAndSpan = true; + } + }) + } + setTotalCount(total_count); + setJournalsCount(total_comment_journals_count); + setJournals(journals); + setSpin(false) + } + }) + }, [reload, category, page]) + + function commentCtx(v){ + return ; + }; + + // 删除评论内容 + function deleteComment(id){ + axios.delete(`/v1/${owner}/${projectsId}/issues/${index}/journals/${id}`).then(res=>{ + if(res && res.data && !res.data.status){ + reloadComment(); + message.success('删除成功'); + } + }) + } + + // markdown 取消操作 + function cancelMd(){ + setShowEdit(false); + } + + return( +
    + {/* 全部 / 评论 / 操作日志 */} +
    0 && 'pb15'}`}> +
    + {setSpin(true);setPage(1);setCategory(e.target.value);history.push(history.pathname)}} value={category}> + 评论{journalsCount} + 操作日志 + 全部 + +
    +
    + {/* 评论快速入口-仅有评论且登录时展示 */} + {login && category !== 'operate' && journalsCount > 0 &&
    0 && 'pt15'}`}>
    } + {/* 评论/操作日志 展示列表 */} + +
    + {journals && (journals.length > 0 && journals.map(item=>{return item.is_journal_detail ? (!item.closeAndSpan || item.id === item.start || open === item.start) ?
    + {/* 操作日志 */} +
    +
    +
    + +
    + + {item.user.name}  + {(item.user.name.length + item.operate_content.length) > 62 ? {item.user.name}
    }> : } +
    + {timeAgo(item.created_at)} +
    + {(item.closeAndSpan && item.id === item.start) && {setOpen(open === item.id ? undefined : item.id)}}>{open === item.id ? `点击收起操作日志` : `已折叠${item.numCount}条, 点击查看`}} +
    +
    : '' :
    + {/* 评论 */} +
    + +
    + {/* 判断是否是编辑状态 */} + {(showEdit === 2 && updateId === item.id) ?
    :
    +
    +
    +
    + {item.user.name} + {timeAgo(item.created_at)} +
    + {login &&
    + {/* 平台管理员/仓库管理员/发布评论者/issue创建者 */} + {(admin || isManager || login === item.user.login || user_id === author.id) && 0 ? '子评论也将被一起删除。' : ''}`} + okText="是" + cancelText="否" + onConfirm={() => deleteComment(item.id)} + > + + } + {/* 仅评论者可修改 */} + {login === item.user.login && } + {setParentId(item.id); setReplyId(item.id); setShowEdit(3)}}> +
    } +
    +
    {commentCtx(item.notes)}
    + {item && item.attachments && item.attachments.length > 0 &&
    } +
    +
    } + {showEdit === 3 && replyId === item.id &&
    } + {/* 评论回复部分 */} + {item.children_journals.map(i =>{return
    + {(showEdit === 4 && updateId === i.id) ?
    :
    +
    +
    + + {i.user.name} + {i.reply_user && 回复} + {i.reply_user && i.reply_user.name} + {timeAgo(i.created_at)} +
    + {login &&
    + {/* 平台管理员/仓库管理员/发布评论者/回复评论者/issue创建者 */} + {(admin || isManager || login === i.user.login || (i.reply_user && login === i.reply_user.login) || user_id === author.id) && deleteComment(i.id)} + > + + } + {/* 仅回复评论者可修改 */} + {login === i.user.login && } + {setParentId(item.id);setReplyId(i.id); setShowEdit(5)}}> + + +
    } +
    +
    {commentCtx(i.notes)}
    + {i && i.attachments && i.attachments.length > 0 &&
    } +
    } + {showEdit === 5 && replyId === i.id &&
    } +
    })} +
    +
    }))} +
    + + {totalCount>limit &&
    + setPage(page)}/> +
    } + {/* 添加评论 */} + {category !== 'operate' &&
    + {login ? showEdit === 1 ? :
    + +
    + {setShowEdit(1)}}> + + +
    +
    :
    + {/* 未登录用户 */} + {showLoginDialog()}}>登录并参与评论与回复 +
    } +
    } +
    + ) +} +export default IssueCommentList; \ No newline at end of file diff --git a/src/forge/Issues/Component/comments/list.scss b/src/forge/Issues/Component/comments/list.scss new file mode 100644 index 000000000..be95b6e3d --- /dev/null +++ b/src/forge/Issues/Component/comments/list.scss @@ -0,0 +1,205 @@ +.typeActionBox{ + padding: 10px 20px 4px; + background-color:rgba(241, 243, 252, 0.55); + border-radius:4px; + .typeActionRadio{ + color: #333; + display: inline-flex; + align-items: center; + margin-right: 25px; + &.ant-radio-wrapper-checked{ + color: $primary-color; + } + } + .journalsCount{ + color:#5e6685; + background-color:rgba(70, 106, 255, 0.09); + border-radius:10px; + display: inline-block; + padding: 0px 6px; + } +} +.commentUserImg{ + height: 28px; + width: 28px; + border-radius: 50%; + object-fit: cover; +} +.issueCommentsBox{ + .iconBackBox{ + display: inline-block; + width:24px; + height:24px; + border-radius: 50%; + line-height: 24px; + text-align: center; + background-color:#f2f3f5; + } + .operationLog, .commentContentBox{ + position: relative; + min-height: 62px; + display: flex; + >.flexCenter, >a{ + z-index: 2; + } + &::before, &::after{ + content: ''; + width: 1px; + height: 50%; + position: absolute; + background: #eee; + left: 12px; + z-index: 1; + } + &::after{ + background: #eee; + top: 50%; + } + } + .commentContentBox+.operationLog .operationCommentBor, .operationLog+.commentContentBox .commentOperationBor{ + border-top: 1px solid #eee; + width: 98.5%; + position: absolute; + left: 12px; + } + .operationLog+.commentContentBox{ + &>a, &>.commentContentRight{ + margin-top: 25px; + } + } + .timeAgo{ + color:#acb0bf; + } + .operationLog:last-child::after, .operationLog:first-child::before, .commentContentBox:last-child::after, &.justComment .commentContentBox::before, &.justComment .commentContentBox::after{ + display: none; + } + .operationLog:first-child, .commentContentBox+.operationLog{ + margin-top: -15px; + } + .commentContentBox:last-child::before{ + height: 35%; + } + .flexCenter{ + display: flex; + align-items: center; + justify-content: space-between; + } + .commentRenderHtml.markdown-body p{ + font-size: 14px !important; + } + .commentReply{ + padding: 15px 0 0 20px; + background-color: rgba(238, 240, 246, 0.41); + &>div{ + padding-bottom: 5px; + } + &+.commentReply>div{ + margin-top: -15px; + padding-top: 15px; + border-top: 1px dashed #eee; + } + } + .opBox{ + justify-content: flex-start; + // flex: 1; + // width: 100%; + } + .commentContentBox{ + display: flex; + align-items: flex-start; + } + .commentContentRight{ + flex: 1; + background-color:#fafafc; + border:1px solid rgba(42, 97, 255, 0.23); + border-radius:6px; + position: relative; + top: -6px; + padding: 10px 15px 16px; + width: 0; + &::before{ + content: ''; + display: block; + position: absolute; + top: 13px; + left: -14px; + width: 0; + height: 0; + overflow: hidden; + font-size: 0; + line-height: 0; + border: 7px; + border-style: solid; + border-color: transparent rgba(42, 97, 255, 0.23) transparent transparent; + } + &::after{ + content: ''; + display: block; + width: 0; + height: 0; + overflow: hidden; + font-size: 0; + line-height: 0; + border: 6px; + border-style: solid; + border-color: transparent #fafafc transparent transparent; + position: absolute; + top: 14px; + left: -11px; + } + } +} +.primaryColor, .primaryColor:link{ + color: $primary-color; +} +.attachmentBox{ + margin-top: -8px; +} +.color-grey-89{ + color: #898d9d; +} +// 添加评论样式 +.unLoginComment{ + height:58px; + line-height: 58px; + background-color:rgba(241, 243, 252, 0.55); + border-radius:4px; + color: rgba(84, 87, 103, 1); + .loginBtn{ + color: $primary-color; + } +} +.quillFlagBox{ + color: red; + margin-bottom: 5px !important; + height: 28px; +} +.addComments{ + display: flex; + align-items: center; + background-color:rgba(241, 243, 252, 0.55); + border-radius:4px; + padding:7px 10px; + &>img{ + height: 28px; + width: 28px; + border-radius: 50%; + margin-right: 13px; + } +} +.addCommentBox{ + width: 100%; + height: 30px; + border: none; +} +.paginationIssueComment{ + text-align: center; +} +.gotoComment{ + background-color:rgba(241, 243, 252, 0.55); + border-radius:4px; + color: rgba(172, 176, 191, 1); + padding: 8px 20px; + margin-top: -10px; + &>a{color: rgba(70, 106, 255, 1);} +} \ No newline at end of file diff --git a/src/forge/Issues/Component/copy.jsx b/src/forge/Issues/Component/copy.jsx new file mode 100644 index 000000000..c10bf90e8 --- /dev/null +++ b/src/forge/Issues/Component/copy.jsx @@ -0,0 +1,22 @@ +import React ,{ useState } from 'react'; +import { Tooltip } from 'antd'; +import { CopyToClipboard } from 'react-copy-to-clipboard'; + + +function Copy({ children , value }){ + const [ title , setTitle ] = useState("点击复制链接"); + let host = window.location.hostname === "localhost" ? "testforgeplus.trustie.net" : window.location.hostname; + let protocol = window.location.protocol; + return( + setTitle("复制成功")}> + + {children} + + + ) +} +export default Copy; \ No newline at end of file diff --git a/src/forge/Issues/Component/datas.jsx b/src/forge/Issues/Component/datas.jsx new file mode 100644 index 000000000..4a99aeafc --- /dev/null +++ b/src/forge/Issues/Component/datas.jsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { getImageUrl } from 'educoder'; +import gold from '../Img/gold.png'; +import { Link } from "react-router-dom"; +import Copy from '../Component/copy'; +import { Tooltip } from 'antd'; + + +// issue列表显示 +function Datas({checkbox ,item , projectsId,owner}){ + + function statusTag(name){ + switch (name) { + case "低": + return "status low"; + case "正常": + return "status normals"; + case "高": + return "status hight"; + default: + return "status urgent"; + } + } + + function renderName(list){ + let l = list.map(i=>{return i.name}); + return l.join(","); + } + + return( +
    +
    + {checkbox} +
    +
    + {item.priority_name} + {/* */} + {item.subject} + { + item.tags && item.tags.length>0? + item.tags.map((i,k)=>{ + return( + {i.name} + ) + }) + :"" + } +
    +
    +
    +
    + { item.project_issues_index && + #{item.project_issues_index} + } +
    + + {item.author && item.author.name} + {item.created_at} 发布 + {item.updated_at}更新 + {item.blockchain_token_num && {item.blockchain_token_num}} + {item.milestone_name && + {window.scrollTo(0,0)}} style={{maxWidth:item.blockchain_token_num ? "261px":"340px",color:"#898d9d"}} title={item.milestone_name} className="task-hide mr20"> + + {item.milestone_name} + + } + {item.test_case} +
    +
    +
    +
    +
    + { + item.assigners && item.assigners.length > 0 ? + +
    1 ?"principal hovers":"principal"}> + {/* { + item.assigners.map((i,k)=>{ + return( + k<5 && + ) + }) + } */} + {renderName(item.assigners)} +
    +
    + :"" + } +
    {item.status_name}
    +
    + {item.comment_journals_count} +
    +
    +
    + ) +} +export default Datas; \ No newline at end of file diff --git a/src/forge/Issues/Component/date.jsx b/src/forge/Issues/Component/date.jsx new file mode 100644 index 000000000..bd4f00a9a --- /dev/null +++ b/src/forge/Issues/Component/date.jsx @@ -0,0 +1,149 @@ +import React,{useRef,useEffect, useState , forwardRef } from 'react'; +import { Dropdown , Calendar , Select, Radio, Col, Row } from 'antd'; +import { findDOMNode } from 'react-dom'; +import moment from 'moment'; +const { Group , Button } = Radio; + +function Date({name , today , setDate , editFlag},ref){ + const [ visible , setVisible ] = useState(false); + const [ time , setTime ] = useState(); + + useEffect(()=>{ + if(today){ + setTime(today); + } + },[today]) + + const refFa = useRef(null); + const refBox = useRef(null); + + useEffect(()=>{ + if(!visible && !editFlag && (time && time !== today)){ + setDate(time); + } + },[visible,editFlag,time]) + + + 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); + let role = target.getAttribute("role"); + if(!isChild && !isBox && !(role && role === "option")){ + setVisible(false); + } + } + } + function onSelect(t){ + setTime(moment(t).format('YYYY-MM-DD')); + setDate(moment(t).format('YYYY-MM-DD')); + setVisible(false); + } + const overlay=( +
    + { + const start = 0; + const end = 12; + const monthOptions = []; + const current = value.clone(); + const localeData = value.localeData(); + const months = []; + for (let i = 0; i < 12; i++) { + current.month(i); + months.push(localeData.monthsShort(current)); + } + + for (let index = start; index < end; index++) { + monthOptions.push( + + {months[index]} + , + ); + } + const month = value.month(); + + const year = value.year(); + const options = []; + for (let i = year - 10; i < year + 10; i += 1) { + options.push( + + {i} + , + ); + } + return ( +
    + + + + {console.log("typyCHange:",e);onTypeChange(e.target.value)}} value={type}> + + + + + + + + + + + +
    + ); + }} + onSelect={onSelect} + /> +
    + ) + return( + +
  • + + {name} + {!editFlag && setVisible(visible ? false : true)}> } + +

    {time || "未设置"}

    +
  • +
    + ) +} +export default forwardRef(Date); \ No newline at end of file diff --git a/src/forge/Issues/Component/delBox.jsx b/src/forge/Issues/Component/delBox.jsx new file mode 100644 index 000000000..89fed3e83 --- /dev/null +++ b/src/forge/Issues/Component/delBox.jsx @@ -0,0 +1,32 @@ +import React from "react"; +import Modals from '../../Component/PublicModal/Index'; +import { AlignTop } from '../../Component/layout'; +import { Button } from 'antd'; + +function DelBox({visible,onCancel,onSuccess,content,title}){ + return( + + + + + } + > +
    + + + { content ? + content + : +

    您确定要删除所有选中的疑修?

    此操作将清空所有已选中的疑修,请谨慎操作

    + } +
    +
    +
    + ) +} +export default DelBox; \ No newline at end of file diff --git a/src/forge/Issues/Component/delayAuto.jsx b/src/forge/Issues/Component/delayAuto.jsx new file mode 100644 index 000000000..dc411a471 --- /dev/null +++ b/src/forge/Issues/Component/delayAuto.jsx @@ -0,0 +1,54 @@ +import React , { useState , useEffect , useRef,useMemo} from 'react'; +import { Select , Spin } from 'antd'; +import debounce from 'lodash/debounce'; + +function DebounceSelect({ fetchOptions, debounceTimeout = 600, ...props }) { + const [fetching, setFetching] = useState(false); + const [options, setOptions] = useState([]); + const fetchRef = useRef(0); + + useEffect(()=>{ + fetchOptions("").then((newOptions) => { + setOptions(newOptions); + }); + },[]) + + const debounceFetcher = useMemo(() => { + const loadOptions = (value) => { + fetchRef.current += 1; + const fetchId = fetchRef.current; + setOptions([]); + setFetching(true); + fetchOptions(value).then((newOptions) => { + if (fetchId !== fetchRef.current) { + // for fetch callback order + return; + } + setOptions(newOptions); + setFetching(false); + }); + }; + return debounce(loadOptions, debounceTimeout); + }, [fetchOptions, debounceTimeout]); + + return ( + + ); +} +export default DebounceSelect \ No newline at end of file diff --git a/src/forge/Issues/Component/drop.jsx b/src/forge/Issues/Component/drop.jsx new file mode 100644 index 000000000..f90a19d69 --- /dev/null +++ b/src/forge/Issues/Component/drop.jsx @@ -0,0 +1,47 @@ +import React,{useState, useEffect, useRef, useImperativeHandle,forwardRef} from 'react'; +import { Dropdown } from 'antd'; +import { findDOMNode } from 'react-dom'; + +function Drop({overlay , children , placement, overlayClassName},ref){ + const [ visible , setVisible ] = useState(false); + const refFa = useRef(null); + const refBox = useRef(null); + + useImperativeHandle(ref, () => ({ + clearVisible: (v) => { + // 父组件按钮:清除筛选条件,将dropmenu里的choose字段全部改为0 + setVisible(v); + } + })) + + 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); + } + } + } + return( + {overlay}} + trigger={['click']} + overlayClassName={overlayClassName} + > + setVisible(visible ? false : true)}> + {children} + + + ) +} +export default forwardRef(Drop); \ No newline at end of file diff --git a/src/forge/Issues/Component/dropMenu.jsx b/src/forge/Issues/Component/dropMenu.jsx new file mode 100644 index 000000000..962243c0a --- /dev/null +++ b/src/forge/Issues/Component/dropMenu.jsx @@ -0,0 +1,210 @@ +import React ,{ useState , useEffect , useRef } from 'react'; +import { findDOMNode } from 'react-dom'; +import { Dropdown , Menu , Input , message} from 'antd'; +import { getImageUrl } from 'educoder'; + +const { Search } = Input; +/** + * + * @param {placeholder} placeholder:默认显示内容(为空时) + * @param {editFlag} editFlag:是否有编辑权限 + * @param {searchFlag} searchFlag:是否显示搜索框 + * @param {selectValueList} selectValueList:将选中的list保存 + * @param {searchFunc} searchFunc:搜索方法 + * @param {onAdd} onAdd:标记管理 + * @param {headImg} headImg:是否显示头像 + * @param {menus} menus:下拉列表 + * @param {chooseFunc} chooseFunc:选择下拉列表 + * @param {double} double:undefined为单选,有值就是最多选的数量 + * @param {colorFlag} colorFlag:选中值需根据颜色显示背景 + * @param {mustFlag} mustFlag:必须选择一个 + * @param {removeFlag} removeFlag:移除按钮 + * @returns + */ +function DropMenu({ + placeholder ="未设置", + editFlag, + searchFlag, + selectValueList, + searchFunc, + onAdd, + headImg, + menus, + chooseFunc,double,colorFlag,mustFlag,removeFlag +}){ + const [ visible , setVisible ] = useState(false); + const [ searchValue , setSearchValue ]= useState(undefined); + const [ valuesId , setValuesId ] = useState([]); + const [ valuesName , setValuesName ] = useState([]); + + const refFa = useRef(null); + const refBox = useRef(null); + + // 根据选中的列表循环出id数组 + useEffect(()=>{ + if(selectValueList && selectValueList.length > 0 ){ + renderSelectList(selectValueList); + }else{ + setValuesId([]) + } + },[selectValueList]) + + function renderSelectList(list){ + let a = list && list.length > 0 && list.map((i,k)=>{ + return i.id ? i.id.toString() : i.name + }) + setValuesId(a); + setValuesName(list); + } + + 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); + } + } + } + // 搜索 + function changeSearchvalue(e){ + setSearchValue(e.target.value); + searchFunc(e.target.value); + } + + function chooseMenu(i){ + let list = selectValueList; + let re = []; + re = list && list.length > 0 ? list:[]; + let filter = []; + if(i.id){ + filter = list.filter(j=>j.id === i.id); + }else{ + filter = list.filter(j=>j.name === i.name); + } + if(filter && filter.length > 0){ + if(!mustFlag){ + if(i.id){ + re = list.filter(j=>j.id !== i.id); + }else{ + re = list.filter(j=>j.name !== i.name); + } + renderSelectList(re); + chooseFunc(re); + !double && setVisible(false); + }else{ + setVisible(false); + } + }else{ + if(double && (selectValueList && selectValueList.length >= double)){ + message.info(`最多只能添加${double}个${placeholder}`); + return; + } + if(double){ + re.push(i); + }else{ + re = [i]; + setVisible(false); + } + renderSelectList(re); + chooseFunc(re); + } + } + + function showRemoveFunc(item){ + let l = selectValueList ; + l = l.filter(i=>(i.id ? i.id.toString() : i.name) !== item.toString()); + renderSelectList(l); + chooseFunc(l); + } + + function renderNames(nameArrs){ + return + { + nameArrs && nameArrs.length>0? + nameArrs.map((i,k)=>{ + return( +

    + {i.image_url && } + { + colorFlag ? + ( + colorFlag === "2" ? + {i.name} + : + {i.name} + ) + : + {i.name} + } + { removeFlag && showRemoveFunc(i.id || i.name)}>} +

    + ) + }) + :{placeholder} + } +
    + } + return( +
  • + + {placeholder} + {!editFlag && + setVisible(visible ? false : true)}> + + + } + + + { searchFunc && +
    + +
    + } + { + menus && menus.length >0? + + { + menus.map((i,k)=>{ + return( + chooseMenu(i)}> + { headImg && } + {i.color && } + {i.name} + + ) + }) + } + + : +
    +

    {searchValue ? 暂无{placeholder}“{searchValue}”: `暂无${placeholder}`}

    +
    + } + { onAdd && } + + }> +
    0 ? "operatevalue color-grey-3":"operatevalue"}>{renderNames(selectValueList)}
    +
    +
  • + ) +} +export default DropMenu; \ No newline at end of file diff --git a/src/forge/Issues/Component/menus.jsx b/src/forge/Issues/Component/menus.jsx new file mode 100644 index 000000000..24aa86ad4 --- /dev/null +++ b/src/forge/Issues/Component/menus.jsx @@ -0,0 +1,93 @@ +import React ,{ useState , useRef , useEffect } from 'react'; +import { Input , Menu , Icon } from 'antd'; +import { getImageUrl } from 'educoder'; +import Drop from './drop'; +const { Search } = Input; + +/** + * @param {className}
  • 的样式名称 + * @param {name} 未选择时显示的内容 + * @param {lists} 下拉列表,从接口获取,需一一填充 + * @param {imgControl} 控制是否显示头像 + * @param {size} 下拉框宽度,small:120px,large:260px + * @param {ids} 默认选择的项的id数组 + * @param {search} 搜索方法 + * @param {chooseFunc} 选择项后需调用列表的查询方法 + * @param {update} 编辑状态 + * @param {double} 是否可以多选 + */ +function Menus({className,name, ids , lists , size , imgControl , searchFunc , chooseFunc , update , double ,names },ref){ + const dropRef = useRef(null); + const [ chooseValue , setChooseValue ] = useState([]); + const [ showValue , setShowValue ] = useState(undefined); + + useEffect(()=>{ + ids ? setChooseValue(ids.split(",")) : setChooseValue([]); + setShowValue(names); + },[ids,names]) + + + // 修改menus数组里的字段值 + function changeMenusValue(i){ + let l = ids; + let id = i.id.toString(); + if(double){ + l = ids ? ids.split(",") : [] ; + let nameArr = names ? names.split(",") : [] ; + if(l && l.indexOf(id)>=0){ + l = l.filter(k=>k.toString() !== id); + nameArr = nameArr.filter(k=>k.toString() !== i.name); + }else{ + l.push(id); + nameArr.push(i.name); + } + setChooseValue(l); + setShowValue(nameArr.join(",")); + chooseFunc(l,nameArr.join(",")); + }else{ + if(l && l.indexOf(id)>=0){ + setChooseValue([]); + setShowValue(undefined); + chooseFunc([]); + }else{ + setChooseValue([`${i.id}`]); + setShowValue([i.name]); + chooseFunc([id],i.name); + } + dropRef.current && dropRef.current.clearVisible(false); + } + } + + function menu(data){ + return
    + { size !== "small" && +
    searchFunc(e.target.value)}/>
    + } + { + data && data.length>0 ? + + { + data.map((i,j)=>{ + return + {imgControl && } + {i.color && } + changeMenusValue(i)}>{i.name} + + }) + } + + : +
    暂无{ids ? '{name}': name}
    + } +
    + } + return( +
  • + + {showValue || (update ? `更换${name}` : name) } + + +
  • + ) +} +export default Menus; \ No newline at end of file diff --git a/src/forge/Issues/Component/newPanel.jsx b/src/forge/Issues/Component/newPanel.jsx new file mode 100644 index 000000000..67a02943f --- /dev/null +++ b/src/forge/Issues/Component/newPanel.jsx @@ -0,0 +1,129 @@ +import React , { forwardRef , useState } from 'react'; +import { Form , Input , Button } from 'antd'; +import MDEditor from "../../../modules/tpm/challengesnew/tpm-md-editor"; +import Upload from "../../Upload/Index"; +import Attachments from "../../Upload/attachment"; +import UploadImg from '../Img/UploadImg.png'; + +function NewPanel(props,ref){ + const [ description , setDescription ] = useState(undefined); + const [ fileList , setFileList] = useState(undefined); + const [ attachments , setAttachments ] = useState([]); + const [ save , setSave ] = useState([]); + const [ receivers_login , setReceiversLogin ] = useState(undefined); + + const { createFunc , title , desc , files , onCancel , owner , projectsId } = props; + + const { form: { getFieldDecorator, validateFields , setFieldsValue } } = props; + useState(()=>{ + title && setTimeout(()=>{ + setFieldsValue({subject:title}); + desc && onContentChange(desc); + },100) + files && setAttachments(files); + files && setSave(files); + },[title , desc , files]) + + function onContentChange(value){ + setDescription(value); + } + function UploadFunc(f){ + let list = []; + let arr = []; + if(save && save.length>0 ){ + arr = save.map(i=>{return i.id}); + } + list = arr && arr.length>0 ? f.concat(arr) : f ; + setFileList(list); + }; + + function deleteLoad(id){ + let arr = []; + let list = save; + if(list && list.length>0 ){ + arr = list.filter(i=>i.id !== id); + } + setFileList(arr.map(i=>{return i.id})); + setSave(arr); + } + + function changeAtWhoLoginList(loginList){ + let list = new Set(receivers_login); + loginList.map(item => list.add(item)); + setReceiversLogin(Array.from(list)); + }; + + function sureFunc(){ + validateFields((error,values)=>{ + if (!error) { + createFunc(values,fileList,receivers_login,description); + } + }) + } + + function cancelFunc(){ + if(onCancel){ + onCancel(); + }else{ + window.history.back(-1); + } + } + return( + +
    + + {getFieldDecorator("subject",{ + rules:[{required:true,message:"请输入疑修标题"}] + })( + + )} + +
    +
    + +
    +
    + + } + size={100} + showNotification={props.showNotification} + /> + {attachments && attachments.length > 0 && + + } +
    +
    + + +
    +
    + ) +} +export default forwardRef(NewPanel); diff --git a/src/forge/Issues/Img/UploadImg.png b/src/forge/Issues/Img/UploadImg.png new file mode 100644 index 000000000..cb1e7d05c Binary files /dev/null and b/src/forge/Issues/Img/UploadImg.png differ diff --git a/src/forge/Issues/Img/biaoji.png b/src/forge/Issues/Img/biaoji.png new file mode 100644 index 000000000..e40377459 Binary files /dev/null and b/src/forge/Issues/Img/biaoji.png differ diff --git a/src/forge/Issues/Img/create.png b/src/forge/Issues/Img/create.png new file mode 100644 index 000000000..63b4cc29c Binary files /dev/null and b/src/forge/Issues/Img/create.png differ diff --git a/src/forge/Issues/Img/emp.png b/src/forge/Issues/Img/emp.png new file mode 100644 index 000000000..5db7c0778 Binary files /dev/null and b/src/forge/Issues/Img/emp.png differ diff --git a/src/forge/Issues/Img/gold.png b/src/forge/Issues/Img/gold.png new file mode 100644 index 000000000..3c64743b6 Binary files /dev/null and b/src/forge/Issues/Img/gold.png differ diff --git a/src/forge/Issues/Img/issue-big.png b/src/forge/Issues/Img/issue-big.png new file mode 100644 index 000000000..8d73e1d93 Binary files /dev/null and b/src/forge/Issues/Img/issue-big.png differ diff --git a/src/forge/Issues/Img/issue.png b/src/forge/Issues/Img/issue.png new file mode 100644 index 000000000..7a85ffff7 Binary files /dev/null and b/src/forge/Issues/Img/issue.png differ diff --git a/src/forge/Issues/Img/millestones-big.png b/src/forge/Issues/Img/millestones-big.png new file mode 100644 index 000000000..b57f83f31 Binary files /dev/null and b/src/forge/Issues/Img/millestones-big.png differ diff --git a/src/forge/Issues/Img/pr.png b/src/forge/Issues/Img/pr.png new file mode 100644 index 000000000..fc3deacf0 Binary files /dev/null and b/src/forge/Issues/Img/pr.png differ diff --git a/src/forge/Issues/Pages/details.jsx b/src/forge/Issues/Pages/details.jsx new file mode 100644 index 000000000..61f423bd9 --- /dev/null +++ b/src/forge/Issues/Pages/details.jsx @@ -0,0 +1,525 @@ +import React , { useEffect , useState , forwardRef , useRef } from 'react'; +import { Spin , Form , InputNumber, message } from 'antd'; +import { getImageUrl } from 'educoder'; +import { Link } from 'react-router-dom'; +import { Box , LongWidth } from '../../Component/layout'; +import RenderHtml from "../../../components/render-html"; +import DropMenu from '../Component/dropMenu'; +import NewPanel from '../Component/newPanel'; +import Attachments from "../../Upload/attachment"; +import DelBox from '../Component/delBox'; +import Date from '../Component/date'; +import moment from 'moment'; +import Copy from '../Component/copy'; +import axios from 'axios'; +import CommentList from '../Component/comments/list'; +import Claims from '../../claims/claims'; +import ChooseMenu from '../Component/chooseMenu'; +import CaseDrop from '../Component/caseDrop'; + +function Details(props){ + const owner = props.match.params.owner; + const projectsId = props.match.params.projectsId; + const index = props.match.params.index; + + const [ details , setDetails ] = useState(undefined); + const [ statusList , setStatusList ] = useState([]); + const [ prioritiesList , setPrioritiesList ] = useState([]); + const [ chargeList , setChargeList ] = useState([]); + const [ charge , setCharge ] = useState(undefined); + const [ millstoneList , setMillstoneList ] = useState([]); + const [ millstone , setMillstone ] = useState(undefined); + const [ tagList , setTagList ] = useState(undefined); + const [ tag , setTag ] = useState(undefined); + const [ branchList , setBranchList ] = useState(undefined); + const [ branch , setBranch ] = useState(undefined); + const [ desc , setDesc ] = useState(undefined); + + const [ orderId , setOrderId ] = useState(undefined); + + const [ delVisible , setDelVisible] = useState(false); + const [ edit , setEdit] = useState(false); + const [ copy , setCopy] = useState(false); + + const [ start_date, setStartDate ] = useState(""); + const [ due_date, setDueDate ] = useState(""); + const [ commentReload, setCommentReload] = useState(undefined); + const [ editFlag , setEditFlag ] = useState(false); + const [ amountEditFlag , setAmountEditFlag ] = useState(false); + + const [ assigner_choose , setAssigner_choose ] = useState([]); + const [ prioritie_choose , setPrioritie_choose ] = useState([]); + const [ status_choose , setStatus_choose ] = useState([]); + const [ tag_choose , setTag_choose ] = useState([]); + const [ millstone_choose , setMillstone_choose ] = useState([]); + const [ branch_choose , setBranch_choose ] = useState([]); + const [ rewardAmount , setRewardAmount ] = useState(undefined); + const [ rewardFlag , setRewardFlag ] = useState(false); + const [ caseValue, setCaseValue ] = useState(undefined); + + const pathname = props.history.location.pathname; + const permission = props && props.projectDetail && props.projectDetail.permission; + let colors = ["#1abcb1","#28be6c","#e67e22","#db3d1d"]; + + const {projectDetail , open_blockchain ,current_user } = props; + useEffect(()=>{ + if(pathname === `/${owner}/${projectsId}/issues/${index}/copy`){ + setEdit(true); + setCopy(true); + }else{ + setEdit(false); + setCopy(false); + } + },[pathname]) + + useEffect(()=>{ + if(projectDetail && details){ + const { author, name} = projectDetail; + document.title = `${details.subject}-疑修-${author.name}/${name}`; + } + },[details,projectDetail]) + + useEffect(()=>{ + if(index){ + Init(); + } + },[index]) + + function Init(){ + const url = `/v1/${owner}/${projectsId}/issues/${index}`; + axios.get(url).then(result=>{ + if(result && result.data){ + let data = result.data; + setDetails(data); + let per = data && !data.user_permission; + setEditFlag(per); + + setDesc(data && data.description); + + const aut = current_user && data.author && (data.author.login === current_user.login); + const con = data.pull_fixed === false && aut; + setAmountEditFlag(con); + + setAssigner_choose(data.assigners); + if(data.priority && data.priority.id){ + setPrioritie_choose([{...data.priority,color:colors[data.priority.id-1]}]); + }else{ + setPrioritie_choose([]); + } + setStatus_choose(data.status && data.status.id ? [data.status]:[]); + setTag_choose(data.tags); + setMillstone_choose(data.milestone && data.milestone.id ? [data.milestone]:[]); + setBranch_choose(data.branch_name ? [{name:data.branch_name}] : []); + setRewardAmount(data.blockchain_token_num); + setCaseValue(data.test_case); + + data.start_date && setStartDate(moment(data.start_date).format('YYYY-MM-DD')); + data.due_date && setDueDate(moment(data.due_date).format('YYYY-MM-DD')); + + setOrderId(data.id); + } + }).catch(error=>{ + console.log(error); + }) + } + + + function statusTag(name){ + switch (name) { + case "低": + return "status low"; + case "正常": + return "status normals"; + case "高": + return "status hight"; + default: + return "status urgent"; + } + } + // 获取状态列表 + useEffect(()=>{ + getStatus(); + },[]) + function getStatus(){ + const url = `/v1/${owner}/${projectsId}/issue_statues`; + axios.get(url).then(result=>{ + if(result && result.data){ + setStatusList(result.data.statues); + } + }) + } + // 获取优先级列表 + useEffect(()=>{ + getPriorities(); + },[]) + + function getPriorities(){ + const url = `/v1/${owner}/${projectsId}/issue_priorities`; + axios.get(url).then(result=>{ + if(result && result.data){ + let priorities = result.data.priorities; + let array = addColorForPriorities(priorities); + setPrioritiesList(array); + } + }) + } + + function addColorForPriorities(list){ + let array =[]; + if(list && list.length>0){ + array = list.map((i,k)=>{ + let arr = {...i,color:colors[k]} + return arr; + }) + } + return array; + } + + // 获取负责人列表 + useEffect(()=>{ + getCharge(); + },[charge]) + + function getCharge(){ + const url = `/v1/${owner}/${projectsId}/collaborators`; + axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{ + if(result && result.data){ + setChargeList(result.data.collaborators); + } + }) + } + // 获取里程碑列表 + useEffect(()=>{ + getMillstone(); + },[millstone]) + + function getMillstone(){ + const url = `/v1/${owner}/${projectsId}/milestones`; + axios.get(url,{params:{keyword:millstone,only_name:true}}).then(result=>{ + if(result && result.data){ + setMillstoneList(result.data.milestones); + } + }) + } + // 获取标记列表 + useEffect(()=>{ + getSign(); + },[tag]) + function getSign(){ + const url = `/v1/${owner}/${projectsId}/issue_tags`; + axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{ + if(result && result.data){ + setTagList(result.data.issue_tags); + } + }) + } + // 获取标记列表 + useEffect(()=>{ + getBranch(branch); + },[branch]) + function getBranch(branch){ + if(branch){ + let b = [...branchList]; + let l = b.filter(i=>i.name.indexOf(branch)>-1); + setBranchList(l); + return ; + } + const url = `/${owner}/${projectsId}/branches.json`; + axios.get(url,{params:{keyword:tag}}).then(result=>{ + if(result && result.data){ + setBranchList(result.data); + } + }) + } + + // 可单个编辑保存右侧的项(!copy:非复制状态下) + function saveForeach(c,s,p,t,m,b,sDate,eDate,rewardAmount,caseValue){ + if(!copy){ + const url = `/v1/${owner}/${projectsId}/issues/${index}`; + axios.patch(url,{ + branch_name: b ? b.join(",") :undefined, + status_id: s ? s.join(",") : undefined, + priority_id: p ? p.join(",") :undefined, + milestone_id: m ? m.join(",") :undefined, + issue_tag_ids: t?t:undefined, + assigner_ids: c?c:undefined, + start_date:sDate, + due_date:eDate, + blockchain_token_num:rewardAmount, + test_case:caseValue + }).then(result=>{ + if(result){ + Init(); + // 更新操作日志 + setCommentReload(Math.random()); + } + }).catch(error=>{}) + } + } + + function deleteFunc(){ + const url = `/v1/${owner}/${projectsId}/issues/${index}`; + axios.delete(url).then(result=>{ + if(result){ + props.showNotification("疑修删除成功!"); + props.history.push(`/${owner}/${projectsId}/issues`); + } + }).catch(error=>{}) + } + + // 编辑保存 + function createFunc(values,fileList,receivers_login,description){ + let params ={ + subject: values.subject, + attachment_ids: fileList, + receivers_login: receivers_login, + description: description + } + if(!copy){ + // 调用编辑保存接口 + const url = `/v1/${owner}/${projectsId}/issues/${index}`; + axios.patch(url,{ + ...params + }).then(result=>{ + if(result){ + window.scrollTo(0,0); + props.showNotification("疑修更新成功!"); + Init(); + setEdit(false); + // 刷新操作记录 + setCommentReload(Math.random()); + } + }).catch(error=>{}) + }else{ + // 调用新建保存接口 + const url = `/v1/${owner}/${projectsId}/issues`; + let b = branch_choose && branch_choose.length>0 ? branch_choose.map(i=>{return i.name}) :undefined; + let t = tag_choose && tag_choose.length>0 ? tag_choose.map(i=>{return i.id}) :undefined; + let a = assigner_choose && assigner_choose.length>0 ? assigner_choose.map(i=>{return i.id}) :undefined; + let m = millstone_choose && millstone_choose.length>0 ? millstone_choose.map(i=>{return i.id}) :undefined; + let p = prioritie_choose && prioritie_choose.length>0 ? prioritie_choose.map(i=>{return i.id}) :undefined; + let s = status_choose && status_choose.length>0 ? status_choose.map(i=>{return i.id}) :undefined; + axios.post(url,{ + ...params, + branch_name: b && b.join(","), + status_id: s && s.join(","), + priority_id: p && p.join(","), + milestone_id: m && m.join(","), + issue_tag_ids: t, + assigner_ids: a, + start_date, + due_date + }).then(result=>{ + if(result && result.data && result.data.project_issues_index){ + window.scrollTo(0,0); + props.showNotification("任务复制成功!"); + props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`); + } + }).catch(error=>{}) + } + } + + // 取消编辑或复制 + function onCancel(){ + if(copy){ + props.history.push(`/${owner}/${projectsId}/issues/${index}`); + }else{ + setEdit(false); + window.scrollTo(0,0); + } + } + + function refreshFunc(){ + // 更新操作日志 + setCommentReload(Math.random()); + } + + + // 悬赏金额 失去焦点保存数据 + function amountBlur(e){ + if(!editFlag){ + setRewardFlag(false); + const value = e.target.value; + if(value<0){ + message.info("请输入大于0的正整数"); + setRewardAmount(rewardAmount); + return; + } + setRewardAmount(e.target.value); + if((value && value >= 0) || !value){ + saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,value); + } + } + } + function changeAmount(value){ + if(amountEditFlag){ + setRewardAmount(value); + }else{ + setRewardAmount(rewardAmount); + } + } + return( + details ? + + + setDelVisible(false)} + onSuccess={deleteFunc} + content={

    您确定要删除当前疑修?

    } + /> + { + edit ? +
    + +
    + : +
    +
    +
    +
    +
    + {details.priority && { details.priority.name } } +

    {details.subject}

    +
    +
    +
    + #{details.project_issues_index} +
    + { + details.author && +
    + {details.author.name} + 添加于{details.created_at} +
    + } +
    +
    + { + details.user_permission && + + } +
    +
    + {desc ? + + : + 暂无描述 + } + {details.attachments && details.attachments.length > 0 ? + + : "" + } +
    +
    +
    + {setCommentReload(Math.random())}}/> +
    +
    + } +
    +
    + {orderId &&
    } + {setCharge(value)}} + headImg + selectValueList={assigner_choose} + editFlag={editFlag} + chooseFunc={(list)=>{setAssigner_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(l);}} + double={5} + removeFlag={!editFlag} + /> + {setStatus_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,l);}} + /> + {setPrioritie_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,l);}} + /> + {setTag(value)}} + selectValueList={tag_choose} + chooseFunc={(list)=>{setTag_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,l);}} + double={3} + colorFlag="2" + removeFlag={!editFlag} + editFlag={editFlag} + onAdd={permission && permission !== "Reporter" ? ()=>{getSign();} :false} + owner={owner} + projectsId={projectsId} + /> + {setMillstone(value)}} + selectValueList={millstone_choose} + editFlag={editFlag} + auto + chooseFunc={(list)=>{setMillstone_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,undefined,l);}} + /> + {setBranch(value)}} + selectValueList={branch_choose} + editFlag={editFlag} + chooseFunc={(list)=>{setBranch_choose(list);let l = list && list.length>0 ?list.map(i=>{return i.id || i.name}):[];saveForeach(undefined,undefined,undefined,undefined,undefined,l);}} + /> + { + open_blockchain && +
  • + + 悬赏金额 + {/* {!editFlag && } */} + + +
  • + } + setCaseValue(v)} onBlur={(v)=>saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,v)}/> + {setStartDate(date);saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,date)}} editFlag={editFlag}/> + {setDueDate(date);saveForeach(undefined,undefined,undefined,undefined,undefined,undefined,undefined,date)}} editFlag={editFlag}/> +
    +
    + : +
    + ) +} +export default Form.create()(forwardRef(Details)); \ No newline at end of file diff --git a/src/forge/Issues/Pages/list.jsx b/src/forge/Issues/Pages/list.jsx new file mode 100644 index 000000000..bc177ec84 --- /dev/null +++ b/src/forge/Issues/Pages/list.jsx @@ -0,0 +1,461 @@ +import React , { useRef , useState , useEffect } from 'react'; +import { Dropdown , Menu , Icon , Input , Checkbox , Pagination, Button , Tooltip ,Spin , DatePicker } from 'antd'; +import bj from '../Img/biaoji.png'; +import issueEmp from '../Img/issue-big.png'; +import { Link } from "react-router-dom"; +import AllMenus from '../Component/allMenus'; +import CheckProfile from '../../Component/ProfileModal/Profile'; +import Datas from '../Component/datas'; +import DelBox from '../Component/delBox'; +import axios from 'axios'; +import cookie from 'react-cookies'; +import emp from '../Img/emp.png'; +import moment from 'moment'; + +const { RangePicker } = DatePicker; + +const { Search } = Input; +const defaultLimit = 15; + +function List(props){ + const [ visible , setVisible ] = useState(false); + const [ issueList , setIssueList ] = useState(undefined); + const [ closedCount , setClosedCount ] = useState(0); + const [ openedCount , setOpenedCount ] = useState(0); + + const[ aboutMe , setAboutMe ] = useState("all"); + const[ value , setValue ] = useState(""); + const[ keyword , setKeyword ] = useState(undefined); + + const [ category , setCategory] = useState("opened"); + const [ begin_date , setBegin] = useState(undefined); + const [ end_date , setEnd] = useState(undefined); + + const [ allValue , setAllValue ] = useState([]); + const [ allIds , setAllIds ] = useState([]); + const [ checkAll , setCheckAll ] = useState(false); + + const [ names , setNames ] = useState(undefined); + const [ updateIds , setUpdateIds ] = useState(undefined); + const [ updateChooseIds , setUpdateChooseIds ] = useState(undefined); + + const [ page , setPage ] = useState(1); + const [ limit , setLimit ] = useState(15); + const [ total , setTotal ] = useState(undefined); + const [ issueTotal , setIssueTotal ] = useState(0); + + const [ clearFlag , setClearFlag ] = useState(false); + const [ has_created_issues , setHas_created_issues] = useState(false); + + const menuRef = useRef(null); + const owner = props.match.params.owner; + const projectsId = props.match.params.projectsId; + const permission = props && props.projectDetail && props.projectDetail.permission; + const { projectDetail , current_user , open_blockchain} = props; + + useEffect(()=>{ + if(projectDetail){ + const { author, name } = projectDetail; + document.title = `疑修-${author.name}/${name}`; + } + },[projectDetail]) + + useEffect(()=>{ + const datas = cookie.load('issuestates'); + let states = datas === "undefined" ? undefined : datas; + if(states){ + setAboutMe(states.participant_category); + setCategory(states.category); + setPage(states.page); + setLimit(states.limit || defaultLimit); + setUpdateIds({...states}); + setNames(states.names); + setBegin(states.begin_date); + setEnd(states.end_date); + // if(states.participant_category ==="all" && states.category === "opened" && states.page === 1 && states.limit===defaultLimit) + // Init({...states},states.page,states.names); + }else{ + Init(); + } + },[]) + + useEffect(()=>{ + let v = allValue && allValue > 0; + if((updateIds && !v && (updateIds.author_id || updateIds.issue_priorities_id || updateIds.issue_tag_ids || updateIds.milestone_id || updateIds.sort_by || updateIds.status_id ||updateIds.assigner_id)) || value || end_date || begin_date || aboutMe !=="all"){ + setClearFlag(true); + }else{ + setClearFlag(false); + } + },[updateIds,value,allValue,begin_date,end_date,aboutMe]) + // 自定义一个初始不更新的hook + const useUpdateEffect = (fn, inputs) => { + const didMountRef = useRef(false); + useEffect(() => { + if (didMountRef.current) fn(); + else didMountRef.current = true; + }, inputs); + }; + + // 定义处理函数 + function handleAllDisabled(){ + Init(updateIds,page,names); + } + + // 使用自定义hook + useUpdateEffect(handleAllDisabled, [aboutMe,keyword,category,limit,end_date,updateIds]); + + + function getDirection(id){ + if(!id) return undefined; + if(id === "1" || id === "3" || id === "5"|| id === "7"){ + return "desc" + }else{ + return "asc" + } + } + function getBy(id){ + if(!id) return undefined; + if(id === "1" || id === "2"){ + return "issues.created_on" + }else if(id === "3" || id === "4"){ + return "issues.updated_on" + }else if(id === "5" || id === "6"){ + return "issue_priorities.position" + }else{ + return "issues.blockchain_token_num" + } + } + + // 获取issue列表数据 + function Init(params,p,names){ + setTotal(undefined); + const datas = cookie.load('issuestates'); + let states = datas === "undefined" ? undefined : datas; + if(states){ + cookie.remove('issuestates'); + } + const url = `/v1/${owner}/${projectsId}/issues`; + axios.get(url,{ + params:{ + ...params, + page:p || 1, + keyword, + participant_category:aboutMe, + category, + limit, + begin_date,end_date, + sort_direction:getDirection(params && params.sort_by), + sort_by:getBy(params && params.sort_by),names:undefined + } + }).then(result=>{ + if(result){ + setIssueList(result.data.issues); + setTotal(result.data.total_count); + setClosedCount(result.data.closed_count); + setOpenedCount(result.data.opened_count); + const ids = result.data.issues.length>0 ? result.data.issues.map(i=>{return i.id}) : []; + setAllIds(ids); + setIssueTotal(result.data.total_issues_count); + setHas_created_issues(result.data.has_created_issues); + + const d = {...params,keyword,participant_category:aboutMe,category,limit,page:p || 1, + sort_direction:getDirection(params && params.sort_by),begin_date,end_date, + sort_by:getBy(params && params.sort_by),names:names}; + + let inFifteenMinutes = new Date(new Date().getTime() + 24 * 3600 * 1000); + cookie.save('issuestates', {...d},{ expires: inFifteenMinutes,path:`/` }); + } + }).then(error=>{}) + } + + // 第一个下拉搜索 + const menu = ( + + 全部 + 与我相关 + 我负责的 + 我创建的 + @我的 + + ) + function chooseAboutMe(e){ + setPage(1); + // setCategory("opened"); + setAboutMe(e.key); + } + + //清除筛选条件 + function clearCondition(){ + setKeyword(undefined); + setAboutMe("all"); + setCategory("opened"); + setUpdateIds(undefined); + setAllValue([]); + setPage(1); + setValue(undefined); + setNames(undefined); + setEnd(undefined); + setBegin(undefined); + // if(!value){ + // Init(); + // } + // 清除下拉选项 + menuRef.current && menuRef.current.clearChoose(); + } + + // 全选所有issue + function chooseAll(e){ + setCheckAll(e.target.checked); + // 清除下拉选项 + menuRef.current && menuRef.current.clearChoose(); + if(e.target.checked){ + setAllValue(allIds); + }else{ + setAllValue([]); + } + } + + // 选择列表里的issue + function checkIssues(value){ + // 清除下拉选项 + menuRef.current && menuRef.current.clearChoose(); + setAllValue(value); + if(value.length === allIds.length){ + setCheckAll(true); + }else{ + setCheckAll(false); + } + } + + // 取消 + function cancelUpdate(){ + setAllValue([]); + setCheckAll(false); + // 清除下拉选项 + menuRef.current && menuRef.current.clearChoose(); + } + // 确认修改 allValue updateIds + function sureUpdate(){ + const url = `/v1/${owner}/${projectsId}/issues/batch_update`; + axios.patch(url,{ + assigner_ids: updateChooseIds && updateChooseIds.assigner_id && updateChooseIds.assigner_id.split(","), + ids: allValue, + issue_tag_ids: updateChooseIds && updateChooseIds.issue_tag_ids && updateChooseIds.issue_tag_ids.split(","), + milestone_id: updateChooseIds && updateChooseIds.milestone_id, + priority_id: updateChooseIds && updateChooseIds.issue_priorities_id, + status_id: updateChooseIds && updateChooseIds.status_id + }).then(result=>{ + if(result){ + setUpdateIds(undefined); + setAllValue([]); + setTotal(undefined); + Init(); + cancelUpdate(); + } + }).catch(error=>{}) + } + + // 切换页码 + function changepage(p){ + setPage(p); + Init(updateIds,p,names); + if (document) { // 可以排除不需要置顶的页面 + if (document.documentElement || document.body) { + document.documentElement.scrollTop = document.body.scrollTop = 0; // 切换路由时手动置顶 + } + } + } + + // 删除issue相关 func + function onSuccess(){ + const url = `/v1/${owner}/${projectsId}/issues/batch_destroy`; + axios.delete(url,{ + params:{ids:allValue} + }).then(result=>{ + if(result){ + setUpdateIds(undefined); + setAllValue([]); + setVisible(false); + setCheckAll(false); + props.showNotification("疑修删除成功!"); + Init(); + } + }).catch(error=>{}) + } + + function chooseFunc(ids,n){ + setNames(n); + setPage(1); + if(allValue && allValue.length>0){ + // 将ids保存下来以便修改 + // setUpdateIds(ids); + setUpdateChooseIds(ids); + }else{ + if(ids.status_id === "5"){ + setCategory("closed"); + }else{ + setCategory("opened"); + } + setUpdateIds(ids); + setTotal(undefined); + // Init(ids,1,n); + } + } + + function chageLimit(c,p){ + setPage(1); + setLimit(p); + } + function changeCategory(value){ + if(value !== category){ + let ids = {...updateIds}; + let ns = {...names}; + if(value === "closed"){ + ns = {...ns,status_name:"关闭"} + ids = {...ids,status_id:'5'}; + setNames(ns) + setUpdateIds(ids); + }else{ + ns = {...ns,status_name:undefined} + ids = {...ids,status_id:undefined}; + setNames(ns) + setUpdateIds(ids); + } + setCategory(value); + setPage(1); + setTotal(undefined); + } + } + + function changeSearchValueFunc(e){ + setValue(e.target.value); + if(e.target.value===""){ + setKeyword(undefined); + } + } + + // 选择搜索时间 + function changeBeginTime(data, value){ + setPage(1); + setBegin(value[0] || ''); + setEnd(value[1] || ''); + } + return( +
    + setVisible(false)} onSuccess={onSuccess}/> +
    +
    + { + (current_user && current_user.login) && + + + {aboutMe === "all" ? "全部":aboutMe === "aboutme"?"与我相关":aboutMe === "assignedme"?"我负责的":aboutMe === "authoredme"?"我创建的":"@我的"} + + + + } + setKeyword(value)} + style={{ width: 354 , height : 32 }} + allowClear + /> + { + clearFlag && + + 清除筛选条件 + } +
    +
    + + { + permission && permission !== "Reporter" && + 标记管理 + } + {props.history.push(`/${owner}/${projectsId}/issues/new`)}} checklogin className="operateButton ml20">创建疑修 +
    +
    +
    +
    +
    + + { + allValue && allValue.length>0 ? + 选择{allValue.length}个issue + : +
      +
    • {changeCategory("all")}}>全部{issueTotal}
    • +
    • {changeCategory("opened")}}>开启中{openedCount}
    • +
    • {changeCategory("closed")}}>已关闭{closedCount}
    • +
    + } +
    +
    + 0} + owner={owner} + projectsId={projectsId} + chooseFunc={chooseFunc} + defaultNames={names} + defaultIds={allValue && allValue.length>0 ? undefined : updateIds} + open_blockchain={open_blockchain} + /> + { + allValue && allValue.length>0 ? +
    + + + +
    + :"" + } +
    +
    + { + total === 0 && + (!has_created_issues ? +
    + +

    欢迎使用疑修(Issue)

    +

    疑修用于记录与跟踪待办事项、项目bug、功能需求等。在使用之前,请您先{props.history.push(`/${owner}/${projectsId}/issues/new`)}} className="color-blue">创建一个疑修

    +
    + : +
    + ) + } + { + total > 0 && + + +
    + {issueList.map((item,key)=>{ + return( + } + item={item} + owner={owner} + projectsId={projectsId} + /> + ) + }) + } +
    +
    + { + total > defaultLimit && +
    + +
    + } +
    + } + {total === undefined &&
    } +
    +
    + ) +} +export default List; \ No newline at end of file diff --git a/src/forge/Issues/Pages/new.jsx b/src/forge/Issues/Pages/new.jsx new file mode 100644 index 000000000..b40307380 --- /dev/null +++ b/src/forge/Issues/Pages/new.jsx @@ -0,0 +1,306 @@ +import React , { useEffect , useState , forwardRef , useRef } from 'react'; +import { Form , InputNumber } from 'antd'; +import { Box , LongWidth } from '../../Component/layout'; +import DropMenu from '../Component/dropMenu'; +import ChooseMenu from '../Component/chooseMenu'; +import AddTagsBox from '../Component/addTagsBox'; +import Date from '../Component/date'; +import NewPanel from '../Component/newPanel'; +import axios from 'axios'; +import CaseDrop from '../Component/caseDrop'; + +function New(props){ + // history search 是否含有意见反馈标识 + const {location:{search}, projectDetail} = props; + const feedBack = search && search.indexOf('type=feedback') !== -1; + // 里程碑id + const milepostId = props.match.params.milepostId; + + + const [ statusList , setStatusList ] = useState([]); + const [ prioritiesList , setPrioritiesList ] = useState([]); + const [ chargeList , setChargeList ] = useState([]); + const [ charge , setCharge ] = useState(undefined); + const [ millstoneList , setMillstoneList ] = useState([]); + const [ millstone , setMillstone ] = useState(undefined); + const [ tagList , setTagList ] = useState(undefined); + const [ tag , setTag ] = useState(undefined); + const [ branchList , setBranchList ] = useState(undefined); + const [ branch , setBranch ] = useState(undefined); + + + const [ assigner_choose , setAssigner_choose ] = useState([]); + const [ prioritie_choose , setPrioritie_choose ] = useState([]); + const [ status_choose , setStatus_choose ] = useState([]); + const [ tag_choose , setTag_choose ] = useState([]); + const [ millstone_choose , setMillstone_choose ] = useState([]); + const [ branch_choose , setBranch_choose ] = useState([]); + const [ caseValue, setCaseValue ] = useState(undefined); + + const [ start_date, setStartDate ] = useState("");//moment().format('YYYY-MM-DD') + const [ due_date, setDueDate ] = useState(""); + + const [ rewardAmount , setRewardAmount ] = useState(undefined); + + const owner = props.match.params.owner; + const projectsId = props.match.params.projectsId; + const permission = props && props.projectDetail && props.projectDetail.permission; + const { open_blockchain } = props; + const ref = useRef(null); + + useEffect(()=>{ + if(projectDetail){ + if(feedBack){ + document.title = `意见反馈`; + return; + } + const { author, name} = projectDetail; + document.title = `新建疑修-${author.name}/${name}` + } + },[projectDetail, feedBack]) + + useEffect(()=>{ + if(milepostId){ + getMillstone(milepostId); + } + },[milepostId]) + + + // 获取状态列表 + useEffect(()=>{ + getStatus(); + },[]) + function getStatus(){ + const url = `/v1/${owner}/${projectsId}/issue_statues`; + axios.get(url).then(result=>{ + if(result && result.data){ + let status = result.data.statues; + setStatusList(status); + if(status && status.length>0){ + setStatus_choose(status.filter(i=>i.id === 1)); + } + } + }) + } + // 获取优先级列表 + useEffect(()=>{ + getPriorities(); + },[]) + + function getPriorities(){ + const url = `/v1/${owner}/${projectsId}/issue_priorities`; + axios.get(url).then(result=>{ + if(result && result.data){ + let priorities = result.data.priorities; + let array =[]; + let colors = ["#1abcb1","#28be6c","#e67e22","#db3d1d"]; + if(priorities && priorities.length>0){ + array = priorities.map((i,k)=>{ + let arr = {...i,color:colors[k]} + return arr; + }) + } + setPrioritiesList(array); + setPrioritie_choose(array.filter(i=>i.id === 2)); + } + }) + } + + // 获取负责人列表 + useEffect(()=>{ + getCharge(); + },[charge]) + + function getCharge(){ + const url = `/v1/${owner}/${projectsId}/collaborators`; + axios.get(url,{params:{keyword:charge,only_name:true}}).then(result=>{ + if(result && result.data){ + setChargeList(result.data.collaborators); + if(feedBack && !charge){ + // 指定指派成员-意见反馈 + const {collaborators} = result.data; + const id = collaborators.filter(item=>item.id === 86107); + setAssigner_choose(id.length === 0 ? [collaborators[0]] : id) + } + } + }) + } + // 获取里程碑列表 + useEffect(()=>{ + getMillstone(millstone); + },[millstone]) + + function getMillstone(keyword){ + const url = `/v1/${owner}/${projectsId}/milestones`; + axios.get(url,{params:{keyword,only_name:true}}).then(result=>{ + if(result && result.data){ + let milestones = result.data.milestones; + if(milepostId && milepostId === keyword){ + setMillstone_choose(milestones); + }else{ + setMillstoneList(milestones); + } + } + }) + } + // 获取标记列表 + useEffect(()=>{ + getSign(); + },[tag]) + function getSign(){ + const url = `/v1/${owner}/${projectsId}/issue_tags`; + axios.get(url,{params:{keyword:tag,only_name:true}}).then(result=>{ + if(result && result.data){ + setTagList(result.data.issue_tags); + } + }) + } + // 获取标记列表 + useEffect(()=>{ + getBranch(branch); + },[branch]) + function getBranch(branch){ + if(branch){ + let b = [...branchList]; + let l = b.filter(i=>i.name.indexOf(branch)>-1); + setBranchList(l); + return ; + } + const url = `/${owner}/${projectsId}/branches.json`; + axios.get(url,{params:{keyword:tag}}).then(result=>{ + if(result && result.data){ + setBranchList(result.data); + } + }) + } + + + // 创建 + function createFunc(values,fileList,receivers_login,description){ + const { subject } = values; + const url = `/v1/${owner}/${projectsId}/issues`; + let b = branch_choose && branch_choose.length>0 ? branch_choose.map(i=>{return i.name}) :undefined; + let t = tag_choose && tag_choose.length>0 ? tag_choose.map(i=>{return i.id}) :undefined; + let a = assigner_choose && assigner_choose.length>0 ? assigner_choose.map(i=>{return i.id}) :undefined; + let m = millstone_choose && millstone_choose.length>0 ? millstone_choose.map(i=>{return i.id}) :undefined; + let p = prioritie_choose && prioritie_choose.length>0 ? prioritie_choose.map(i=>{return i.id}) :undefined; + let s = status_choose && status_choose.length>0 ? status_choose.map(i=>{return i.id}) :undefined; + axios.post(url,{ + description,subject, + branch_name:b && b.join(","), + status_id:s && s.join(","), + priority_id:p && p.join(","), + milestone_id:m && m.join(","), + issue_tag_ids:t, + assigner_ids:a, + attachment_ids:fileList, + start_date,due_date,receivers_login, + blockchain_token_num:rewardAmount, + test_case:caseValue + }).then(result=>{ + if(result && result.data && result.data.project_issues_index){ + window.scrollTo(0,0); + props.showNotification("任务创建成功!"); + props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`); + } + }).catch(error=>{}) + } + + + function changeAmount(value){ + if(value && value > 0){ + setRewardAmount(value); + } + if(!value){ + setRewardAmount(value); + } + } + return( +
    +

    新建疑修

    + + + {/* 意见反馈初始化内容 */} + + +
    + {setCharge(value)}} + headImg + selectValueList={assigner_choose} + chooseFunc={(list)=>{setAssigner_choose(list);}} + double={5} + removeFlag + /> + {setStatus_choose(list);}} + /> + {setPrioritie_choose(list);}} + /> + {setTag(value)}} + selectValueList={tag_choose} + chooseFunc={(list)=>{setTag_choose(list);}} + double={3} + colorFlag="2" + removeFlag + onAdd={permission && permission !== "Reporter" ? ()=>{getSign();} :false} + owner={owner} + projectsId={projectsId} + /> + {setMillstone(value)}} + selectValueList={millstone_choose} + editFlag={milepostId ? true : false} + chooseFunc={(list)=>{setMillstone_choose(list);}} + /> + {setBranch(value)}} + selectValueList={branch_choose} + chooseFunc={(list)=>{setBranch_choose(list);}} + /> + { + open_blockchain && +
  • + + 悬赏金额 + {/* {!(milepostId ? true : false) && } */} + + +
  • + } + {console.log("case:",v);setCaseValue(v)}}/> + setStartDate(date)}/> + setDueDate(date)}/> +
    +
    +
    + ) +} +export default Form.create()(forwardRef(New)); \ No newline at end of file diff --git a/src/forge/Issues/Pages/sign.jsx b/src/forge/Issues/Pages/sign.jsx new file mode 100644 index 000000000..be3abaf0e --- /dev/null +++ b/src/forge/Issues/Pages/sign.jsx @@ -0,0 +1,267 @@ +import React , { useEffect , useState } from 'react'; +import create from '../Img/create.png'; +import { Menu , Dropdown , Icon, Input, Button , Spin , Pagination } from 'antd'; +import ColorCard from '../Component/colorCard'; +import DelBox from '../Component/delBox'; +import axios from 'axios'; +import {Link} from 'react-router-dom'; +const limit = 15; +function Sign(props){ + const [ edit , setEdit ] = useState(false); + const [ colors , setColors ] = useState(undefined); + const [ delVisible , setDelVisible ] = useState(false); + const [ editId , setEditId ] = useState(undefined); + const [ list , setList ] = useState(undefined); + const [ total , setTotal ] = useState(undefined); + const [ page , setPage ] = useState(1); + + const [ name , setName ] = useState(undefined); + const [ desc , setDesc ] = useState(undefined); + const [ order_name , setOrderName ] = useState(undefined); + const [ order_type , setOrderType ] = useState(undefined); + const [ nameFlag , setNameFlag ] = useState(undefined); + const [ defaultColor , setDefaultColor ]= useState(undefined); + + const [ arrayName , setArrayName ] = useState("标记"); + const [ key , setKey ] = useState("0"); + const owner = props.match.params.owner; + const projectsId = props.match.params.projectsId; + const { projectDetail } = props; + const permission = projectDetail && props.projectDetail.permission; + + + useEffect(()=>{ + if(permission !== undefined){ + if(!permission || (permission && permission === "Reporter")){ + window.location.href = "/403"; + } + } + },[permission]) + + useEffect(()=>{ + if(projectDetail){ + const { author, name } = projectDetail; + document.title = `项目标记-${author.name}/${name}`; + } + },[projectDetail]) + + useEffect(()=>{ + Init(); + },[page,order_name,order_type]) + + function Init(){ + const url = `/v1/${owner}/${projectsId}/issue_tags`; + axios.get(url,{ + params:{ + page,limit,sort_by:order_name,sort_direction:order_type + } + }).then(result=>{ + if(result && result.data){ + setList(result.data.issue_tags); + setTotal(result.data.total_count); + } + }) + } + + function arrayList(e){ + const { eventKey , value , children , createName } = e.item.props; + setPage(1); + if(value === order_type && createName === order_name){ + setOrderName(undefined); + setOrderType(undefined); + setArrayName("标记"); + setKey("0"); + }else{ + setOrderName(createName); + setOrderType(value); + setArrayName(children); + setKey(eventKey); + } + } + + const menu = ( + + + 按创建时间降序排序 + + + 按创建时间升序排序 + + + 按疑修数量降序排序 + + + 按疑修数量升序排序 + + + 按合并请求数量降序排序 + + + 按合并请求数量升序排序 + + + ); + + function getColor(colors){ + setColors(colors); + } + + function deleteFunc(){ + if(editId){ + const url = `/v1/${owner}/${projectsId}/issue_tags/${editId}`; + axios.delete(url).then(result=>{ + if(result){ + props.showNotification("项目标记删除成功!"); + if(list && list.length === 1 && page>1){ + setPage(page-1); + }else{ + Init(); + } + setDelVisible(false); + } + }).catch(error=>{}) + } + } + + function sureSave(){ + if(!name){ + setNameFlag(true); + }else{ + setNameFlag(false); + if(editId){ + // 编辑 + const url = `/v1/${owner}/${projectsId}/issue_tags/${editId}`; + axios.patch(url,{ + color: colors || defaultColor, + description: desc, + name + }).then(result=>{ + if(result){ + props.showNotification("标记编辑成功!"); + Init(); + cancel(); + } + }).catch(error=>{}) + }else{ + // 保存 + const url = `/v1/${owner}/${projectsId}/issue_tags`; + axios.post(url,{ + color: colors || defaultColor, + description: desc, + name + }).then(result=>{ + if(result){ + props.showNotification("标记新增成功!"); + Init(); + cancel(); + } + }).catch(error=>{}) + } + } + } + + function cancel(){ + setNameFlag(false); + setName(undefined); + setEdit(false); + setDesc(undefined); + } + + function editFunc(i){ + setEditId(i.id); + setDefaultColor(i.color); + setName(i.name); + setDesc(i.description); + setEdit(true); + } + + function onChangeValue(e){ + setName(e.target.value); + if(e.target.value){ + setNameFlag(false); + }else{ + setNameFlag(true); + } + } + + function createSign(){ + const c = Math.random().toString(16).substr(-6); + c && setDefaultColor(`#${c}`); + setEdit(true); + } + return( +
    + setDelVisible(false)} + onSuccess={deleteFunc} + title="删除标记" + content={

    您确定要删除当前标记?

    } + /> +
    + 疑修 / 项目标记 + 创建标记 +
    + { + edit && +
    +
    + + { nameFlag &&

    请输入标记名称

    } +
    + setDesc(e.target.value)} placeholder="描述30字以内"/> + + + + + +
    + } +
    + 项目标记({total || 0}) + + {arrayName} + +
    + { + list && list.length > 0 ? + + :"" + } + { + total > limit && +
    + setPage(p)} pageSize={limit}/> +
    + } + {total === undefined &&
    } +
    + ) +} +export default Sign; \ No newline at end of file diff --git a/src/forge/Issues/index.jsx b/src/forge/Issues/index.jsx new file mode 100644 index 000000000..a3bb5dfd4 --- /dev/null +++ b/src/forge/Issues/index.jsx @@ -0,0 +1,80 @@ +import React,{useEffect} from 'react'; +import { Route, Switch } from "react-router-dom"; +import Loadable from "react-loadable"; +import Loading from "../../Loading"; +import './index.scss'; + +const List = Loadable({ + loader: () => import("./Pages/list"), + loading: Loading, +}); +const Detail = Loadable({ + loader: () => import("./Pages/details"), + loading: Loading, +}); +const Sign = Loadable({ + loader: () => import("./Pages/sign"), + loading: Loading, +}); +const New = Loadable({ + loader: () => import("./Pages/new"), + loading: Loading, +}); + +function Index(props){ + const pathname = props.history.location.pathname; + const { project } = props; + const open_blockchain = project && project.open_blockchain; + + useEffect(() => { + if (document) { // 可以排除不需要置顶的页面 + if (document.documentElement || document.body) { + document.documentElement.scrollTop = document.body.scrollTop = 0; // 切换路由时手动置顶 + } + } + }, [pathname]); + return( +
    + + ( + + )} + > + {/* 里程碑创建issue */} + ( + + )} + > + ( + + )} + > + ( + + )} + > + ( + + )} + > + ( + + )} + > + +
    + ) +} +export default Index; \ No newline at end of file diff --git a/src/forge/Issues/index.scss b/src/forge/Issues/index.scss new file mode 100644 index 000000000..7a5541822 --- /dev/null +++ b/src/forge/Issues/index.scss @@ -0,0 +1,870 @@ +.borderNo.ant-input,.borderNo .ant-input-number-input,.borderNo,.borderNo.ant-input-number-focused{ + border:none!important; + padding:4px 0px!important; + background-color: transparent!important; + box-shadow: none!important; + .ant-input-number-handler-wrap{ + display: none; + } + &:focus{ + border:none!important; + box-shadow: none!important; + } +} + +.pagebox{ + width: 1200px; + margin:0px auto; +} +// 删除issue弹框 +.deldesc{ + justify-content: center; + .red{ + color: #DF0002; + } +} +// 可公用样式(在issue的页面中) +// 可点击(有向下箭头且点击出现下拉框,移入灰色背景加深)的灰色按钮样式 +.dorpdownButton{ + padding:0px 15px; + height:32px; + color: #333333!important; + cursor: pointer; + line-height: 32px; + background-color:#fafbfc; + border:1px solid #d0d0d0; + border-radius: 4px; + display: flex; + align-items: center; + min-width: 107px; + box-sizing: border-box; + &>span{ + display: block; + width: 56px; + text-align: center; + } + &:hover{ + background-color: #f3f4f6; + } +} +// flex-左右,上下居中 +.between{ + display: flex; + align-items: center; + justify-content: space-between; +} +// 蓝色背景操作栏 +.bluebar{ + height:50px; + background-color:#fafcff; + border:1px solid rgba(42, 97, 255, 0.23); + border-radius:4px 4px 0px 0px; + padding:0px 26px; +} +// 按钮样式 +.ant-btn-background-ghost{ + border-color:#acb0bf; + color: #acb0bf; + background-color: #fff!important; + &.ant-btn-primary{ + color: #466aff; + border-color: #466aff; + background-color: #fff!important; + } + &.ant-btn-danger{ + color: #f60011; + border-color: #f60011; + background-color:rgba(196, 0, 14, 0.09)!important; + } +} +.lists{ + margin-bottom: 50px; +} +.dataempty{ + height: 420px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid; + border-color: #d9d9d9; + border-radius: 0px 0px 4px 4px; + border-top: none; +} +// 列表为空样式 +.listempty{ + display: flex; + flex-direction: column; + align-items: center; + background-color:#fafcff; + border:1px solid rgba(42, 97, 255, 0.23); + border-top: none; + border-radius:0px 0px 4px 4px; + height: 344px; + padding-top: 62px; + color: #333; +} +// 列表页面样式 +.pageheader{ + display: flex; + justify-content: space-between; + padding:30px 0px; + align-items: center; + &>div{ + display: flex; + align-items: center; + } +} +.listheader{ + display: flex; + justify-content: space-between; + align-items: center; + height:50px; + background-color:#fafcff; + border:1px solid; + border-color:rgba(42, 97, 255, 0.23); + border-radius:4px 4px 0px 0px; + padding-left: 18px; + .menusul{ + display: flex; + align-items: center; + } + ul{ + display: flex; + align-items: center; + &.statusul{ + li{ + width: 106px; + font-size: 15px; + &>span{ + height:19px; + line-height: 17px; + color: #666; + background-color:rgba(70, 106, 255, 0.09); + border-radius:10px; + margin-left: 5px; + padding:0px 6px; + font-size: 13px; + display: block; + border:1px solid ; + border-color: rgba(70, 106, 255, 0.01); + min-width: 30px; + text-align: center; + } + } + } + li{ + color: #898d9d; + display: flex; + align-items: center; + cursor: pointer; + &.active{ + color: #466aff; + // font-weight: 500; + &>span{ + font-weight: normal; + color: #466aff; + border-color: #466aff; + background-color: #fff; + } + } + } + } + .dropboxul{ + padding-right: 20px; + li.minwidth{ + min-width: 70px; + } + li{ + min-width:88px; + display: flex; + justify-content: flex-end; + cursor: default; + margin-left: 12px; + .dropspan{ + display: flex; + align-items: center; + cursor: pointer; + &>span{ + max-width: 80px; + display: inline-block; + text-align: right; + } + } + } + } + +} +// issue列表 +.listdatas{ + border:1px solid #d0d0d0; + border-top: none; + border-radius: 0px 0px 2px 2px; + &>div:not(.none_panels){ + padding:20px 18px; + border-bottom: 1px solid #eee; + display: flex; + align-items: center; + justify-content: space-between; + &:hover{ + background-color:rgba(231, 233, 242, 0.26); + } + &:last-child{ + border-bottom: none; + } + .issuedetail{ + display: flex; + align-items: flex-start; + flex:1; + } + } + .idetails{ + display: flex; + align-items: center; + &>a{ + color: #40424a; + margin-right: 10px; + font-size: 15px; + display: block; + overflow: hidden; + text-overflow: ellipsis; + max-width: 375px; + white-space: nowrap; + } + .tagscolor{ + padding:0px 6px; + border-radius: 2px; + height: 20px; + line-height: 20px; + max-width: 108px; + font-size: 13px; + cursor: default; + color: #fff; + } + } + .infos{ + display: flex; + align-items: center; + color: #898d9d; + margin-top: 12px; + font-size: 13px; + // img{ + // height: 22px; + // width: 22px; + // margin-right: 4px; + // border-radius: 50%; + // } + } + .issuecondition{ + display: flex; + align-items: center; + color: #40424a; + .principal{ + position: relative; + display: flex; + justify-content: flex-end; + width: 150px; + text-align: justify; + // &.hovers:hover{ + // a{ + // position:initial; + // } + // } + // a{ + // position: absolute; + // right: 0px; + // transition: 0.6s; + // } + // img,span{ + // width: 22px; + // height: 22px; + // border-radius: 50%; + // } + // span{ + // background-color:#ced5ef; + // text-align: center; + // line-height: 12px; + // color:#000000; + // z-index: 6; + // } + } + &>div{ + width: 90px; + text-align: right; + } + .commentnum{ + padding-right: 5px; + display: flex; + align-items: center; + justify-content: flex-end; + } + } +} +// 状态样式-公用 + +.status{ + display: block; + height:22px; + border-radius:6px; + text-align: center; + margin-right: 10px; + line-height: 20px; + width: 45px; + font-size: 14px; + border:1px solid #fff; + &.normals{ + border-color:#28be6c; + color:#28be6c; + } + &.hight{ + border-color: #e67e22; + color: #e67e22; + } + &.urgent{ + border-color: #db3d1d; + color: #db3d1d; + } + &.low{ + border-color: #1abcb1; + color: #1abcb1; + } +} +.ilog{ + min-width: 45px; + margin-right: 10px; + clear: both; + .number{ + background-color:rgba(213, 220, 246, 0.36); + border-radius:4px; + height: 19px; + line-height: 19px; + color: #666666; + text-align: center; + float: left; + padding:0px 4px; + cursor: pointer; + font-size: 13px; + } +} +// colorCard +.color{ + width: 20px; + height: 20px; + border-radius: 2px; +} +.swatch{ + padding: 5px; + background: #fff; + border-radius: 1px; + width: 92px; + height: 28px; + line-height: 20px; + box-shadow: 0 0 0 1px rgba(0,0,0,.1); + display: flex; + cursor: pointer; +} +.popover { + position: absolute; + z-index: 2; +} +.cover { + position: fixed; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} +.modalcolor { + width: 20px; + height: 20px; + border-radius: 2px; +} +// 标记管理页面 +.editbar{ + padding:18px 20px; + background-color:rgba(218, 225, 251, 0.24); + display: flex; + align-items: center; + margin-bottom: 30px; + justify-content: space-between; + &>div{ + position: relative; + .inputred{ + border-color: red; + } + .red{ + position: absolute; + left: 166px; + width: 100%; + top: 2px; + color: red; + } + } +} +.signlist{ + padding:10px 0px; + margin-bottom: 40px!important; + &>li{ + display: flex; + margin-bottom: 5px; + padding:10px 20px; + &>p{ + flex:1; + color: 40424a; + &:first-child{ + flex:1.2; + display: flex; + align-items: center; + justify-content: flex-start; + } + &:last-child{ + display: flex; + align-items: center; + justify-content: flex-start; + justify-content: flex-end; + } + .square{ + display: block; + width: 22px; + height: 15px; + border-radius:2px; + } + .line{ + position: relative; + padding-left: 13px; + &::before{ + position: absolute; + height:13px; + width: 1px; + left: 0px; + top:7px; + background-color:#d5d5d5; + content: ""; + } + } + } + } +} +// 新建页面样式 +.shortwidth{ + width: 330px; + padding-left: 50px; + &>li{ + border-bottom: 1px solid rgba(172, 176, 191, 0.17); + padding-bottom: 8px; + margin-bottom: 22px!important; + &>span{ + font-size: 15px; + position: relative; + color: #40424a; + display: flex; + width: 100%; + height: 20px; + line-height: 20px; + font-weight: 600; + a{ + position: absolute; + right: 0px; + } + } + .operatevalue{ + line-height: 30px; + color: #acb0bf; + margin-top: 10px; + display: flex; + flex-wrap: wrap; + .colorsborder{ + padding: 0px 8px; + height: 20px; + line-height: 18px; + border-radius: 4px; + text-align: center; + border:1px solid ; + font-size: 13px; + } + .colorsquare{ + padding: 0px 8px; + height: 20px; + line-height: 20px; + border-radius: 4px; + text-align: center; + color: #fff; + font-size: 13px; + } + &>p{ + margin-top: 5px!important; + position: relative; + padding-right: 15px; + &.removeFlag>span{ + max-width: 230px; + } + .removeicon{ + display: none; + } + } + .removeFlag:hover{ + border-radius:2px; + .removeicon{ + display: block; + } + } + &>p:first-child{ + margin-top: 0px; + } + } + } +} +.addTags_form{ + .ant-row.ant-form-item{ + margin-bottom: 12px; + } + .manageTitle{ + margin-bottom: 20px!important; + padding-bottom: 15px; + line-height: 22px; + color:#5f6872; + font-size:16px; + border-bottom:1px solid #e1e3e8; + } + .inline{ + display: flex; + align-items: flex-start; + } +} +.addTagsModal{ + .ant-modal-content{ + background-image:linear-gradient(359.37deg,#ebf3ff 0%,#f8fbff 55.01%,#f1f5ff 100%); + } + .ant-modal-header{ + background-color: unset; + padding:0px 24px; + height: 50px; + line-height: 50px; + .ant-modal-title{ + text-align: left; + color:#333333; + font-size:17px; + font-weight: normal!important; + height: 50px; + line-height: 50px!important; + } + } + .ant-modal-close{ + top:0px !important; + } + .popover{ + left: 94px; + top:0px; + } +} +.menusEmpty{ + padding:12px 20px 10px 16px; + &>p{ + padding:10px 16px; + } + &>a{ + padding:10px 16px; + display: block; + border-top: 1px solid rgba(172, 176, 191, 0.17); + } +} +.piecemenu{ + li{ + display: flex; + align-items: center; + } +} +.colorpiece{ + display: block; + width: 22px!important; + height: 15px; + margin-right: 9px; + border-radius:2px; +} +.overlayStyle{ + background-color:#ffffff; + border-radius:6px; + box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17); + max-width: 280px; + z-index: 100; + .searchbox{ + padding:12px 20px 0px 16px; + } + .ant-menu{ + padding:10px 16px 10px 16px; + max-height: 324px; + overflow-y: auto; + li{ + padding:0px 16px; + height: 38px; + line-height: 38px; + border-bottom: none; + color:#898d9d; + position: relative; + width: 100%; + box-sizing: border-box; + &:hover,&.ant-menu-item-selected{ + background-color:rgba(70, 106, 255, 0.07); + color:#40424a; + } + &.ant-menu-item-selected::before{ + content:"✓"; + position: absolute; + right: 15px; + color: #acb0bf; + top: 0px; + } + } + } +} +//详情页面 +.editpanel{ + padding:20px; + margin-top: 25px; + border:1px solid rgba(42, 97, 255, 0.23); + border-radius: 2px; +} +.detailbanner{ + // margin-top: 25px; + // height:82px; + // background-color:#fafcff; + // border:1px solid rgba(42, 97, 255, 0.23); + // border-radius:4px; + // padding:12px 25px 12px 20px; + display: flex; + align-items: center; + justify-content: space-between; + .detailtitle{ + flex: 1; + &>div{ + display: flex; + align-items: flex-start; + } + .name{ + font-size: 16px; + font-weight: 700; + color:#333; + line-height: 22px; + max-width: 580px; + word-break: break-all; + } + .author{ + img{ + width: 26px; + height: 26px; + margin-right: 4px; + border-radius: 50%; + } + color: #466aff; + } + } + .detailoperate{ + display: flex; + align-items: center; + } +} +.descPanel{ + margin-top:30px; + // padding:20px; + // background-color: #f7f8fd; + // border:1px solid #c9d8ff; + // border-radius: 2px; +} +// 新建页面 +.explain{ + .ant-form-explain{ + position: absolute; + } +} + + +.overlaydrop{ + &.large{ + width: 260px; + } + &.small{ + width: 120px; + padding:12px 0px!important; + } + padding:18px 15px; + position: relative; + border-radius:6px; + box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17); + background-color: #fff; + .ant-menu{ + max-height: 305px; + overflow-y: auto; + li{ + height: 38px; + line-height: 38px; + border-bottom: none!important; + display: flex; + align-items: center; + padding-left: 6px; + padding-right: 0px; + img{ + width: 24px; + height: 24px; + border-radius: 50%; + } + span{ + display: block; + width:100%; + margin-left: 10px; + padding-right: 25px; + } + &:hover,&.ant-menu-item-selected{ + background-color:rgba(70, 106, 255, 0.07); + color:#40424a; + } + &.ant-menu-item-selected::before{ + content:"✓"; + position: absolute; + right: 15px; + color: #acb0bf; + top: 0px; + } + } + } +} + +// 声明 +.claimpart{ + padding-bottom: 15px; + border-bottom: 1px solid #eee; + margin-bottom: 20px; +} + +// issue版本2 +.overlayChooseStyle{ + width: 636px; + background-color:#ffffff; + border-radius:6px; + box-shadow:0px 0px 10px rgba(24, 54, 181, 0.17); + z-index: 100; + padding:30px 20px; + .choosedul{ + display: flex; + flex-wrap: wrap; + padding-bottom: 10px; + border-bottom: 1px solid #e4e4e6; + margin-bottom: 20px!important; + li{ + height:24px; + background-color:#eff2ff; + border:1px solid; + border-color:#466aff; + border-radius:2px; + color:#466aff; + font-size:14px; + margin-right: 15px; + margin-bottom: 10px!important; + padding:0px 7px; + display: flex; + align-items: center; + span{ + display: block; + max-width:120px ; + } + } + } + .counttips{ + display: flex; + justify-content: space-between; + margin-bottom: 20px; + } + .ant-menu{ + display: flex; + flex-wrap: wrap; + padding:20px 0px 5px 0px; + box-sizing: border-box; + max-height: 307px; + overflow-y: auto; + &.piecemenu li{ + width: auto; + &>span{ + display: flex; + } + span.task-hide{ + max-width: unset; + } + &:nth-child(5n){ + margin-right: 24px!important; + } + } + li{ + width: 98px; + height:32px; + line-height:30px; + text-align: center; + font-size:14px; + border-radius:2px; + margin-right: 24px!important; + margin-bottom: 15px!important; + border:1px solid!important; + border-color: #f4f6fe!important; + padding:0px 5px; + &.commonli{ + background-color:#f4f6fe!important; + } + .removeicon{ + display: block; + } + span.task-hide{ + color: #3f455b; + margin:0px auto; + position: relative; + color:#3f455b; + max-width: 80px; + display: inline-block; + } + span{ + position: relative; + margin:0px auto; + } + &.colorli{ + border-color: transparent!important; + span{ + color: #fff; + position: relative; + margin:0px auto; + } + } + &.commonli.ant-menu-item-selected{ + background-color:#e2e8ff!important; + color:#3f5097; + border-color:#466aff!important; + } + &.colorli.ant-menu-item-selected{ + border-color: transparent!important; + } + &.colorli.ant-menu-item-selected{ + &>span::before{ + content:"✓"; + position: absolute; + right: -12px; + color: #fff; + top: -16px; + } + } + &:nth-child(5n){ + margin-right: 0px!important; + } + } + } +} + +.boxStyles{ + margin-top: 10px!important; + width: 100%; + input,input[class~="ant-input"]:focus{ + border:none!important; + padding:0px; + } + div[class~='ant-select-selection__placeholder']{ + margin-left: 0px!important; + margin-right: 0px!important; + } + span[class~='ant-select-arrow']{ + opacity: 0; + } +} \ No newline at end of file diff --git a/src/forge/Main/Detail.js b/src/forge/Main/Detail.js index 213d891c8..e52be3a40 100644 --- a/src/forge/Main/Detail.js +++ b/src/forge/Main/Detail.js @@ -32,7 +32,7 @@ const OrderDetail = Loadable({ loading: Loading, }) const OrderIndex = Loadable({ - loader: () => import('../Order/order'), + loader: () => import('../Issues/index'), loading: Loading, }) const CoderRootIndex = Loadable({ @@ -93,7 +93,7 @@ const UpdateMerge = Loadable({ }) const MilepostDetail = Loadable({ - loader: () => import('../Order/MilepostDetail'), + loader: () => import('../Order/MilepostDetail.jsx'), loading: Loading, }) const WatchUsers = Loadable({ @@ -711,11 +711,11 @@ class Detail extends Component { } > {/* 标签列表 */} - () } - > + > */} {/* 仓库设置 */} {/*里程碑详情*/} - () } @@ -748,36 +748,36 @@ class Detail extends Component { } > {/* 里程碑页面新建任务 */} - () } - > + > */} {/* 新建任务 */} - () } - > + > */} {/* 修改详情 edit*/} - () } - > + > */} {/* 复制详情 copyetail*/} - () } - > + > */} {/* 任务详情 */} - () } - > + > */} {/* 动态 */} { - getList(); - },[]) + // getList(); + const url = `/v1/${owner}/${projectsId}/branches.json`; + Axios.get(url,{ + params:{ + keyword: name, + page: current, + limit: pageSize + } + }).then(result=>{ + if(result){ + const {branches, total_count} = result.data; + setTotal(total_count) + setList(branches); + setNewFormBranchName(branches[0].name); + } + setIsSpin(false); + }).catch(error=>{setIsSpin(false);}) + },[name, reload, pageSize, current]) + + useEffect(()=>{ + if(projectDetail && document.title.indexOf('所有分支-') === -1){ + const { author, name} = projectDetail; + document.title = `所有分支-${author.name}/${name}`; + } + }, [projectDetail]) const menu =(zip_url,tar_url)=> ( @@ -29,6 +66,105 @@ function Index(props) { ) + const columns = [ + { + title: "分支名称", + dataIndex: "name", + key: 1, + ellipsis: true, + className: "branchNameColumn", + render: (txt, item) => { + return ( + {window.scrollTo(0,0)}} className="task-hide">{txt} + ) + } + }, + { + title: "更新信息", + dataIndex: "commit", + key: 3, + ellipsis: true, + width: "240px", + render: (txt, item) => { + return ( +
    + { + txt && txt.committer && txt.committer.id? + + + {txt && txt.committer && txt.committer.name} + + : + + + {txt && txt.committer && txt.committer.name} + + } + 更新于{txt && txt.time_ago} +
    + ) + } + }, + { + title: "commit信息", + dataIndex: "commit_id", + key: 4, + ellipsis: true, + width: "250px", + render: (txt, item) => { + return
    +
    + + sha + {truncateCommitId(txt)} + + + +
    +
    + } + }, + { + title: "分支类型", + dataIndex: "default_branch", + key: 5, + width: "150px", + render: (txt, item) => { + return isManager ? txt === item.name ? 默认分支 : : txt === item.name ? '默认分支' : '普通分支' + } + }, + { + title: "操作", + dataIndex: "name", + key: 6, + align: "center", + width: "320px", + className: "branchActionColumn", + render: (txt, item) => { + return ( +
    + { + (isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && + + 合并请求 + } + + 下载 + + {isManager && (txt === item.default_branch ? : )} +
    + ) + } + } + ]; + function getList() { const url = `/${owner}/${projectsId}/branches_slice.json`; Axios.get(url).then(result=>{ @@ -39,10 +175,121 @@ function Index(props) { }).catch(error=>{setIsSpin(false);}) } + // 删除分支确认函数 + function deleteLabel(){ + Axios.delete(`/v1/${owner}/${projectsId}/branches/${visible.name}.json`).then(res=>{ + if(res && res.data){ + if(current>1 && (list && list.length===1)){ + setCurrent(current-1); + }else{ + setReload(Math.random()); + } + setVisible(false); + !res.data.status && message.success('删除成功'); + } + }) + } + + // 新建分支确认函数 + function createBranch(){ + setNewBranchError(undefined); + if(!newBranchName){ + setNewBranchError("请输入新建分支名称"); + }else{ + Axios.post(`/v1/${owner}/${projectsId}/branches.json`, { + new_branch_name: newBranchName, + old_branch_name: newFormBranchName + }).then(res=>{ + if(res && res.data){ + // 重新请求仓库详情接口,更新分支总数信息 + getDetail(); + setReload(Math.random()); + setAddBranch(false); + message.success('新建分支成功'); + } + }) + } + } + + // 新建分支名检查格式 + function checkNewBranchName(e) { + setNewBranchError(undefined); + const value = e.target.value; + if (value) { + const str = '^*'; + let reg = /\\/g; + if(reg.test(value)){ + setNewBranchError('分支名不能包含下列任何字符:^ * \\ '); + }else{ + for(var i=0;i -1){ + setNewBranchError('分支名不能包含下列任何字符:^ * \\ '); + return; + }else{ + setNewBranchName(e.target.value); + } + } + } + } + } + + // 更新默认仓库分支 + function updateDefaultBranch(value, info){ + Axios.patch(`/v1/${owner}/${projectsId}/branches/update_default_branch.json`,{ + name: info.name + }).then(res=>{ + if(res && res.data){ + getDetail(); + setReload(Math.random()); + !res.data.status && message.success('操作成功'); + } + }) + } + + // 改变pagesize + function onShowSizeChange(current, pageSize){ + window.scrollTo(0, 0); + setCurrent(1); + setPageSize(pageSize); + } + + // 切换页数 + function changePage(page, pageSize){ + window.scrollTo(0, 0); + setCurrent(page); + } + + // 新建分支按钮点击函数 + function addBranchBtn(){ + setAddBranch(true); + const url = `/v1/${owner}/${projectsId}/branches/all.json`; + Axios.get(url).then(result=>{ + if(result){ + setBranchList(result.data); + } + }).catch(error=>{console.log('error', error);}) + } + return(
    - { +
    + {setCurrent(1);setName(value)}} allowClear/> +
    + {isManager && 分支设置} + {(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && } +
    +
    + +
    + {/* { list && list.length>0 && list.map((item,key)=>{ return( @@ -86,7 +333,12 @@ function Index(props) {
    { (isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && - + 合并请求 + + 合并请求 } 下载 @@ -101,11 +353,47 @@ function Index(props) { ) }) - } - { - list && list.length === 0 && - } + } */}
    + {setVisible(false)}} + visible={Boolean(visible)} + width="456px" + btn={ +
    + + +
    + } + > +
    + +

    确定删除{visible.name}分支?

    +
    +
    + {setAddBranch(false)}} + visible={addBranch} + width="388px" + footer={ + + } + className="createNewBranchModal" + > +
    +
    分支名称
    + +
    {newBranchError}
    +
    分支起点
    + +
    +
    ) } diff --git a/src/forge/Main/tree/Index.scss b/src/forge/Main/tree/Index.scss index f65a1c68f..1c31ea8f1 100644 --- a/src/forge/Main/tree/Index.scss +++ b/src/forge/Main/tree/Index.scss @@ -1,3 +1,98 @@ +.treeinfo{ + .branchLastUpdateUserImage{ + height: 20px; + width: 20px; + margin-right: 5px; + } +} +.f6_bor_but, .f6_bor_but:focus{ + color:#f60011; + background-color:rgba(196, 0, 14, 0.09); + border:1px solid #f60011; + border-radius:5px; + &:hover{ + color:#f60011; + background-color:rgba(196, 0, 14, 0.18); + border-color:#ff727c; + } + &:active{ + color:#f60011; + background-color:rgba(196, 0, 14, 0.22); + border-color:#f60011; + } +} +.branchListTable{ + .ant-table-thead > tr > th{ + border-top: 1px solid rgba(42, 97, 255, 0.23); + border-bottom: 1px solid rgba(42, 97, 255, 0.23) !important; + background-color:#fafcff; + } + .ant-table-column-title{ + color:#40424a; + } + .ant-table-thead > tr:first-child > th:last-child{ + border-right: 1px solid rgba(42, 97, 255, 0.23); + } + .ant-table-thead > tr:first-child > th:first-child{ + border-left: 1px solid rgba(42, 97, 255, 0.23); + } + .ant-table-tbody > tr > td{ + border-color: #eeeeee; + padding: 18.5px 16px; + } + .branchNameColumn{ + border-left: 1px solid #d0d0d0; + } + .branchActionColumn{ + border-right: 1px solid #d0d0d0; + } + .task-hide{ + color: $primary-color; + } + .ant-table-empty .ant-table-placeholder{ + border-left: 1px solid #d0d0d0; + border-right: 1px solid #d0d0d0; + } +} +.branchListActionBox{ + display: flex; + justify-content: space-between; + align-items: center; + margin: 20px 0 30px; +} +.createNewBranchModal{ + .ant-modal-title{ + text-align: left; + color:#333333; + font-size:17px; + font-weight: normal !important; + } + .ant-modal-header{ + border-bottom: none; + background-color:rgba(70, 106, 255, 0.05); + } + .ant-modal-close{right: 5px;} + .ant-modal-close-x{font-size: 26px;} + .ant-modal-footer{ + border-top: none; + text-align: center; + padding-bottom: 53px; + } + .ant-modal-content{ + background-image:linear-gradient(359.37deg,#ebf3ff 0%,#f8fbff 55.01%,#f1f5ff 100%); + } + .createBranchLabel{ + color:#27282d; + margin-bottom: 12px; + } + .createBranchInput, .createBranchInput .ant-select-selection{ + border-color:#9eaacb; + background-color: transparent !important; + } + .newBranchError{ + color:#df0002; + } +} .branchSort{ font-weight: 500; color: #333333; @@ -49,14 +144,16 @@ } .treecopy{ flex:1; - display: flex; - justify-content: center; + display: flex; + // justify-content: center; + &>div:not(.treeCopyBox){ + position: relative; + } &>div{ height: 32px; background: #FAFBFC; border-radius: 4px; border: 1px solid #D0D0D0; - position: relative; z-index: 1; display: flex; align-items: center; @@ -64,6 +161,7 @@ padding:0px 15px; border-right: 1px solid rgba(153, 153, 153, 0.4); height: 100%; + width: 140px; img{ margin-right: 4px; } diff --git a/src/forge/Order/AmplifyImg.js b/src/forge/Order/AmplifyImg.js index 568ac7f92..114fa7e6a 100644 --- a/src/forge/Order/AmplifyImg.js +++ b/src/forge/Order/AmplifyImg.js @@ -1,7 +1,7 @@ import React from 'react'; import { Modal } from 'antd'; -import './order.css'; +import './order.scss'; function AmplifyImg(props) { return ( diff --git a/src/forge/Order/Detail.js b/src/forge/Order/Detail.js index 4e6c92a29..e76c24451 100644 --- a/src/forge/Order/Detail.js +++ b/src/forge/Order/Detail.js @@ -1,4 +1,4 @@ -import React, { Component, useRef } from "react"; +import React, { Component } from "react"; import { Link } from "react-router-dom"; import axios from "axios"; @@ -8,7 +8,7 @@ import Attachments from "../Upload/attachment"; import RenderHtml from "../../components/render-html"; import Comments from "../comments/comments"; import Claims from "../claims/claims" -import "./order.css"; +import "./order.scss"; class Detail extends Component { constructor(props) { @@ -44,6 +44,20 @@ class Detail extends Component { this.getDetail(); }; + componentDidUpdate=()=>{ + this.updateDocumentTitle(); + } + + // 更新网页标题 + updateDocumentTitle(){ + const {projectDetail} = this.props; + const {data} = this.state; + if(projectDetail && data){ + const { author, name} = projectDetail; + document.title = `${data.subject}-疑修-${author.name}/${name}`; + } + } + getDetail = () => { const { projectsId, orderId, owner } = this.props.match.params; const url = `/${owner}/${projectsId}/issues/${orderId}.json`; @@ -55,11 +69,6 @@ class Detail extends Component { data: result.data, isSpins: false }); - const {projectDetail} = this.props; - if(projectDetail){ - const { author, name} = projectDetail; - document.title = `${result.data && result.data.subject}-疑修-${author.name}/${name}`; - } } }) .catch((error) => { diff --git a/src/forge/Order/Milepost.js b/src/forge/Order/Milepost.js index a6f0c3cb6..e228dd452 100644 --- a/src/forge/Order/Milepost.js +++ b/src/forge/Order/Milepost.js @@ -1,54 +1,49 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; -import { Dropdown, Icon, Menu, Pagination, Typography, Popconfirm, Spin } from 'antd'; -import NoneData from '../Nodata'; +import { Dropdown, Icon, Menu, Pagination, Popconfirm, Spin, Button, Tooltip } from 'antd'; +import emp from '../../forge/Issues/Img/emp.png'; import axios from 'axios'; -import './order.css'; -import CheckProfile from '../Component/ProfileModal/Profile'; - -const { Text } = Typography; - +import './milepost.scss'; class Milepost extends Component { constructor(props) { super(props); this.state = { data: undefined, - limit: 15, + limit: 10, page: 1, order_type: undefined, - //新建标签区域是否显示 none 隐藏 block 显示 - display: 'none', + // 里程碑 开启/关闭 状态 status: 'open', - openselect: 1, - closeselect: undefined, order_name: undefined, - - spinings: true + spinings: true, + // tooltip显隐 + visible: false } } + componentDidUpdate(){ + this.updateDocumentTitle(); + } componentDidMount = () => { - this.getList(1, this.state.status, 'desc'); - } - componentDidUpdate = () => { + this.getList(1, this.state.limit, this.state.status); this.updateDocumentTitle(); } + // 更新网页标题 updateDocumentTitle(){ const {projectDetail} = this.props; if(projectDetail){ const { author, name} = projectDetail; - document.title = `新建合并请求-${author.name}/${name}`; + document.title = `里程碑-${author.name}/${name}`; } } - getList = (page, status, order_type, order_name) => { + getList = (page, limit, category, sort_by, sort_direction) => { const { projectsId ,owner } = this.props.match.params; - const { limit } = this.state; - const url = `/${owner}/${projectsId}/milestones.json`; + const url = `/v1/${owner}/${projectsId}/milestones.json`; axios.get(url, { params: { - projectsId, page, limit, status, order_type, order_name + page, limit: limit, category, sort_by, sort_direction } }).then((result) => { if (result) { @@ -63,45 +58,24 @@ class Milepost extends Component { } opneMilelist = (type) => { - const { order_name } = this.state; if (type) { - const { current_user } = this.props; - if (type === 1) { - this.setState({ - status: 'open', - openselect: current_user.user_id, - closeselect: undefined, - - }) - this.getList(1, 'open', 'desc', order_name); - } else { - this.setState({ - status: 'closed', - openselect: undefined, - closeselect: current_user.user_id - }) - this.getList(1, 'closed', 'desc', order_name); - } + this.setState({ + status: type === 1 ? 'open' : 'closed' + }) + this.getList(1, this.state.limit, type === 1 ? 'open' : 'closed', this.state.order_name, this.state.order_type); } - } updatestatusemile = (status, arr) => { const { projectsId , owner } = this.props.match.params; const url = `/${owner}/${projectsId}/milestones/${arr.id}/update_status.json`; - const { current_user } = this.props; axios.post(url, { project_id: projectsId, id: arr.id, status: status }).then(result => { if (result) { - this.setState({ - status: status, - closeselect: status === "closed" ? current_user.user_id : undefined, - openselect: status === "closed" ? undefined : current_user.user_id - }) - this.getList(1, status, 'desc'); + this.getList(1, this.state.limit, this.state.status, this.state.order_name, this.state.order_type); const { getDetail } = this.props; getDetail && getDetail(); } @@ -120,7 +94,7 @@ class Milepost extends Component { } }).then((result) => { if (result) { - this.getList(1, this.state.status, 'desc'); + this.getList(1, this.state.limit, this.state.status, this.state.order_name, this.state.order_type); const { getDetail } = this.props; getDetail && getDetail(); } @@ -134,39 +108,44 @@ class Milepost extends Component { this.setState({ page }) - const { status } = this.state; - this.getList( page , status ); + this.getList(page, this.state.limit, this.state.status, this.state.order_name, this.state.order_type); } // 排序 arrayList = (e) => { + const name = e.key !== 'order' ? e.key : undefined; + const type = e.item.props.value !== 'order' ? e.item.props.value : undefined; this.setState({ - order_name: e.key, - order_type: e.item.props.value + order_name: name, + order_type: type }) - this.getList(1, this.state.status, e.item.props.value, e.key); + this.getList(1, this.state.limit, this.state.status, name, type); } - - //控制新建标签页是否显示 - newshow = () => { + // 分页组件 + onShowSizeChange = (current, pageSize) =>{ this.setState({ - display: 'block' - }); - - }; - newclose = () => { - this.setState({ - display: 'none' - }); - }; + page: 1, + limit: pageSize + }) + this.getList(1, pageSize, this.state.status, this.state.order_name, this.state.order_type); + } + // tooltip判断是否显示 + // onVisibleChange = (visible, value) => { + // console.log('visible, value', visible, value); + // this.setState({ + // visible: value.length >24 ? visible : false + // }) + // } render() { - const { data, limit, page, openselect, closeselect, spinings } = this.state; + const { data, limit, page, spinings, status, order_name, order_type, visible } = this.state; const { projectsId , owner } = this.props.match.params; + const { isManager, isDeveloper} = this.props; const menu = ( - + + 默认排序 到期日从后到先 到期日从先到后 完成度从低到高 @@ -176,103 +155,90 @@ class Milepost extends Component { ) + // 排序字段 选中显示文本 + const sortByMile = { + 'effective_date':{ + 'desc': '到期日从后到先', + 'asc': '到期日从先到后' + }, + 'percent':{ + 'desc': '完成度从低到高', + 'asc': '完成度从高到低' + }, + 'issues_count':{ + 'desc': '任务从多到少', + 'asc': '任务从少到多' + }, + 'order':{ + 'order': '排序' + } + } return ( -
    -
    -
    - 里程碑{data && data.issue_tags_count}已创建 +
    + {/* 创建里程碑按钮 */} + {/*
    + {(isManager || isDeveloper) && } +
    */} + {/* 里程碑列表表头 */} +
    +
    + this.opneMilelist(1)}>开启中{data && data.opening_milestone_count} + this.opneMilelist(2)}>已关闭{data && data.closed_milestone_count} +
    +
    + {(isManager || isDeveloper) && } + + {order_name ? sortByMile[order_name][order_type] : '排序'} +
    -
    -
    -
  • this.opneMilelist(1)}>{data && data.open_count}个开启中
  • -
  • this.opneMilelist(2)}>{data && data.closed_count}个已关闭
  • -
    -
    -
      -
    • - - 排序 - -
    • -
    - { - data && data.user_admin_or_member ? - {this.props.history.push(`/${owner}/${projectsId}/milestones/new`)}}>新的里程碑 - : '' - } -
    -
    - { - data && data.versions && data.versions.length > 0 - && -
    - { - data.versions.map((item, key) => { - return ( -
    -
    -
    -
    - - {item.name} -
    -
    -
    -
    -
    -
    - - {item.effective_date || "暂无截止时间"} -
    -
    - - {item.open_issues_count}个开启 -
    -
    - - {item.close_issues_count}个关闭 -
    -
    - { - data && data.user_admin_or_member ? - - : '' - } -
    -
    -
    - {item.description} -
    -
    + {/* 里程碑列表展示 */} + {data && data.milestones && data.milestones.length === 0 &&
    暂无数据~
    } + {data && data.milestones && data.milestones.length > 0 &&
    + {data.milestones.map((item, key)=>{ + return
    +
    +
    + + {item.name} +
    + {item.description && item.description.length > 44 ? {item.description} : {item.description}} +
    +
    +
    + + 0 ? 'outTime' : ''}`}>{item.effective_date || "暂无截止时间"} +
    +
    + {item.opened_issues_count || 0}个开启 + | + {item.close_issues_count || 0}个关闭 +
    + { + (isManager || isDeveloper) && + } +
    - } - { data && data.versions && data.versions.length === 0 && } + })} +
    } { - data && data.versions_count > limit ? -
    - + data && data.total_count > limit ? +
    +
    : "" }
    diff --git a/src/forge/Order/MilepostDetail.js b/src/forge/Order/MilepostDetail.js index cfed74efc..4829485ca 100644 --- a/src/forge/Order/MilepostDetail.js +++ b/src/forge/Order/MilepostDetail.js @@ -110,7 +110,7 @@ class MilepostDetail extends Component { getIssueList = ( page , item , value , update , updateValue , type ) => { const { projectsId, meilid , owner } = this.props.match.params; const { limit , order_name , order_type , issue_tag_id , author_id , assigned_to_id , tracker_id , status_id , done_ratio , status_type } = this.state; - + console.log(this.props); const url = `/${owner}/${projectsId}/milestones/${meilid}.json`; let params = update ? { page, limit , order_name:value , order_type:updateValue , issue_tag_id , diff --git a/src/forge/Order/MilepostDetail.jsx b/src/forge/Order/MilepostDetail.jsx new file mode 100644 index 000000000..ba1fc49bc --- /dev/null +++ b/src/forge/Order/MilepostDetail.jsx @@ -0,0 +1,264 @@ +import React , { useRef , useState , useEffect } from 'react'; +import { Menu, Input , Checkbox , Pagination, Button , Tooltip ,Spin } from 'antd'; +import { Link } from "react-router-dom"; +import AllMenus from './component/allMenus'; +import Datas from '../Issues/Component/datas'; +import axios from 'axios'; +import { FlexAJ } from '../Component/layout'; +import CheckProfile from '../Component/ProfileModal/Profile'; +import millestones from '../../forge/Issues/Img/millestones-big.png'; +import './milepost.scss'; +import './order.scss'; +import '../Issues/index.scss'; + +function MilepostDetail(props){ + const [ milepost, setMilepost] = useState(undefined); + const [ issueList , setIssueList ] = useState(undefined); + const [ closedCount , setClosedCount ] = useState(0); + const [ openedCount , setOpenedCount ] = useState(0); + + + const [ category , setCategory] = useState("opened"); + + const [ allValue , setAllValue ] = useState([]); + const [ allIds , setAllIds ] = useState([]); + const [ checkAll , setCheckAll ] = useState(false); + + const [ updateIds , setUpdateIds ] = useState([]); + const [ limit, setLimit] = useState(10); + const [ page , setPage ] = useState(1); + const [ total , setTotal ] = useState(undefined); + const [ issuesCount, setIssueCount] = useState(undefined); + + const [ chooseValue, setChooseValue ] = useState(undefined); + + const menuRef = useRef(null); + const { projectsId, mileId , owner } = props.match.params; + const {current_user, projectDetail, showLoginDialog, history} = props; + const permission = props && props.projectDetail && props.projectDetail.permission; + // 排序字段 + const sortBy = { + '1': {'sort_by': 'issues.created_on', 'sort_direction': 'desc'}, + '2': {'sort_by': 'issues.created_on', 'sort_direction': 'asc'}, + '3': {'sort_by': 'issues.updated_on', 'sort_direction': 'desc'}, + '4': {'sort_by': 'issues.updated_on', 'sort_direction': 'asc'}, + '5': {'sort_by': 'issue_priorities.position', 'sort_direction': 'desc'}, + '6': {'sort_by': 'issue_priorities.position', 'sort_direction': 'asc'} + } + + useEffect(()=>{ + Init(chooseValue); + },[category, page, limit]) + + useEffect(()=>{ + updateDocumentTitle() + }, [projectDetail, milepost]) + + // 更新网页标题 + function updateDocumentTitle(){ + if(projectDetail && milepost){ + const { author, name} = projectDetail; + document.title = `${milepost.name}-里程碑-${author.name}/${name}`; + } + } + + // 获取issue列表数据 + function Init(params){ + const url = `/v1/${owner}/${projectsId}/milestones/${mileId}.json`; + axios.get(url,{ + params:{ + page, + limit, + category, + ...params + } + }).then(result=>{ + if(result){ + const {milestone, issues, total_count, closed_issues_count, opened_issues_count, total_issues_count} = result.data; + if(issues.length === 0 && page>1){ + setPage(page-1); + return; + } + setMilepost(milestone); + setIssueList(issues); + setTotal(total_count); + setClosedCount(closed_issues_count); + setOpenedCount(opened_issues_count); + setIssueCount(total_issues_count) + const ids = issues.length>0 ? issues.map(i=>{return i.id}) : []; + setAllIds(ids); + } + }).then(error=>{console.log("error:",error)}) + } + + + // 全选所有issue + function chooseAll(e){ + setCheckAll(e.target.checked); + if(e.target.checked){ + setAllValue(allIds); + }else{ + setAllValue([]); + } + } + + // 选择列表里的issue + function checkIssues(value){ + setAllValue(value); + if(value.length === allIds.length){ + setCheckAll(true); + }else{ + setCheckAll(false); + } + } + + // 取消 + function cancelUpdate(){ + setAllValue([]); + setCheckAll(false); + // 清除下拉选项 + menuRef.current && menuRef.current.clearChoose(); + } + + // 确认修改 allValue updateIds + function sureUpdate(){ + const url = `/v1/${owner}/${projectsId}/issues/batch_update`; + axios.patch(url,{ + assigner_ids: updateIds && updateIds.assigner_id && updateIds.assigner_id.split(","), + ids: allValue, + issue_tag_ids: updateIds && updateIds.issue_tag_ids && updateIds.issue_tag_ids.split(","), + milestone_id: updateIds && updateIds.milestone_id, + }).then(result=>{ + if(result){ + setUpdateIds(undefined); + setAllValue([]); + setTotal(undefined); + Init(); + cancelUpdate(); + } + }).catch(error=>{}) + } + + // 切换页码 + function changepage(page){ + setPage(page); + window.scrollTo(0,0); + } + + function chooseFunc(ids){ + if(allValue && allValue.length>0){ + // 将ids保存下来以便修改 + setUpdateIds(ids); + setChooseValue(undefined); + }else{ + const {sort_by} = ids; + const params = {...ids, ...sortBy[sort_by]} + setChooseValue(params); + page===1 && Init(params); + setPage(1); + } + } + + return( +
    + {milepost && +
    +

    {milepost.name}

    +

    + + + {milepost.effective_date || '暂无截止时间'} + + {(milepost.percent || milepost.percent===0) ? {milepost.percent > 0 ? parseInt(milepost.percent*100):milepost.percent}%完成 :"" } +

    +
    +
    + { + (current_user && current_user.login) && (permission && permission !== "Reporter") ? + 编辑里程碑 + :"" + } + {/* { + current_user && current_user.login ? + + : + } */} + {history.push(`/${owner}/${projectsId}/issues/${mileId}/new`)}} checklogin className="operateButton ml20">+ 创建疑修 +
    +
    } + {/* 里程碑描述 */} +
    {milepost && milepost.description}
    +
    +
    +
    + + { + allValue && allValue.length>0 ? + 选择{allValue.length}个issue + : +
      +
    • setCategory("all")}>全部{issuesCount}
    • +
    • setCategory("opened")}>开启中{openedCount}
    • +
    • setCategory("closed")}>已关闭{closedCount}
    • +
    + } +
    +
    + 0} + owner={owner} + projectsId={projectsId} + chooseFunc={chooseFunc} + /> + { + allValue && allValue.length>0 ? +
    + + +
    + :"" + } +
    +
    + { + total === 0 && +
    + +

    欢迎使用里程碑

    +

    里程碑用于集中归类管理项目的疑修及进度。在使用之前,您可以先{props.history.push(`/${owner}/${projectsId}/issues/${mileId}/new`)}} className="color-blue">创建一个疑修

    +
    + } + { + total > 0 && + + +
    + {issueList.map((item,key)=>{ + return( + } + item={item} + owner={owner} + projectsId={projectsId} + /> + ) + }) + } +
    +
    + { + total > 10 && +
    + {setPage(1);setLimit(pageSize);}}/> +
    + } +
    + } + {total === undefined &&
    } +
    +
    + ) +} +export default MilepostDetail; \ No newline at end of file diff --git a/src/forge/Order/MilepostDetail_old.js b/src/forge/Order/MilepostDetail_old.js new file mode 100644 index 000000000..eb7c30dbb --- /dev/null +++ b/src/forge/Order/MilepostDetail_old.js @@ -0,0 +1,407 @@ +import React, { Component } from "react"; +import { Link } from 'react-router-dom'; +import { Dropdown, Menu, Icon, Pagination, Spin, Button, Checkbox } from 'antd'; +import './order.scss'; +import { FlexAJ } from '../Component/layout'; +import CheckProfile from '../Component/ProfileModal/Profile'; +import AllMenus from "../Issues/Component/allMenus"; +import NoneData from '../Nodata'; +import OrderItem from './OrderItem'; + +import axios from 'axios'; +import './milepost.scss'; + +/** + * issue_chosen:下拉的筛选列表, + * data:列表接口返回的所有数据, + * issues:列表数组, + * isSpin:加载中, + * author_id:发布者id, + * assigned_to_id:指派给。。。的id, + * limit:每页条数, + * page:当前页, + * search_count:列表总条数 + * issue_type:搜索条件 + * status_type: issue的关闭和开启,1表示开启中的,2表示关闭的 + * category: issue状态: 全部、开启、关闭 + */ +class MilepostDetail extends Component { + constructor(props) { + super(props); + this.state = { + issue_chosen: undefined, + data: undefined, + issues: undefined, + isSpin: true, + author_id: undefined, + status_id: undefined, + assigned_to_id: undefined, + limit: 15, + page: 1, + search_count: undefined, + issue_type: undefined, + status_type: '1', + //设置选择高亮 + issue_tag_ids: '标记', + tracker_ids: '类型', + author_ids: '发布人', + assigned_to_ids: '负责人', + status_ids: "状态", + done_ratios: '完成度', + paix: '排序', + issueFlag:true, + checkAll: false, + allValue: [], + allIds: [], + category: 'all' + } + } + + componentDidMount = () => { + this.getSelectList(); + const { page } = this.state; + this.getIssueList(page); + } + + componentDidUpdate=(prevProps)=>{ + this.updateDocumentTitle(); + const prebannerList = prevProps && prevProps.bannerList; + const bannerList = this.props && this.props.bannerList; + if(prebannerList !== bannerList){ + if(bannerList && bannerList.length>0){ + let i = bannerList.filter(i=>i.menu_name === "issues"); + this.setState({ + issueFlag:i && i.length === 0 ? false :true + }) + } + } + } + + // 更新网页标题 + updateDocumentTitle(){ + const {projectDetail} = this.props; + const {data} = this.state; + if(projectDetail && data){ + const { author, name} = projectDetail; + document.title = `${data.name}-里程碑-${author.name}/${name}`; + } + } + + getSelectList = () => { + const { projectsId , owner } = this.props.match.params; + const url = `/${owner}/${projectsId}/issues/index_chosen.json`; + axios.get(url).then((result) => { + if (result) { + this.setState({ + issue_chosen: result.data.issue_chosen + }) + } + }).catch((error) => { + console.log(error); + }) + } + + deletedetail = (id) => { + const { projectsId , owner } = this.props.match.params; + const url = `/${owner}/${projectsId}/issues/${id}.json`; + axios.delete(url, { + data: { + project_id: projectsId, + id: id, + }, + }) + .then((result) => { + if (result) { + const { page } = this.state; + this.getIssueList(page); + const { getDetail } = this.props; + getDetail && getDetail(); + } + }) + .catch((error) => { + console.log(error); + }); + }; + // 获取列表数据 + getIssueList = ( page , item , value , update , updateValue , type ) => { + const { projectsId, meilid , owner } = this.props.match.params; + const { limit , order_name , order_type , issue_tag_id , author_id , assigned_to_id , tracker_id , status_id , done_ratio , status_type } = this.state; + + const url = `/v1/${owner}/${projectsId}/milestones/${meilid}.json`; + let params = update ? { + page, limit , order_name:value , order_type:updateValue , issue_tag_id , + author_id , assigned_to_id , tracker_id , status_id , done_ratio, + status_type:type || status_type, + }:{ + page, limit , order_name , order_type , issue_tag_id , + author_id , assigned_to_id , tracker_id , status_id , done_ratio, + status_type:type || status_type, + [item]:value + } + axios.get(url, { + params: params + }).then((result) => { + if (result) { + this.setState({ + data: result.data.milestone, + issues: result.data.issues, + search_count: params.status_type ==="1" ? result.data.open_issues_count : result.data.close_issues_count, + isSpin: false + }) + } + }).catch((error) => { + console.log(error); + }) + } + + getOption = (e, id, name) => { + if (e.key === "created_on") { + if (e.item.props.value === "desc") { + this.setState({ + paix: '最新创建' + }) + } else { + this.setState({ + paix: '最早创建' + }) + } + } else if (e.key === "updated_on") { + if (e.item.props.value === "desc") { + this.setState({ + paix: '最新更新' + }) + } else { + this.setState({ + paix: '最早更新' + }) + } + } + this.setState({ + [id]: e.key, + [id+"s"]:name, + page:1 + }) + if (e.key === "created_on" || e.key === "updated_on") { + this.setState({ + order_name: e.key, + order_type: e.item.props.value + }) + this.getIssueList(1,id,e.key,true,e.item.props.value); + }else{ + this.getIssueList(1,id,e.key); + } + } + + renderMenu = (array, name, id) => { + return ( + + this.getOption(e, id, name)}>{name} + { + array && array.length > 0 && array.map((item, key) => { + return ( + this.getOption(e, id, item.name)}>{item.name} + ) + }) + } + + ) + } + + // 翻页 + ChangePage = (page) => { + this.setState({ + page, + isSpin: true + }) + this.getIssueList(page); + } + + category = (type) => { + this.setState({ + status_type: type, + issue_tag_id : undefined, + author_id : undefined, + tracker_id : undefined, + done_ratio : undefined, + status_id: undefined, + assigned_to_id: undefined, + issue_tag_ids: '标记', + tracker_ids: '类型', + author_ids: '发布人', + assigned_to_ids: '负责人', + status_ids: "状态", + done_ratios: '完成度', + paix: '排序' + },() => { + this.getIssueList(1,undefined,undefined,undefined,undefined,type); + }); + + } + + // 全选所有issue + chooseAll = (e) =>{ + const {allIds} = this.state; + this.setState({ + checkAll: e.target.checked, + allValue: e.target.checked ? allIds : [] + }); + } + + render() { + + const { issue_chosen, issues, limit, page, search_count, data, isSpin , status_type , issueFlag, checkAll, allValue, category } = this.state; + const { projectsId, meilid ,owner} = this.props.match.params; + const { current_user , showLoginDialog , projectDetail } = this.props; + const menu = ( + this.getOption(e)}> + 最新创建 + 最早创建 + 最新更新 + 最早更新 + + ) + return ( +
    + +
    +

    {data && data.name}

    +

    + + + + { + data && data.effective_date ? + {data && data.effective_date} + : + 暂无截止时间 + } + + {data && (data.percent || data.percent===0) ? {data.percent > 0 ? data.percent.toFixed(2):data.percent}%完成 :"" } +

    +
    +
    + { + (current_user && current_user.login) && ( projectDetail && projectDetail.permission && projectDetail.permission !== "Reporter") ? + 编辑里程碑 + :"" + } + { + issueFlag ? (current_user && current_user.login ? + + :) + :"" + } +
    +
    + + +
    +
    + + { + allValue && allValue.length>0 ? + 选择{allValue.length}个issue + : +
      +
    • this.category("all")}>全部{total}
    • +
    • this.category("opened")}>开启中{openedCount}
    • +
    • this.category("closed")}>已关闭{closedCount}
    • +
    + } +
    +
    + 0} + owner={owner} + projectsId={projectsId} + chooseFunc={chooseFunc} + /> + { + allValue && allValue.length>0 ? +
    + + + +
    + :"" + } +
    +
    + {/*
    +
    +
      +
    • this.openorder("1")}>{data && data.open_issues_count}个开启中
    • +
    • this.openorder("2")}>{data && data.close_issues_count}个已关闭
    • +
    +
      +
    • + + {this.state.issue_tag_ids} + +
    • +
    • + + {this.state.author_ids} + +
    • +
    • + + {this.state.assigned_to_ids} + +
    • +
    • + + {this.state.tracker_ids} + +
    • +
    • + + {this.state.status_ids} + +
    • +
    • + + {this.state.done_ratios} + +
    • +
    • + + {this.state.paix} + +
    • +
    +
    +
    + { + search_count === 0 ? + + : + issues && issues.length>0 && issues.map((item,key)=>{ + return( + + ) + }) + } +
    + { + search_count > limit? +
    + +
    :"" + } +
    */} +
    +
    + ) + } +} +export default MilepostDetail; \ No newline at end of file diff --git a/src/forge/Order/Nav.js b/src/forge/Order/Nav.js index 3f5a20c59..c3f967184 100644 --- a/src/forge/Order/Nav.js +++ b/src/forge/Order/Nav.js @@ -1,6 +1,6 @@ import React , { Component } from "react"; import { NavLink } from 'react-router-dom'; -import './order.css' +import './order.scss' class Nav extends Component{ render(){ diff --git a/src/forge/Order/OrderItem.js b/src/forge/Order/OrderItem.js index 4455d7bad..0da644c28 100644 --- a/src/forge/Order/OrderItem.js +++ b/src/forge/Order/OrderItem.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; -import { Popconfirm } from 'antd' +import { Popconfirm,Tooltip } from 'antd' import { TagInfo } from '../Utils/TagColor'; class OrderItem extends Component { constructor(props) { @@ -84,7 +84,9 @@ class OrderItem extends Component { }
  • {item.tracker || "--"}
  • - { !mile ?
  • {item.version || "--"}
  • :""} + {!mile ?
  • + {item.version ? {item.version} : "--"} +
  • : ""}
  • {item.issue_status || "--"}
  • {item.done_ratio || "--"}
  • diff --git a/src/forge/Order/Tags.js b/src/forge/Order/Tags.js index 221db711c..4082c022a 100644 --- a/src/forge/Order/Tags.js +++ b/src/forge/Order/Tags.js @@ -76,11 +76,6 @@ class Tags extends Component { componentDidMount = () => { this.getList(); - const {projectDetail} = this.props; - if(projectDetail){ - const { author, name} = projectDetail; - document.title = `标签列表-${author.name}/${name}`; - } } @@ -110,7 +105,6 @@ class Tags extends Component { const url = `/projects/${owner}/${projectsId}/labels.json`; axios.post(url, { ...values, - project_id: projectsId, color: this.state.textcolor }).then(result => { if (result) { diff --git a/src/forge/Order/UpdateMilepost.js b/src/forge/Order/UpdateMilepost.js index c4f386575..4bb410409 100644 --- a/src/forge/Order/UpdateMilepost.js +++ b/src/forge/Order/UpdateMilepost.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { Calendar, Select, Radio, Col, Row, Divider, Input, Form, Spin } from 'antd'; import axios from 'axios'; import moment from 'moment'; -import '../Order/order.css'; +import '../Order/order.scss'; const TextArea = Input.TextArea; @@ -22,6 +22,20 @@ class UpdateMilepost extends Component { this.getmeil(); } + componentDidUpdate=()=>{ + this.updateDocumentTitle(); + } + + // 更新网页标题 + updateDocumentTitle(){ + const {projectDetail} = this.props; + const {data} = this.state; + if(projectDetail && data){ + const { author, name} = projectDetail; + document.title = `${data.name}-里程碑-${author.name}/${name}`; + } + } + onPanelChange = (time, mode) => { this.setState({ value: time @@ -53,7 +67,6 @@ class UpdateMilepost extends Component { name: result.data.name, description: result.data.description }); - } }).catch((error) => { console.log(error); @@ -79,8 +92,8 @@ class UpdateMilepost extends Component { }).then(result => { if (result) { this.setState({ isSpin: false }) - this.props.history.push(`/${owner}/${projectsId}/milestones`); - + window.scrollTo(0,0); + this.props.history.push(`/${owner}/${projectsId}/milestones/${meilid}`); } }).catch(error => { @@ -120,7 +133,7 @@ class UpdateMilepost extends Component { required: true, message: '请输入标题' }], })( - + )}
  • @@ -131,8 +144,7 @@ class UpdateMilepost extends Component { required: true, message: '请输入描述内容' }], })( -