Merge branch 'gitlink_server' of https://gitlink.org.cn/durian/forgeplus-react into feature_IDE
|
@ -7245,8 +7245,7 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"optional": true
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -7267,14 +7266,12 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"optional": true
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -7289,20 +7286,17 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"optional": true
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"optional": true
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"optional": true
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -7419,8 +7413,7 @@
|
|||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"optional": true
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -7432,7 +7425,6 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -7447,7 +7439,6 @@
|
|||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -7455,14 +7446,12 @@
|
|||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"optional": true
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -7481,7 +7470,6 @@
|
|||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
|
||||
"integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
|
@ -7543,8 +7531,7 @@
|
|||
"npm-normalize-package-bin": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
|
||||
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
|
||||
"optional": true
|
||||
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
|
||||
},
|
||||
"npm-packlist": {
|
||||
"version": "1.4.8",
|
||||
|
@ -7572,8 +7559,7 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"optional": true
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -7585,7 +7571,6 @@
|
|||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -7663,8 +7648,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"optional": true
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -7700,7 +7684,6 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -7720,7 +7703,6 @@
|
|||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -7764,14 +7746,12 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"optional": true
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"optional": true
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {useEffect} from 'react';
|
||||
import { WhiteBack } from '../Component/layout';
|
||||
import './ops.scss';
|
||||
|
||||
import devops from '../Images/devops.png';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import Loadable from 'react-loadable';
|
||||
import Loading from '../../Loading';
|
||||
|
@ -32,8 +32,7 @@ const Params = Loadable({
|
|||
})
|
||||
|
||||
export default ((props)=>{
|
||||
console.log('props', props);
|
||||
const {jianmu_devops} = props;
|
||||
const {jianmu_devops, isManager, project} = props;
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("message", iframeHeight, false);
|
||||
|
@ -44,7 +43,6 @@ export default ((props)=>{
|
|||
|
||||
|
||||
function iframeHeight(e){
|
||||
console.log('e',e&&typeof(e.data), e);
|
||||
if (e && e.data && typeof(e.data) === "string") {
|
||||
let myHeight = JSON.parse(e.data);
|
||||
if (document.querySelector("#devopsIframe")) {
|
||||
|
@ -65,9 +63,16 @@ export default ((props)=>{
|
|||
}
|
||||
|
||||
return(
|
||||
<WhiteBack className="opsPanel main">
|
||||
<WhiteBack className={`opsPanel ${isManager ? 'main' : ''}`}>
|
||||
{/* 嵌入devops */}
|
||||
{jianmu_devops && <iframe title={`devopsIframe`} src={`https://ci-v3.test.jianmuhub.com/oauth2/authorize?code=${jianmu_devops}`} id={`devopsIframe`} frameBorder="0" name={`devopsIframe`} width="100%" onLoad={iframeLoad} height={'auto'}></iframe>}
|
||||
{jianmu_devops && project && isManager && <iframe title={`devopsIframe`} src={`${project.jianmu_devops_url}/oauth2/authorize?code=${jianmu_devops}`} id={`devopsIframe`} frameBorder="0" name={`devopsIframe`} width="100%" onLoad={iframeLoad} height={'auto'}></iframe>}
|
||||
{!isManager && <div className='nullJurisdictionBox'>
|
||||
<div className='jurTil font-16'>引擎配置</div>
|
||||
<div className='jurCont mt25'>
|
||||
<img src={devops} alt="" width={110}/>
|
||||
<div className='font-18 mt30'>暂无权限,仅仓库管理员可访问</div>
|
||||
</div>
|
||||
</div>}
|
||||
{/* 旧引擎页面 */}
|
||||
{/* <Switch {...props}>
|
||||
<Route path="/:owner/:projectsId/devops/params"
|
||||
|
|
|
@ -522,4 +522,22 @@
|
|||
border:1px solid #999;
|
||||
color:#999 ;
|
||||
}
|
||||
}
|
||||
.nullJurisdictionBox{
|
||||
color:#333333;
|
||||
.jurTil{
|
||||
width:1200px;
|
||||
padding: 15px 16px;
|
||||
background-color:#fafcff;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
border-radius:3px 3px 0px 0px;
|
||||
}
|
||||
.jurCont{
|
||||
width:1200px;
|
||||
height:317px;
|
||||
padding-top: 45px;
|
||||
background-color:#fafcff;
|
||||
border-radius:4px 4px 0px 0px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -32,15 +32,15 @@ function DetailBanner({ history,list , owner , projectsId ,showNotification , ur
|
|||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
|
||||
return(
|
||||
<div className="f-wrap-between mt25">
|
||||
<QuitBox visible={visible} onCancel={()=>setVisible(false)} name={projectDetail && projectDetail.name} onSuccess={onSuccess}/>
|
||||
{
|
||||
menuName && projectDetail ?
|
||||
menuName && projectDetail ?
|
||||
<ul className="headerMenu-wrapper">
|
||||
{
|
||||
Array.isArray(menuName)&& menuName.map((item,key)=>{
|
||||
Array.isArray(menuName)&& menuName.map((item,key)=>{
|
||||
return(
|
||||
<React.Fragment key={item.menu_name}>
|
||||
{
|
||||
|
@ -85,7 +85,7 @@ function DetailBanner({ history,list , owner , projectsId ,showNotification , ur
|
|||
}
|
||||
{/* 引擎仅对当前仓库管理员可见 */}
|
||||
{
|
||||
item.menu_name === "devops" && isManager ?
|
||||
item.menu_name === "devops" ?
|
||||
<li className={pathname==="devops" ? "active" : ""}>
|
||||
{/* <Link to={{ pathname: `/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}> */}
|
||||
<Link to={{ pathname: `/${owner}/${projectsId}/devops`, state:{...state,open_devops} }}>
|
||||
|
|
|
@ -13,10 +13,20 @@ const Data = Loadable({
|
|||
loader: () => import('./data'),
|
||||
loading: Loading,
|
||||
})
|
||||
// 跨平台代码同步服务
|
||||
const Reposyncer = Loadable({
|
||||
loader: () => import('./reposyncer'),
|
||||
loading: Loading,
|
||||
})
|
||||
function ServerIndex(props){
|
||||
return(
|
||||
<div className="panels">
|
||||
<Switch {...props}>
|
||||
<Route path="/:owner/:projectsId/server/reposyncer"
|
||||
render={
|
||||
() => (<Reposyncer {...props}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/:owner/:projectsId/server/:id"
|
||||
render={
|
||||
() => (<Data {...props}/>)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React,{useState , useEffect} from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import AgreementModal from './agreementModal';
|
||||
|
||||
function Main(props){
|
||||
|
@ -6,10 +7,12 @@ function Main(props){
|
|||
const { owner , projectsId } = props.match.params;
|
||||
const [ has_trace_user , setHas_trace_user ] = useState(false);
|
||||
|
||||
const { current_user , resetUserInfo } = props;
|
||||
const { current_user , resetUserInfo, projectDetail, showNotification } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
if(current_user){
|
||||
if(current_user && !current_user.login){
|
||||
props.history.push(`/login?go_page=/${owner}/${projectsId}/server`);
|
||||
}else{
|
||||
setHas_trace_user(current_user.has_trace_user);
|
||||
}
|
||||
},[current_user])
|
||||
|
@ -21,7 +24,9 @@ function Main(props){
|
|||
}
|
||||
|
||||
function openDetail(){
|
||||
if(!has_trace_user){
|
||||
if(projectDetail && projectDetail.forked_from_project_id){
|
||||
showNotification("fork仓库暂不支持代码溯源服务,敬请谅解。");
|
||||
}else if(!has_trace_user){
|
||||
setVisible(true);
|
||||
}else{
|
||||
props.history.push(`/${owner}/${projectsId}/server/1`);
|
||||
|
@ -41,6 +46,16 @@ function Main(props){
|
|||
<a onClick={openDetail} className="btnhover">查看详情</a>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="servername">
|
||||
<img src={require('./img/logo.png')} alt=""/>
|
||||
<Link to={`/${owner}/${projectsId}/server/reposyncer`}>Reposyncer仓库同步系统</Link>
|
||||
</span>
|
||||
<p className="task-hide-2 serverdesc">支持不同开源托管平台自动同步推送/拉取相关代码,实现多平台项目同步开发功能</p>
|
||||
<span className="serverbtn">
|
||||
<Link to={`/${owner}/${projectsId}/server/reposyncer`} className="btnhover">查看详情</Link>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -3,22 +3,23 @@ import DataEmpty from './dataEmpty';
|
|||
import DetectionModal from './detectionModal';
|
||||
import { Table, Spin, message } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
import img from '../Images/img1.png';
|
||||
|
||||
function Data(props) {
|
||||
const [detectionVisible, setDetectionVisible] = useState(false);
|
||||
const [dataSource, setDataSource] = useState(undefined);
|
||||
const [page, setPage] = useState(1);
|
||||
const [spining, setSpining] = useState(true);
|
||||
const [spining, setSpining] = useState(false);
|
||||
const [relayCount, setRelayCount] = useState(5);
|
||||
const [repeatId, setRepeatId] = useState(undefined);
|
||||
const [repeatBranch, setRepeatBranch] = useState(undefined);
|
||||
const [lookResultUrl, setLookResultUrl] = useState(undefined);
|
||||
const [openResultTaskId, setOpenResultTaskId] = useState(undefined);
|
||||
const [viewBase, setViewBase] = useState('https://cjntest.trustie.net/');
|
||||
const [operateTime, setOperateTime] = useState(undefined);
|
||||
|
||||
const { owner, projectsId } = props.match.params;
|
||||
const { current_user, isManager } = props;
|
||||
const { isManager, projectDetail, history } = props;
|
||||
const limit = 15;
|
||||
|
||||
|
||||
|
@ -37,8 +38,9 @@ function Data(props) {
|
|||
}
|
||||
}).then(result => {
|
||||
if (result) {
|
||||
|
||||
if (Array.isArray(result.data.data)) {
|
||||
if(result.data.code === 501){
|
||||
setOperateTime(result.data.data.operate_time);
|
||||
}else if (Array.isArray(result.data.data)) {
|
||||
setDataSource(result.data.data);
|
||||
setRelayCount(result.data.left_tasks_count);
|
||||
setLookResultUrl(result.data.view_base);
|
||||
|
@ -47,21 +49,28 @@ function Data(props) {
|
|||
if (item.detect_status === "detecting" || item.detect_status === "waiting" || (item.detect_status=="detected"&& item.is_reported=="0")) {
|
||||
setTimeout(Init, 2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result && result.data.message && props.showNotification(result.data.message);
|
||||
}
|
||||
|
||||
setSpining(false);
|
||||
}else{
|
||||
setSpining(false);
|
||||
}
|
||||
}).catch(error => { })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setSpining(true);
|
||||
Init();
|
||||
}, [])
|
||||
if(projectDetail){
|
||||
if(projectDetail.forked_from_project_id){
|
||||
history.go(-1);
|
||||
}else{
|
||||
setSpining(true);
|
||||
Init();
|
||||
}
|
||||
}
|
||||
}, [projectDetail])
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("message", iframeHeight, false);
|
||||
|
@ -154,12 +163,17 @@ function Data(props) {
|
|||
/>
|
||||
<div className="servertitle">
|
||||
<span className="systitle">重晴鸟代码溯源系统</span>
|
||||
{isManager && <a className="btnhover" onClick={createCheck}>新建分析</a>}
|
||||
{!operateTime && isManager && <a className="btnhover" onClick={createCheck}>新建分析</a>}
|
||||
</div>
|
||||
<Spin spinning={spining}>
|
||||
<div style={{ minHeight: "400px" }}>
|
||||
{operateTime && <div className='operateBox mt25'>
|
||||
<img src={img} alt='' width={130}/>
|
||||
<div className='font-20 mt20 mb5'>系统维护中</div>
|
||||
<div className='font-17'>预计<span className='timeBox'>{operateTime}小时后</span>代码溯源系统将恢复正常访问,给您带来不便,敬请谅解!</div>
|
||||
</div>}
|
||||
{
|
||||
dataSource && dataSource.length > 0 &&
|
||||
!operateTime && dataSource && dataSource.length > 0 &&
|
||||
<div>
|
||||
<ul className="dataUl">
|
||||
<li className="dataUlhead">
|
||||
|
@ -229,7 +243,7 @@ function Data(props) {
|
|||
</div>
|
||||
}
|
||||
{
|
||||
(dataSource === null || (dataSource && dataSource.length === 0)) &&
|
||||
!operateTime && (dataSource === null || (dataSource && dataSource.length === 0)) &&
|
||||
<DataEmpty />
|
||||
}
|
||||
</div>
|
||||
|
|
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="23.589" height="23.589" viewBox="0 0 23.589 23.589">
|
||||
<g id="gitee" transform="translate(0)">
|
||||
<g id="Group" transform="translate(0 0)">
|
||||
<ellipse id="Combined-Shape" cx="11.795" cy="11.795" rx="11.795" ry="11.795" fill="#c71d23"/>
|
||||
<path id="G" d="M32.458,25.178h-6.7a.583.583,0,0,0-.583.582v1.456a.582.582,0,0,0,.582.583h4.078a.582.582,0,0,1,.582.582h0v.146h0v.146a1.747,1.747,0,0,1-1.747,1.747H23.138a.582.582,0,0,1-.582-.582V24.3A1.747,1.747,0,0,1,24.3,22.557h8.153a.584.584,0,0,0,.583-.582V20.518a.582.582,0,0,0-.582-.583H24.3A4.368,4.368,0,0,0,19.935,24.3v8.154a.582.582,0,0,0,.582.582h8.591a3.931,3.931,0,0,0,3.931-3.931V25.76A.582.582,0,0,0,32.458,25.178Z" transform="translate(-14.693 -14.693)" fill="#fff" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 822 B |
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,3 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="23.585" height="23.587" viewBox="0 0 23.585 23.587">
|
||||
<path id="路径_46" data-name="路径 46" d="M11.831,0A11.914,11.914,0,0,0,.136,10.156,12.116,12.116,0,0,0,8.089,23.568c.6.111.806-.267.806-.586V20.925c-3.306.735-4-1.626-4-1.626a3.23,3.23,0,0,0-1.315-1.774c-1.068-.742.087-.742.087-.742A2.492,2.492,0,0,1,5.473,18.03,2.539,2.539,0,0,0,6.99,19.266a2.481,2.481,0,0,0,1.927-.226,2.593,2.593,0,0,1,.727-1.618c-2.63-.3-5.391-1.344-5.391-5.938A4.734,4.734,0,0,1,5.466,8.239a4.5,4.5,0,0,1,.116-3.2s1-.327,3.255,1.24a10.968,10.968,0,0,1,5.929,0c2.26-1.566,3.248-1.24,3.248-1.24a4.475,4.475,0,0,1,.145,3.177,4.734,4.734,0,0,1,1.213,3.244c0,4.647-2.768,5.664-5.406,5.938a2.893,2.893,0,0,1,.806,2.227c0,1.618,0,2.925,0,3.318s.211.7.814.579a12.12,12.12,0,0,0,7.824-13.379A11.917,11.917,0,0,0,11.831,0Z" transform="translate(0.018 0)" fill="#1a1414" fill-rule="evenodd"/>
|
||||
</svg>
|
After Width: | Height: | Size: 952 B |
After Width: | Height: | Size: 6.5 KiB |
|
@ -451,4 +451,14 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.operateBox{
|
||||
color: #333;
|
||||
text-align: center;
|
||||
background-color:#fafcff;
|
||||
border-radius:4px 4px 0px 0px;
|
||||
padding: 50px 0 80px;
|
||||
.timeBox{
|
||||
color: rgba(70, 106, 255, 1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
import React, {useState} from 'react';
|
||||
import { Button, Modal, Input, Select, message } from 'antd';
|
||||
import axios from 'axios';
|
||||
const {Option} = Select;
|
||||
|
||||
function DeleteBox({owner, projectsId, visible, setVisible, createJobBy, branchOptions, reload}) {
|
||||
const [error, setError] = useState(undefined);
|
||||
const [errorByOther, setErrorByOther] = useState(undefined);
|
||||
const [branch, setBranch] = useState(undefined);
|
||||
const [branchByOther, setBranchByOther] = useState(undefined);
|
||||
|
||||
// 新建同步分支点击函数
|
||||
function createNew(){
|
||||
if(branch && branchByOther){
|
||||
// 新建同步分支
|
||||
const param = {
|
||||
gitlink_branch: branch,
|
||||
job_type: 'TwoWay',
|
||||
github_branch: undefined,
|
||||
gitee_branch: undefined
|
||||
}
|
||||
createJobBy === 'Github' ? (param.github_branch = branchByOther) : (param.gitee_branch = branchByOther)
|
||||
axios.post(`/${owner}/${projectsId}/synchronizes/create_jobs.json`,param).then(res=>{
|
||||
if(res && res.data.message === "success"){
|
||||
setBranch(undefined);
|
||||
setBranchByOther(undefined);
|
||||
reload && reload(Math.random());
|
||||
message.success('新建成功');
|
||||
setVisible(false);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
!branch && setError('请选择仓库分支')
|
||||
!branchByOther && setErrorByOther('请输入仓库分支')
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<Modal
|
||||
title="新建同步分支"
|
||||
visible={visible}
|
||||
onCancel={()=>{setVisible(false)}}
|
||||
footer={<div><Button style={{width: '104px', height:'36px'}} onClick={()=>{setVisible(false)}}>取消</Button><Button type="primary" style={{width: '104px', height:'36px', marginLeft: '40px'}} onClick={createNew}>确认</Button></div>}
|
||||
width={550}
|
||||
className="cancelBound createJobBox"
|
||||
>
|
||||
<div className="itemBox mt10">
|
||||
<label className="labelBox font-16"><i className="iconfont icon-a-bitian2x font-12"></i> {createJobBy}分支:</label>
|
||||
<Input placeholder="请输入分支名称" className="inputBox" value={branchByOther} onChange={(e)=>setBranchByOther(e.target.value)} maxLength={50}/>
|
||||
<div className="errorBox">{errorByOther}</div>
|
||||
</div>
|
||||
<div className="itemBox mt30 mb20">
|
||||
<label className="labelBox font-16"><i className="iconfont icon-a-bitian2x font-12"></i> GitLink分支:</label>
|
||||
<Select
|
||||
value={branch}
|
||||
onSelect={(e) => {setBranch(e)}}
|
||||
showSearch
|
||||
className="inputBox"
|
||||
dropdownMatchSelectWidth={false}
|
||||
dropdownClassName="overlihide"
|
||||
placeholder="请选择仓库分支"
|
||||
>
|
||||
{branchOptions && branchOptions.map((item, key) => {
|
||||
return (
|
||||
<Option key={key + 1} value={item.name}>
|
||||
{item.name}
|
||||
</Option>
|
||||
)})}
|
||||
</Select>
|
||||
<div className="errorBox">{error}</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default DeleteBox;
|
|
@ -0,0 +1,97 @@
|
|||
import React, {forwardRef} from "react";
|
||||
import { Button, Form, Input, message } from "antd";
|
||||
import gitHub from '../../img/github.png';
|
||||
import gitee from '../../img/gitee.png';
|
||||
import '../index.scss';
|
||||
import axios from "axios";
|
||||
|
||||
function EditStore(props){
|
||||
const { form, history} = props;
|
||||
const { owner , projectsId } = props.match.params;
|
||||
const { getFieldDecorator, validateFields , setFieldsValue, setFields } = form;
|
||||
|
||||
function submit() {
|
||||
validateFields((error,values)=>{
|
||||
setFields({
|
||||
'github_address':{value: values.github_address, errors: null},
|
||||
'github_token':{value: values.github_token, errors: null},
|
||||
'gitee_address':{value: values.gitee_address, errors: null},
|
||||
'gitee_token':{value: values.gitee_token, errors: null}
|
||||
})
|
||||
if(!error){
|
||||
// 处理空值
|
||||
for(let i in values){
|
||||
if(!values[i]){
|
||||
delete values[i];
|
||||
}
|
||||
}
|
||||
const keysArr = Object.keys(values);
|
||||
if(keysArr.indexOf('github_address') === -1 && keysArr.indexOf('gitee_address') === -1){
|
||||
// 校验必填一个github或者gitee地址
|
||||
form.setFields({github_address: {value:values.github_address,errors:[new Error('请至少输入一个地址')]}});
|
||||
form.setFields({gitee_address: {value:values.gitee_address,errors:[new Error('请至少输入一个地址')]}});
|
||||
}else if(keysArr.indexOf('github_address') !== -1 && keysArr.indexOf('github_token') === -1){
|
||||
form.setFields({github_token: {value:values.github_token,errors:[new Error('请输入Github的授权token')]}});
|
||||
}else if(keysArr.indexOf('gitee_address') !== -1 && keysArr.indexOf('gitee_token') === -1){
|
||||
form.setFields({gitee_token: {value:values.gitee_token,errors:[new Error('请输入Gitee的授权token')]}});
|
||||
}else{
|
||||
axios.post(`/${owner}/${projectsId}/synchronizes.json`,values).then(res=>{
|
||||
if(res && res.data.message === "success"){
|
||||
message.success('绑定成功');
|
||||
window.location.href = `/${owner}/${projectsId}/server/reposyncer`;
|
||||
}else{
|
||||
// message.error('绑定失败');
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="storeListBox mt20">
|
||||
<div className="registerBox">
|
||||
<Form>
|
||||
<div className="storeTitle pb10 mb15"><img src={gitHub} alt="" className="storeLogo"/></div>
|
||||
<Form.Item label="Github同步仓库地址" className="storeFormItem">
|
||||
{getFieldDecorator("github_address",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input placeholder="请输入Github目标版本库地址" className="storeInput"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="Github同步仓库授权验证" className="storeFormItem">
|
||||
{getFieldDecorator("github_token",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input addonBefore="token" placeholder="请输入Github的授权token" className="storeInput"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="storeTitle pb10 mb15 pt20"><img src={gitee} alt="" className="storeLogo"/></div>
|
||||
<Form.Item label="Gitee同步仓库地址" className="storeFormItem">
|
||||
{getFieldDecorator("gitee_address",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input placeholder="请输入Gitee目标版本库地址" className="storeInput"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="Gitee同步仓库授权验证" className="storeFormItem">
|
||||
{getFieldDecorator("gitee_token",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input addonBefore="token" placeholder="请输入Gitee的授权token" className="storeInput"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="tipStoreBox">
|
||||
1、在开启仓库同步前,您需要绑定并授权目标同步仓库;<br/>
|
||||
2、完成仓库绑定后,需对仓库内的分支进行二次绑定。当已绑定的分支有代码提交、代码推送变更,将实时同步更新至其他仓库;<br/>
|
||||
3、目前仅提供Github与Gitee平台的仓库同步功能,每个平台支持同时同步一个仓库;
|
||||
</div>
|
||||
<Form.Item>
|
||||
<Button type="primary" style={{width: '112px', height: '36px'}} onClick={submit}>确认绑定</Button>
|
||||
<Button style={{width: '112px', height: '36px'}} className="ml40" onClick={()=>{history.goBack(-1)}}>取消</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
export default Form.create()(forwardRef(EditStore));
|
|
@ -0,0 +1,165 @@
|
|||
import React, {useState} from "react";
|
||||
import { Table, Modal, Button, message } from 'antd';
|
||||
import { Link } from "react-router-dom";
|
||||
import gitHub1 from '../../img/github2.svg';
|
||||
import gitee1 from '../../img/gitee1.svg';
|
||||
import anniu from '../../img/anniu.png';
|
||||
import anniu2 from '../../img/anniu2.png';
|
||||
import '../index.scss';
|
||||
import { useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import CreateJobModal from './createJobModal';
|
||||
import Nodata from "../../../Nodata";
|
||||
|
||||
function RecordList(props){
|
||||
const { owner , projectsId, type } = props.match.params;
|
||||
const { storeDetail} = props;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState([]);
|
||||
// table
|
||||
const [current, setCurrent] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
|
||||
const [deleteRecordId, setDeleteRecordId] = useState(undefined);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [reload, setReload] = useState(undefined);
|
||||
const [visibleNew, setVisibleNew] = useState(false);
|
||||
const [branchOptions, setBranchOptions] = useState([]);
|
||||
const [logInfo, setLogInfo]= useState([]);
|
||||
|
||||
useEffect(()=>{
|
||||
axios.get(`/${owner}/${projectsId}/pulls/get_branches.json`, {}).then(res=>{
|
||||
res && setBranchOptions(res.data);
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取仓库同步记录
|
||||
axios.get(`/${owner}/${projectsId}/synchronizes/jobs.json`, {params: {type: type, limit: pageSize, page: current}}).then(res=>{
|
||||
if(res && res.data && res.data.message === "success"){
|
||||
// 处理从后往前删除
|
||||
if(current !== 1 && res.data.data.length === 0){
|
||||
setCurrent(current-1);
|
||||
}else{
|
||||
res.data.data.map(item=>{
|
||||
item.create_time = item.create_time.replace('T', ' ')
|
||||
})
|
||||
setData(res.data.data);
|
||||
setTotal(res.data.count);
|
||||
}
|
||||
}
|
||||
})
|
||||
},[reload, pageSize, current])
|
||||
|
||||
// 同步状态errorBox
|
||||
let columns = [
|
||||
{ title: '序号', dataIndex: 'index', className:"recordColumns", render: (text, item, index) => <span>{(current-1)*pageSize+index + 1}</span> },
|
||||
{ title: type === 'github' ? 'Github分支' : 'Gitee分支', dataIndex: type === 'github' ? 'github_branch': 'gitee_branch', className:"recordColumns taskName"},
|
||||
{ title: 'GitLink分支', dataIndex: 'gitlink_branch', className:"recordColumns"},
|
||||
{ title: '创建时间', dataIndex: 'create_time', className:"primaryColor recordColumns"},
|
||||
{ title: '同步状态', dataIndex: 'status', className:"recordColumns", render: ()=><span className="accomplish statusBox">开启中</span>},
|
||||
{ title: '操作', dataIndex: 'action', align: 'center', className:"primaryColor recordColumns", render: (text, item)=><span className="deleteRecord" onClick={()=>{setVisible(true);setDeleteRecordId(item.id);}}>删除</span>},
|
||||
]
|
||||
|
||||
const customExpandIcon = (props) => {
|
||||
if (props.expanded) {
|
||||
return <a className='primaryColor' onClick={e => {
|
||||
props.onExpand(props.record, e);
|
||||
}}>查看日志<img alt="" src={anniu2} style={{width: '18px'}} className="ml5"/></a>
|
||||
} else {
|
||||
return <a className='primaryColor' onClick={e => {
|
||||
axios.get(`/${owner}/${projectsId}/synchronizes/job_logs.json`, {params:{job_id: props.record.id}}).then(res=>{
|
||||
if(res && res.data){
|
||||
setLogInfo(res.data.data);
|
||||
props.onExpand(props.record, e);
|
||||
}
|
||||
})
|
||||
}}>查看日志<img alt="" src={anniu} style={{width: '18px'}} className="ml5"/></a>
|
||||
}
|
||||
}
|
||||
|
||||
const expandRow = (record) => {
|
||||
return logInfo && logInfo.length > 0 ? <div className="expandBox">
|
||||
{logInfo.map(item=>{
|
||||
return <div className="expandCont" key={item.id}>
|
||||
{item.create_time + ' [' + item.log_type + '] ' + item.log}
|
||||
</div>
|
||||
})}
|
||||
</div> : <Nodata _html="暂无数据"/>}
|
||||
|
||||
// 展开收起行回调
|
||||
function onExpand(expanded, record){
|
||||
const keys = new Set(expandedRowKeys);
|
||||
if(expanded){
|
||||
keys.add(record.id);
|
||||
}else{
|
||||
keys.delete(record.id);
|
||||
}
|
||||
setExpandedRowKeys(Array.from(keys));
|
||||
}
|
||||
|
||||
// 改变pagesize
|
||||
function onShowSizeChange(current, pageSize){
|
||||
window.scrollTo(0, 0);
|
||||
setCurrent(1);
|
||||
setPageSize(pageSize);
|
||||
}
|
||||
|
||||
// 切换页数
|
||||
function changePage(page, pageSize){
|
||||
window.scrollTo(0, 0);
|
||||
setCurrent(page);
|
||||
}
|
||||
|
||||
// 删除同步分支
|
||||
function deleteRecord(){
|
||||
deleteRecordId && axios.delete(`/${owner}/${projectsId}/synchronizes/delete_job.json`, {data: {job_id: deleteRecordId}}).then(res=>{
|
||||
if(res && res.data.message === "success"){
|
||||
setReload(Math.random());
|
||||
message.success('删除成功');
|
||||
setVisible(false);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="storeListBox">
|
||||
<div className="font-16">
|
||||
<Link to={`/${owner}/${projectsId}/server/reposyncer`} className="blueSpan">仓库绑定</Link>
|
||||
<span> > 同步分支</span>
|
||||
</div>
|
||||
<div className="headBox font-16 pl15 mt20 mb10">
|
||||
<img src={type === 'github' ? gitHub1 : gitee1} alt="" className="mr10 mb5"/>
|
||||
<span>{type === 'github' ? 'Github' : 'Gitee'}仓库地址</span>
|
||||
{storeDetail && <a className="ml15 blueSpan" href={type === 'github' ? storeDetail.github_address : storeDetail.gitee_address} target="_blank">{type === 'github' ? storeDetail.github_address : storeDetail.gitee_address}</a>}
|
||||
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but createJobBut" onClick={()=>{setVisibleNew(true);}}>新建同步分支</Button>
|
||||
</div>
|
||||
<Table
|
||||
className="storeListTable"
|
||||
loading={loading}
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
expandedRowRender={expandRow}
|
||||
expandIconColumnIndex={5}
|
||||
expandIconAsCell={false}
|
||||
expandIcon={customExpandIcon}
|
||||
rowKey={'id'}
|
||||
expandedRowKeys={expandedRowKeys}
|
||||
onExpand={onExpand}
|
||||
pagination={{current: current, pageSize: pageSize, total: total, showSizeChanger: true, onShowSizeChange:onShowSizeChange, showQuickJumper: true, onChange: changePage}}
|
||||
/>
|
||||
<Modal
|
||||
title="删除同步分支"
|
||||
visible={visible}
|
||||
onCancel={()=>{setVisible(false)}}
|
||||
footer={<div><Button style={{width: '90px', height:'36px'}} onClick={()=>{setVisible(false)}}>取消</Button><Button className="okBut" style={{width: '90px', height:'36px'}} onClick={deleteRecord}>确认删除</Button></div>}
|
||||
width={535}
|
||||
className="cancelBound"
|
||||
>
|
||||
<div className="bTilModal font-16"><span className="errorRedSpan font-18 mr20 mt20 ml15">!</span>确认删除此同步分支?</div>
|
||||
<div className="sTilModal"> 删除同步分支后,系统将清除此条同步数据及日志,对应分支也将停止自动同步</div>
|
||||
</Modal>
|
||||
<CreateJobModal owner={owner} projectsId={projectsId} visible={visibleNew} setVisible={setVisibleNew} createJobBy={type === 'github' ? 'Github' : 'Gitee'} branchOptions={branchOptions} reload={setReload}/>
|
||||
</div>
|
||||
}
|
||||
export default RecordList;
|
|
@ -0,0 +1,89 @@
|
|||
import React, { useEffect, useState} from "react";
|
||||
import { Button, Modal, Icon, message, Select, Input, Tooltip } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import gitHub1 from '../../img/github2.svg';
|
||||
import gitee1 from '../../img/gitee1.svg';
|
||||
import logo from '../../img/logo2.png';
|
||||
import '../index.scss';
|
||||
import axios from "axios";
|
||||
import CreateJobModal from './createJobModal';
|
||||
|
||||
function StoreList(props){
|
||||
const { storeDetail} = props;
|
||||
const { owner , projectsId } = props.match.params;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [visibleNew, setVisibleNew] = useState(false);
|
||||
const [createJobBy, setCreateJobBy] = useState("Github");
|
||||
const [branchOptions, setBranchOptions] = useState([]);
|
||||
|
||||
useEffect(()=>{
|
||||
axios.get(`/${owner}/${projectsId}/pulls/get_branches.json`, {}).then(res=>{
|
||||
res && setBranchOptions(res.data);
|
||||
})
|
||||
}, [])
|
||||
|
||||
// 取消仓库绑定点击函数
|
||||
function updateStore(){
|
||||
axios.delete(`/${owner}/${projectsId}/synchronizes/delete.json`).then(res=>{
|
||||
if(res && res.data.message === "success"){
|
||||
message.success('取消绑定成功');
|
||||
setVisible(false);
|
||||
props.history.push(`/${owner}/${projectsId}/server`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return <div className="storeListBox">
|
||||
<div className="headBox font-16 pl15">Reposyncer仓库同步系统
|
||||
{storeDetail !== null && <Tooltip title="Reposyncer仓库同步系统提供跨托管平台的项目协同开发同步功能。支持用户在任何一个托管平台上的代码提交、代码推送、合并请求等操作自动同步至其他托管平台。不仅增加每个开源项目与开发者的流量,也使不同平台的开源项目维护与更新变得方便与快捷" overlayStyle={{width: 400}}><span className="helpBox1 font-12 ml10">?</span></Tooltip>}
|
||||
</div>
|
||||
{/* 空数据 */}
|
||||
{!storeDetail && <div className="nullStoreBox mt25">
|
||||
<img src={logo} alt="" className="loBox mt50"/>
|
||||
<p className="font-22 mt10">欢迎使用跨平台代码同步系统</p>
|
||||
<div className="introBox font-15">跨平台代码同步系统提供跨托管平台的项目协同开发同步功能。支持用户在任何一个托管平台上的代码提交、代码推送、合并请求等操作自动同步至其他托管平台。不仅增加每个开源项目与开发者的流量,也使不同平台的开源项目维护与更新变得方便与快捷</div>
|
||||
<div className="borBox"></div>
|
||||
<Button type="primary" style={{width: '112px', height: '36px'}}><Link to={`/${owner}/${projectsId}/server/reposyncer/store/edit`}>开始体验</Link></Button>
|
||||
</div>}
|
||||
{/* 已绑定仓库信息 */}
|
||||
{storeDetail && <div className="listStore mt20">
|
||||
<div className="storeTitle pb5 mb5 font-18">已绑定仓库地址<Icon type="exclamation-circle" style={{color: '#466aff'}} className="ml10 font-14"/><span className="ml5 font-14" style={{fontWeight: 'normal'}}>对已绑定的仓库,请添加同步分支实现分支的跨平台双向同步</span></div>
|
||||
{storeDetail && storeDetail.github_address && <div className="showStoreInfo dashedBor">
|
||||
<div className="storeInfoBox">
|
||||
<div className="font-15 sTil"><img src={gitHub1} alt="" className="mr10"/><span>Github仓库地址</span></div>
|
||||
<span>{storeDetail && storeDetail.github_address}</span>
|
||||
</div>
|
||||
<div>
|
||||
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but mr20"><Link to={`/${owner}/${projectsId}/server/reposyncer/record/github`}>查看同步分支</Link></Button>
|
||||
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but" onClick={()=>{setVisibleNew(true);setCreateJobBy('Github')}}>新建同步分支</Button>
|
||||
</div>
|
||||
</div>}
|
||||
{storeDetail && storeDetail.gitee_address && <div className="showStoreInfo">
|
||||
<div className="storeInfoBox">
|
||||
<div className="font-15 sTil"><img src={gitee1} alt="" className="mr10"/><span>Gitee仓库地址</span></div>
|
||||
<span>{storeDetail.gitee_address}</span>
|
||||
</div>
|
||||
<div>
|
||||
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but mr20"><Link to={`/${owner}/${projectsId}/server/reposyncer/record/gitee`}>查看同步分支</Link></Button>
|
||||
<Button style={{width: '94px', height:'36px', padding: 0}} className="blue_border_but" onClick={()=>{setVisibleNew(true);setCreateJobBy('Gitee')}}>新建同步分支</Button>
|
||||
</div>
|
||||
</div>}
|
||||
{/* <Button type="primary" style={{width: '134px', height:'36px', padding: 0}} className="mt30"><Link to={`/${owner}/${projectsId}/server/reposyncer/store/edit`}>更改绑定仓库信息</Link></Button> */}
|
||||
<Button style={{width: '134px', height:'36px'}} className="red_border_but mt40" onClick={()=>{setVisible(true)}}>清空仓库绑定</Button>
|
||||
</div>}
|
||||
<Modal
|
||||
title="取消绑定"
|
||||
visible={visible}
|
||||
onCancel={()=>{setVisible(false)}}
|
||||
footer={<div><Button style={{width: '90px', height:'36px'}} onClick={()=>{setVisible(false)}}>取消</Button><Button className="okBut" style={{width: '90px', height:'36px'}} onClick={updateStore}>确认清空</Button></div>}
|
||||
width={535}
|
||||
className="cancelBound"
|
||||
>
|
||||
<div className="bTilModal font-16"><span className="errorRedSpan font-18 mr20 mt20 ml15">!</span>您确定要清空已绑定仓库?</div>
|
||||
<div className="sTilModal">此操作将清空所有绑定仓库/绑定分支及同步日志,请谨慎操作</div>
|
||||
</Modal>
|
||||
<CreateJobModal owner={owner} projectsId={projectsId} visible={visibleNew} setVisible={setVisibleNew} createJobBy={createJobBy} branchOptions={branchOptions}/>
|
||||
</div>
|
||||
}
|
||||
export default StoreList;
|
|
@ -0,0 +1,64 @@
|
|||
import React, {useState, useEffect} from "react";
|
||||
import { Button, Tooltip } from "antd";
|
||||
import './index.scss';
|
||||
import logo from '../img/logo2.png';
|
||||
import Loadable from "react-loadable";
|
||||
import { Route, Switch } from "react-router";
|
||||
import Loading from "../../../Loading";
|
||||
import { Link } from "react-router-dom";
|
||||
import axios from 'axios';
|
||||
|
||||
// 编辑同步仓库页面
|
||||
const EditStore = Loadable({
|
||||
loader: () => import("./component/editStore"),
|
||||
loading: Loading,
|
||||
});
|
||||
// 同步仓库页面
|
||||
const StoreList = Loadable({
|
||||
loader: () => import("./component/storeList"),
|
||||
loading: Loading,
|
||||
});
|
||||
// 跨平台代码同步服务-同步记录页面
|
||||
const RecordList = Loadable({
|
||||
loader: () => import("./component/recordList"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
function Reposyncer(propsF){
|
||||
const { owner , projectsId } = propsF.match.params;
|
||||
|
||||
const [storeDetail, setStoreDetail] = useState(null);
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取仓库同步详情
|
||||
axios.get(`/${owner}/${projectsId}/synchronizes.json`).then(res=>{
|
||||
if(res && res.data.message === "success"){
|
||||
setStoreDetail(res.data.data);
|
||||
}
|
||||
})
|
||||
},[])
|
||||
|
||||
return <div className="reposyncerBox">
|
||||
<Switch {...propsF}>
|
||||
<Route
|
||||
path="/:owner/:projectsId/server/reposyncer/record/:type"
|
||||
render={(props) => (
|
||||
<RecordList {...propsF} {...props} storeDetail={storeDetail}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:owner/:projectsId/server/reposyncer/store/edit"
|
||||
render={(props) => (
|
||||
<EditStore {...propsF} {...props}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/:owner/:projectsId/server/reposyncer"
|
||||
render={(props) => (
|
||||
<StoreList {...propsF} {...props} storeDetail={storeDetail}/>
|
||||
)}
|
||||
></Route>
|
||||
</Switch>
|
||||
</div>
|
||||
}
|
||||
export default Reposyncer;
|
|
@ -0,0 +1,295 @@
|
|||
.red_border_but, .red_border_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;
|
||||
}
|
||||
}
|
||||
.blue_border_but, .blue_border_but:focus{
|
||||
color:$primary-color;
|
||||
background-color:rgba(70, 106, 255, 0.09);
|
||||
border:1px solid #1a47ff;
|
||||
border-radius:5px;
|
||||
&:hover{
|
||||
color:#6684fe;
|
||||
background-color:rgba(70, 106, 255, 0.09);;
|
||||
border-color:#6684fe;
|
||||
}
|
||||
&:active{
|
||||
color:#1a47ff;
|
||||
background-color:rgba(70, 106, 255, 0.09);
|
||||
border-color:#1a47ff;
|
||||
}
|
||||
}
|
||||
.reposyncerBox .headBox, .storeListBox .headBox{
|
||||
height:60px;
|
||||
line-height: 60px;
|
||||
background-color:#fafcff;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
border-radius:3px 3px 0px 0px;
|
||||
color:#333333;
|
||||
position: relative;
|
||||
}
|
||||
.reposyncerBox{
|
||||
padding-bottom: 60px;
|
||||
.helpBox1{
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
background-color: #466aff;
|
||||
line-height: normal;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
}
|
||||
.nullStoreBox{
|
||||
background-color:#fafcff;
|
||||
border-radius:4px 4px 0px 0px;
|
||||
text-align: center;
|
||||
color:#333333;
|
||||
padding-bottom: 65px;
|
||||
.loBox{width: 68px;}
|
||||
.introBox{
|
||||
color:#666666;
|
||||
width: 57%;
|
||||
margin: 15px auto;
|
||||
}
|
||||
.borBox{
|
||||
width: 45%;
|
||||
margin: 0 auto 20px;
|
||||
border-bottom: 1px solid rgba(90, 117, 193, 0.23);
|
||||
}
|
||||
}
|
||||
.rightContentBox{
|
||||
display: flex;
|
||||
.leftNav{
|
||||
width: 185px;
|
||||
margin-right: 36px;
|
||||
background-size: 100% 100%;
|
||||
background-image: url('../img/bg1.png');
|
||||
.oneBox{
|
||||
padding: 15px;
|
||||
color:#4c5b76;
|
||||
display: block;
|
||||
&.active{
|
||||
position: relative;
|
||||
background-color:rgba(70, 106, 255, 0.06);
|
||||
color: $primary-color;
|
||||
&::before{
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
height: 86%;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
background-color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.storeListBox{
|
||||
.storeListTable{
|
||||
width: 100%;
|
||||
}
|
||||
.blueSpan{
|
||||
color: $primary-color;
|
||||
}
|
||||
.createJobBut{
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
right: 40px;
|
||||
}
|
||||
.storeTitle{
|
||||
font-weight:700;
|
||||
color:#151d40;
|
||||
border-bottom: 1px solid #e0e6f5;;
|
||||
}
|
||||
.registerBox{
|
||||
width: 85%;
|
||||
.storeLogo{
|
||||
width: 75px;
|
||||
}
|
||||
.storeFormItem{
|
||||
width: 70%;
|
||||
}
|
||||
.has-error .ant-input, .has-error .ant-input:hover, .has-error .storeInput .ant-input{
|
||||
border-color:#f60011;
|
||||
}
|
||||
#gitHubToken, #giteeToken, .storeInput{
|
||||
border-color: #9eaacb;
|
||||
height: 36px;
|
||||
&:hover{border-color: $primary-color;}
|
||||
}
|
||||
.storeInput .ant-input-group-addon, .storeInput .ant-input{
|
||||
border-color: #9eaacb;
|
||||
}
|
||||
.tipStoreBox{
|
||||
color: #4c5b76;
|
||||
line-height: 30px;
|
||||
padding: 10px 0 30px;
|
||||
}
|
||||
}
|
||||
.listStore{
|
||||
.showStoreInfo{
|
||||
padding: 20px 0 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
&.dashedBor{
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
}
|
||||
}
|
||||
.linkSpan{
|
||||
color:$primary-color;
|
||||
&:hover{color: $primary-color-hover;}
|
||||
}
|
||||
.storeInfoBox{color: #666;}
|
||||
.sTil{
|
||||
color:#202d40;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
.expandBox{
|
||||
background-color: #171b23;
|
||||
color: white;
|
||||
margin: -16px;
|
||||
padding-bottom: 16px;
|
||||
.expandCont{
|
||||
word-break: break-all;
|
||||
padding: 16px 16px 0;
|
||||
}
|
||||
}
|
||||
th.recordColumns{
|
||||
background-color: rgba(90, 117, 193, 0);
|
||||
.ant-table-column-title{
|
||||
font-family:PingFang SC;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color:#202d40;
|
||||
}
|
||||
}
|
||||
.ant-table-tbody > tr > td.recordColumns{
|
||||
color: #333;
|
||||
font-size: 15px;
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
&.primaryColor{color: $primary-color;}
|
||||
}
|
||||
tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) td.recordColumns{
|
||||
background-color:#f8f9ff !important;
|
||||
}
|
||||
.statusBox{
|
||||
width:58px;
|
||||
height:32px;
|
||||
border-radius:4px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
&.errorBox{
|
||||
color:#ff0c0c;
|
||||
background-color:rgba(230, 0, 6, 0.1);
|
||||
border:1px solid #fcb6c2;;
|
||||
}
|
||||
&.accomplish{
|
||||
color:#009c44;
|
||||
background-color:rgba(83, 255, 163, 0.1);
|
||||
border:1px solid #00b843;;
|
||||
}
|
||||
}
|
||||
.deleteRecord{
|
||||
color:#f60011;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
&::before{
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 5px;
|
||||
width: 1px;
|
||||
height: 9px;
|
||||
background-color: #dfdfdf;;
|
||||
}
|
||||
}
|
||||
.primaryColor{color: $primary-color;}
|
||||
}
|
||||
.cancelBound{
|
||||
.ant-modal-header{
|
||||
padding: 10px 25px;
|
||||
background-color: #f8f8f8;
|
||||
.ant-modal-title{
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
}
|
||||
.ant-modal-close{
|
||||
top: 0 !important;
|
||||
.ant-modal-close-x{font-size: 22px;}
|
||||
}
|
||||
.bTilModal{
|
||||
color:#333333;
|
||||
.errorRedSpan{
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
background-color:#ca0002;
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.sTilModal{
|
||||
color:#666666;
|
||||
margin: 30px 0 40px 70px;
|
||||
}
|
||||
.ant-modal-footer{
|
||||
border-top: none;
|
||||
text-align: center;
|
||||
padding-bottom: 50px;
|
||||
.okBut, .okBut:focus{
|
||||
color:#df0002;
|
||||
border-color: #d9d9d9;
|
||||
margin-left: 43px;
|
||||
&:hover{
|
||||
border-color:#ff727c;
|
||||
}
|
||||
&:active{
|
||||
border-color:#f60011;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.createJobBox{
|
||||
.itemBox{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
.errorBox{
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
left: 130px;
|
||||
color:#f60011;
|
||||
}
|
||||
}
|
||||
.labelBox{
|
||||
width: 125px;
|
||||
.icon-a-bitian2x{color: #fe1010;}
|
||||
}
|
||||
.inputBox{
|
||||
width: 80%;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
|||
import { Banner , AlignCenter } from '../../Component/layout';
|
||||
import DeleteBox from '../../Component/DeleteModal/Index';
|
||||
import './Index.scss';
|
||||
import { Button , List, Pagination, Modal, Tooltip } from 'antd';
|
||||
import { Button , List, Pagination, Modal, Tooltip, Popover } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
const limit = 15;
|
||||
|
@ -118,10 +118,14 @@ function Index(props) {
|
|||
<List.Item key={k}>
|
||||
<i className={`iconfont mr12 font-17 ${i.isJianMu ? 'icon-gongzuoliuicon color-grey-6' : 'icon-a-xuanzhongwebhookicon color-grey-d'}`}></i>
|
||||
<span className="webName">{i.isJianMu ? <Tooltip title="该Webhook由流水线创建,请勿编辑或删除以防流水线失效"><span className="webName spanBox">{i.url}</span></Tooltip> : i.url}</span>
|
||||
<span>
|
||||
{!i.isJianMu && <span>
|
||||
<Button ghost type={"primary"} onClick={()=>{gotoEditWebhook(i)}}>编辑</Button>
|
||||
<Button ghost className="ml20" type="danger" onClick={()=>{deleteFunc(i)}}>删除</Button>
|
||||
</span>
|
||||
</span>}
|
||||
{i.isJianMu && <span>
|
||||
<Popover content={"该Webhook由流水线创建, 编辑此Webhook后, 将大概率导致本仓库相关流水线失效。请在流水线中对该Webhook配置进行更改"} title="无法编辑此Webhook" overlayClassName="disabledButPopover"><Button type={"primary"} onClick={()=>{gotoEditWebhook(i)}} disabled>编辑</Button></Popover>
|
||||
<Popover content={"该Webhook由流水线创建, 删除此Webhook后, 将导致本仓库相关流水线失效。请在流水线中删除该Webhook"} title="无法删除此Webhook" overlayClassName="disabledButPopover"><Button className="ml20" type="danger" onClick={()=>{deleteFunc(i)}} disabled>删除</Button></Popover>
|
||||
</span>}
|
||||
</List.Item>
|
||||
)
|
||||
})}
|
||||
|
|
|
@ -177,4 +177,7 @@
|
|||
}
|
||||
.editWebhookModalTitle{
|
||||
justify-content: center;
|
||||
}
|
||||
.disabledButPopover{
|
||||
width: 300px;
|
||||
}
|
|
@ -141,4 +141,56 @@ export function getPassList(params) {
|
|||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 中期考核-查看中期考核信息(返回导师评分)
|
||||
export function getMediumTermExamineInfo(taskId) {
|
||||
return fetch({
|
||||
url: `/api/mediumTermExamineMaterial/getMediumTermExamineInfo/${taskId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 中期考核-查看中期考核信息(不返回返回导师评分)
|
||||
export function getBriefMediumTermExamineMaterial(taskId) {
|
||||
return fetch({
|
||||
url: `/api/mediumTermExamineMaterial/getBriefMediumTermExamineMaterial/${taskId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 中期考核-提交中期考核信息
|
||||
export function submitMedium(data) {
|
||||
return fetch({
|
||||
url: `/api/mediumTermExamineMaterial/create`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 中期考核-导师提交中期考核评分
|
||||
export function submitTutorEvaluation(data) {
|
||||
return fetch({
|
||||
url: `/api/tutorEvaluation/create`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 中期考核-导师更新中期考核评分
|
||||
export function updateTutorEvaluation(data) {
|
||||
return fetch({
|
||||
url: `/api/tutorEvaluation/update`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 中期考核结果list
|
||||
export function getMediumTermExamineInfoList(params) {
|
||||
return fetch({
|
||||
url: `/api/mediumTermExamineMaterial/getMediumTermExamineInfoList`,
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
|
@ -11,13 +11,15 @@ import banner from "../img/banner.png";
|
|||
import introduce from "../img/introduce.png";
|
||||
import apply1 from "../img/apply1.png";
|
||||
import apply2 from "../img/apply2.png";
|
||||
import img1 from "../img/img1.png";
|
||||
import img2 from "../img/img2.png";
|
||||
import teacher from "../img/teacher.png";
|
||||
import logo from "../img/openmmlab/logo1.png";
|
||||
import { hasAuditRole } from '../api';
|
||||
import './index.scss';
|
||||
|
||||
export default (props) => {
|
||||
const { current_user, isGlccApplyDate, showNotification, studentApplyStart, history, secondStudentApplyDate, isStudentApplyDate } = props;
|
||||
const { current_user, isGlccApplyDate, showNotification, studentApplyStart, history, secondStudentApplyDate, isStudentApplyDate, checkedTaskId, checkTime1, checkTime2, checkTime3 } = props;
|
||||
// function goToApply() {
|
||||
// if (isGlccApplyDate) {
|
||||
// if (current_user && current_user.login) {
|
||||
|
@ -31,9 +33,9 @@ export default (props) => {
|
|||
// }
|
||||
// }
|
||||
|
||||
const [hasRole, setHhasRole] = useState(false);
|
||||
const [hasRole, setHasRole] = useState(false);
|
||||
const resultTime1 = new Date().getTime() > new Date('2022-06-28 1:0').getTime() && new Date().getTime() < new Date('2022-07-01 0:0').getTime();
|
||||
const resultTime2 = new Date().getTime() > new Date('2022-07-01 12:0').getTime();
|
||||
const resultTime2 = new Date().getTime() > new Date('2022-07-01 12:0').getTime() && new Date().getTime() < new Date('2022-08-12 0:0').getTime();
|
||||
|
||||
useEffect(() => {
|
||||
if (!current_user.user_id) {
|
||||
|
@ -41,7 +43,7 @@ export default (props) => {
|
|||
}
|
||||
hasAuditRole({ userId: current_user.user_id }).then(res => {
|
||||
if (res && res.message == 'success' && res.data.hasRole) {
|
||||
setHhasRole(true);
|
||||
setHasRole(true);
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
@ -85,6 +87,32 @@ export default (props) => {
|
|||
</div>
|
||||
<div className="pt6">查看各课题入选学生名单</div>
|
||||
</div>}
|
||||
|
||||
{/* 学生中期考核 */}
|
||||
{!hasRole && checkTime1 && checkedTaskId && <Link className="apply" to={`/glcc/middle/submit`}>
|
||||
<div>
|
||||
<img src={img1} alt="" className="applyIcon" />
|
||||
<span className="til">中期考核</span>
|
||||
</div>
|
||||
<div className="pt6">学生提交考核材料</div>
|
||||
</Link>}
|
||||
{/* 导师中期考核 */}
|
||||
{hasRole && checkTime2 && <Link className="apply" to={`/glcc/middle/examination`}>
|
||||
<div>
|
||||
<img src={img1} alt="" className="applyIcon" />
|
||||
<span className="til">中期考核</span>
|
||||
</div>
|
||||
<div className="pt6">导师拟定中期考核结果</div>
|
||||
</Link>}
|
||||
{/* 中期考核结果公示页 */}
|
||||
{checkTime3 && <Link className="apply" to={`/glcc/middle/result`}>
|
||||
<div>
|
||||
<img src={img1} alt="" className="applyIcon" />
|
||||
<span className="til">考核结果</span>
|
||||
</div>
|
||||
<div className="pt6">中期课题考核结果公示</div>
|
||||
</Link>}
|
||||
|
||||
{/* 项目报名 */}
|
||||
<Link to="/glcc/projects" className="apply project" >
|
||||
<div>
|
||||
|
@ -102,7 +130,7 @@ export default (props) => {
|
|||
</div>
|
||||
<div className="pt6">选择课题,开启您的开源之旅</div>
|
||||
</div> */}
|
||||
|
||||
|
||||
{/* 导师审核 */}
|
||||
{/* {hasRole && new Date().getTime() < new Date('2022-07-01 0:0').getTime() && <div className="apply" onClick={goToCheck}>
|
||||
<div>
|
||||
|
|
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.7 MiB |
|
@ -61,6 +61,21 @@ const Result = Loadable({
|
|||
loader: () => import("./checkResult"),
|
||||
loading: Loading,
|
||||
});
|
||||
// 中期考核-学生
|
||||
const StudentSubmit = Loadable({
|
||||
loader: () => import("./interimReview/studentSubmit"),
|
||||
loading: Loading,
|
||||
});
|
||||
// 中期考核-导师
|
||||
const TutorReview = Loadable({
|
||||
loader: () => import("./interimReview/tutorReview"),
|
||||
loading: Loading,
|
||||
});
|
||||
// 中期考核-结果公示
|
||||
const MiddleResult = Loadable({
|
||||
loader: () => import("./interimReview/result"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Glcc = (propsF) => {
|
||||
const {current_user, showLoginDialog} = propsF;
|
||||
// 判断时间是否在开源夏令营报名时间内(4月15日~5月20日)
|
||||
|
@ -70,6 +85,12 @@ const Glcc = (propsF) => {
|
|||
const studentApplyStart = new Date().getTime() > new Date('2022-05-26').getTime();
|
||||
const isStudentApplyDate = new Date().getTime() > new Date('2022-05-26').getTime() && new Date().getTime() < new Date('2022-06-25 0:0').getTime();
|
||||
const secondStudentApplyDate = new Date().getTime() > new Date('2022-06-29 1:0').getTime() && new Date().getTime() < new Date('2022-06-30 0:0').getTime();
|
||||
// 学生中期考核入口可见时间
|
||||
const checkTime1 = new Date().getTime() > new Date('2022-08-12 8:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime();
|
||||
// 导师中期考核时间
|
||||
const checkTime2 = new Date().getTime() > new Date('2022-08-12 10:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime();
|
||||
// 中期考核结果公示8月26日0点
|
||||
const checkTime3 = new Date().getTime() > new Date('2022-08-26 0:0').getTime();
|
||||
|
||||
// 用户已报名课题id数组
|
||||
const [applyTaskId, setApplyTaskId] = useState({});
|
||||
|
@ -78,6 +99,9 @@ const Glcc = (propsF) => {
|
|||
const [cancelCount,setCancelCount]=useState(0);
|
||||
// 用户是否已经被导师选中
|
||||
const [lockedTaskName, setLockedTaskName] = useState(undefined);
|
||||
// 用户与导师双向选定的课题id
|
||||
const [checkedTaskId, setCheckedTaskId] = useState(undefined);
|
||||
const [studentRegId, setStudentRegId] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取用户课题报名信息current_user user_id
|
||||
|
@ -88,6 +112,8 @@ const Glcc = (propsF) => {
|
|||
response.data && response.data.registrationStudentTaskList.map(item=>{
|
||||
data[item.taskId] = item.id;
|
||||
item.locked && setLockedTaskName(item.taskName);
|
||||
item.passStatus && setCheckedTaskId(item.taskId);
|
||||
item.passStatus && setStudentRegId(item.studentRegId);
|
||||
})
|
||||
setApplyTaskId(data);
|
||||
response.data&&setCancelCount(Number(response.data.cancelCount));
|
||||
|
@ -183,11 +209,35 @@ const Glcc = (propsF) => {
|
|||
)}
|
||||
></Route>
|
||||
|
||||
{/* 中期审核-结果公示 */}
|
||||
<Route
|
||||
path="/glcc/middle/result"
|
||||
render={(props) => (
|
||||
<MiddleResult current_user={current_user} history={props.history} checkTime3={checkTime3}/>
|
||||
)}
|
||||
></Route>
|
||||
|
||||
{/* 中期审核-学生 */}
|
||||
<Route
|
||||
path="/glcc/middle/submit"
|
||||
render={(props) => (
|
||||
<StudentSubmit current_user={current_user} history={props.history} checkedTaskId={checkedTaskId} checkTime1={checkTime1} studentRegId={studentRegId}/>
|
||||
)}
|
||||
></Route>
|
||||
|
||||
{/* 中期审核-导师 */}
|
||||
<Route
|
||||
path="/glcc/middle/examination"
|
||||
render={(props) => (
|
||||
<TutorReview current_user={current_user} history={props.history} checkTime2={checkTime2}/>
|
||||
)}
|
||||
></Route>
|
||||
|
||||
{/* 首页 */}
|
||||
<Route
|
||||
path="/glcc"
|
||||
render={(props) => (
|
||||
<Home {...propsF} {...props} studentApplyStart={studentApplyStart} isStudentApplyDate={isStudentApplyDate} secondStudentApplyDate={secondStudentApplyDate}/>
|
||||
<Home {...propsF} {...props} studentApplyStart={studentApplyStart} isStudentApplyDate={isStudentApplyDate} secondStudentApplyDate={secondStudentApplyDate} checkedTaskId={checkedTaskId} checkTime1={checkTime1} checkTime2={checkTime2} checkTime3={checkTime3}/>
|
||||
)}
|
||||
></Route>
|
||||
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
.interimBox{
|
||||
background-image:linear-gradient(180deg,#ebf2ff 0%,#ebf2ff 43.09%,#f3f4f8 100%);
|
||||
padding-bottom: 125px;
|
||||
position: relative;
|
||||
.bg1, .bg2{
|
||||
width: 150px;
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
left: 6%;
|
||||
}
|
||||
.bg2{
|
||||
top: auto;
|
||||
left: auto;
|
||||
right: 20%;
|
||||
bottom: 0;
|
||||
}
|
||||
&.glcc-check .bg1{top: 36%;}
|
||||
.bannerInterim{
|
||||
width: 100%;
|
||||
}
|
||||
.navBox{
|
||||
padding: 20px 0 10px;
|
||||
color:#202d40;
|
||||
border-bottom: 1px dashed #bec5d5;
|
||||
.linkBox{
|
||||
color:#a4aabb;
|
||||
&:hover{color: $primary-color;}
|
||||
}
|
||||
}
|
||||
.mainBox{
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
.tipBox{
|
||||
padding: 12px 20px;
|
||||
color:#6c7283;
|
||||
line-height: 2.5;
|
||||
background-color:#e4edff;
|
||||
.spanBox{color: #000;}
|
||||
div{line-height: 1.8;margin-bottom: 10px;}
|
||||
.blueSpan{
|
||||
color: $primary-color;
|
||||
&:hover{color: $primary-color-hover;}
|
||||
}
|
||||
}
|
||||
.titleBox{color: #333;}
|
||||
.referBox{
|
||||
margin-top: 25px;
|
||||
background-color:rgba(255, 255, 255, 0.27);
|
||||
border:1px solid #ffffff;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
padding: 30px 0;
|
||||
position: relative;
|
||||
.referItem{
|
||||
width: 47%;
|
||||
display: flex;
|
||||
.ant-form-item-control-wrapper{flex-grow: 1;}
|
||||
&.oneCont{
|
||||
width: 97%;
|
||||
.ant-upload-list-item{width: 30%;}
|
||||
.ant-upload-list-item:hover .ant-upload-list-item-info{background: none;}
|
||||
}
|
||||
.ant-input, .uploadBox{
|
||||
border-color:#b3c3db;
|
||||
background: none !important;
|
||||
}
|
||||
.contentBox{
|
||||
padding: 10px 11px 20px
|
||||
}
|
||||
}
|
||||
&.tutor{
|
||||
border: none;
|
||||
margin-top: -20px;
|
||||
background: none;
|
||||
.remarkBox{
|
||||
position: relative;
|
||||
flex: 0.97;
|
||||
.has-error .ant-form-explain{
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
.oneCont{display: block;}
|
||||
.wordNum{
|
||||
position: absolute;
|
||||
right: 55px;
|
||||
bottom: 10px;
|
||||
}
|
||||
.referItem{margin-bottom: 5px;}
|
||||
}
|
||||
}
|
||||
.tutorContent{
|
||||
border: 1px solid #fff;
|
||||
margin-top: 40px;
|
||||
background-color: #f1f6ff;
|
||||
.task-tabs {
|
||||
margin-top: 0px;
|
||||
border: none;
|
||||
background: none;
|
||||
.task-title-stuName{
|
||||
font-weight:700;
|
||||
color:#333;
|
||||
padding-bottom: 25px;
|
||||
border-bottom:1px dashed #bec5d5;
|
||||
}
|
||||
}
|
||||
}
|
||||
.reviewBox{
|
||||
.successReviewBox{
|
||||
color:#2dab4d;
|
||||
background-color:rgba(82, 206, 152, 0.11);
|
||||
border:1px solid rgba(36, 167, 67, 0.61);
|
||||
margin: -5px 20px 20px;
|
||||
padding: 6px 15px;
|
||||
}
|
||||
.title{
|
||||
color:#333333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.blueBox{
|
||||
display: inline-block;
|
||||
width:5px;
|
||||
height:14px;
|
||||
background-color:#466aff;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.flexBox{
|
||||
color:#202d40;
|
||||
border-bottom: 1px dashed #bec5d5;
|
||||
div{
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-bottom: 10px;
|
||||
.blueSpan{
|
||||
color: $primary-color;
|
||||
word-break: break-all;
|
||||
display: block;
|
||||
flex: 1;
|
||||
}
|
||||
.pptAttachment{
|
||||
color: $primary-color;
|
||||
max-width: 80%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.resultBox{
|
||||
padding: 30px 20px 50px 20px;
|
||||
.nullDateTip{color: #ff3838;}
|
||||
&.nullData .flexBox div{align-items: center;}
|
||||
.blueBg{
|
||||
width: 85%;
|
||||
min-height: 36px;
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
background-color:rgba(70, 106, 255, 0.05);
|
||||
border-radius:4px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.mustSpan{
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
&::before{
|
||||
content: '*';
|
||||
color: #ff3838;
|
||||
font-size: 18px;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 3px;
|
||||
}
|
||||
&.ppt::before{
|
||||
top: -2px;
|
||||
left: 12px;
|
||||
}
|
||||
}
|
||||
.flexBox{
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
.tutorRes{
|
||||
width: 100%;
|
||||
background-color:rgba(107, 136, 255, 0.05);
|
||||
border-radius:4px;
|
||||
color:#6c7283;
|
||||
padding: 16px 30px 30px 20px;
|
||||
.smallTil{
|
||||
color: #202d40;
|
||||
}
|
||||
.passStatusBox{
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width:59px;
|
||||
height:28px;
|
||||
color:#d2001d;
|
||||
background-color:rgba(251, 0, 34, 0.06);
|
||||
border:1px solid #df001f;
|
||||
border-radius:6px;
|
||||
&.pass{
|
||||
color:#2dab4d;
|
||||
background-color:rgba(82, 206, 152, 0.11);
|
||||
border-color:rgba(36, 167, 67, 0.61);
|
||||
}
|
||||
}
|
||||
.blueSpan{color: $primary-color;}
|
||||
}
|
||||
.tutorRemark{
|
||||
word-break: break-all;
|
||||
flex: 1;
|
||||
color:#6c7283;
|
||||
}
|
||||
}
|
||||
}
|
||||
.interimBox.resultListBox{
|
||||
.searchBox{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.resultListTable th.columnsResult.actionBox .ant-table-column-title{
|
||||
visibility: visible;
|
||||
}
|
||||
.projectDetailBox.nodata{
|
||||
margin: 0 auto;
|
||||
}
|
||||
.resultListTable .ant-table{
|
||||
border: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Input, Table, Tooltip, Checkbox } from 'antd';
|
||||
import { getMediumTermExamineInfoList } from '../api';
|
||||
import ProjectDetail from '../project/component/projectDetail';
|
||||
import resultBanner from "../img/resultBanner3.png";
|
||||
import bgPng from "../img/bgPng.png";
|
||||
import './index.scss';
|
||||
import '../checkResult/index.scss';
|
||||
import '../project/taskList/index.scss';
|
||||
import '../project/index.scss';
|
||||
const { Search } = Input;
|
||||
|
||||
// 课题列表
|
||||
function CheckResult({current_user, history, checkTime3}) {
|
||||
// 输入搜索框
|
||||
const [keyword, setKeyword] = useState(undefined);
|
||||
const [data, setData] = useState([]);
|
||||
// table
|
||||
const [current, setCurrent] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
|
||||
|
||||
useEffect(()=>{
|
||||
// 未到时间
|
||||
if(!checkTime3){
|
||||
history.push('/glcc');
|
||||
}
|
||||
},[])
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
setExpandedRowKeys([]);
|
||||
const params = {
|
||||
curPage: current,
|
||||
keyword,
|
||||
pageSize
|
||||
}
|
||||
getMediumTermExamineInfoList(params).then(response => {
|
||||
if (response && response.message === "success") {
|
||||
response.data.rows.map((item, index)=>{
|
||||
item.id = (current-1)*pageSize+index + 1;
|
||||
})
|
||||
setData(response.data.rows);
|
||||
setTotal(response.data.total);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
}, [keyword, current, pageSize])
|
||||
|
||||
const columns = [
|
||||
{ title: '序号', dataIndex: 'index', align: 'center', className:"columnsResult", width: '6%', render: (text, item, index) => <span>{(current-1)*pageSize+index + 1}</span> },
|
||||
{ title: '入选学生', dataIndex: 'studentName', className:"columnsResult taskName", width: '10%', ellipsis: true},
|
||||
{ title: '课题导师', dataIndex: 'tutorName', className:"columnsResult", width: '10%', ellipsis: true},
|
||||
{ title: '课题名称', dataIndex: 'taskName', className:"columnsResult", width: '20%', ellipsis: true, render: (text, item) => <Tooltip title={text} placement="topLeft"><span className='toolTipSpan link' onClick={()=>{window.open(`/glcc/subjects/detail/${item.taskId}`)}}>{text}</span></Tooltip> },
|
||||
{ title: '项目名称', dataIndex: 'projectName', className:"columnsResult", ellipsis: true, width: '14%', render: (text) => <Tooltip title={text} placement="topLeft"><span className='toolTipSpan'>{text}</span></Tooltip> },
|
||||
{ title: '项目简介', dataIndex: 'introduce', className:"columnsResult", width: '13%', ellipsis: true},
|
||||
{ title: '答辩视频', dataIndex: 'defenceVideoUrl', className:"columnsResult", width: '15%', ellipsis: true, render: (text, item) => <Tooltip title={text} placement="topLeft"><span className='toolTipSpan link' onClick={()=>{window.open(`/glcc/subjects/detail/${item.taskId}`)}}>{text}</span></Tooltip>},
|
||||
{ title: '考核结果', dataIndex: 'totalityEvaluation', align: 'center', className:"columnsResult actionBox", render: (text, item, index) => <span>{text === 'D' || !text ? '未通过' : '通过'}</span>},
|
||||
]
|
||||
|
||||
const customExpandIcon = (props) => {
|
||||
if (props.expanded) {
|
||||
return <a className='toolTipSpan link' style={{marginRight: 8 }} onClick={e => {
|
||||
props.onExpand(props.record, e);
|
||||
}}><i className='iconfont icon-ketixiangqingicon mr5'></i>项目简介<i className="iconfont icon-changyongtubiao-xianxingdaochu-zhuanqu- font-12 ml5 down mr10"></i></a>
|
||||
} else {
|
||||
return <a className='toolTipSpan link' style={{marginRight: 8 }} onClick={e => {
|
||||
props.onExpand(props.record, e);
|
||||
}}><i className='iconfont icon-ketixiangqingicon mr5'></i>项目简介<i className="iconfont icon-jiantou9 font-12 ml5 down mr10"></i></a>
|
||||
}
|
||||
}
|
||||
|
||||
const expandRow = (record) => {
|
||||
return <ProjectDetail detail={null} projectId={record.projectIntro} showTask={false}/>
|
||||
}
|
||||
|
||||
// 展开收起行回调
|
||||
function onExpand(expanded, record){
|
||||
const keys = new Set(expandedRowKeys);
|
||||
if(expanded){
|
||||
keys.add(record.id);
|
||||
}else{
|
||||
keys.delete(record.id);
|
||||
}
|
||||
setExpandedRowKeys(Array.from(keys));
|
||||
}
|
||||
|
||||
// 改变pagesize
|
||||
function onShowSizeChange(current, pageSize){
|
||||
window.scrollTo(0, 0);
|
||||
setCurrent(1);
|
||||
setPageSize(pageSize);
|
||||
}
|
||||
|
||||
// 切换页数
|
||||
function changePage(page, pageSize){
|
||||
window.scrollTo(0, 0);
|
||||
setCurrent(page);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="interimBox taskList resultListBox">
|
||||
<img className="bannerInterim" src={resultBanner} alt=""></img>
|
||||
<div className='bgBox'>
|
||||
<div className="resultList">
|
||||
<div className='goBackBox'><a href='/glcc'>开源夏令营 / </a>中期课题考核结果公示</div>
|
||||
<div className='searchBox'>
|
||||
<Search className='search' placeholder='请输入学生姓名或课题名称进行搜索' allowClear enterButton onSearch={(value) => { setCurrent(1); setKeyword(value) }} />
|
||||
<div style={{width: 100}}></div>
|
||||
</div>
|
||||
<Table
|
||||
loading={loading}
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
expandedRowRender={expandRow}
|
||||
expandIconColumnIndex={5}
|
||||
expandIconAsCell={false}
|
||||
expandIcon={customExpandIcon}
|
||||
rowKey={'id'}
|
||||
expandedRowKeys={expandedRowKeys}
|
||||
onExpand={onExpand}
|
||||
pagination={{current: current, pageSize: pageSize, total: total, showSizeChanger: true, onShowSizeChange:onShowSizeChange, showQuickJumper: true, onChange: changePage}}
|
||||
className='resultListTable pb30'
|
||||
/>
|
||||
</div>
|
||||
<img src={bgPng} alt='' className='bgPng3'/>
|
||||
<img src={bgPng} alt='' className='bgPng4'/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default CheckResult;
|
|
@ -0,0 +1,134 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Form, Upload, Input, Icon, Button } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import banner from '../img/banner-interim.png';
|
||||
import img1 from '../img/img1.png';
|
||||
import bg from '../img/bgPng.png';
|
||||
import { httpUrl, main_site_url } from '../fetch';
|
||||
import {getMediumTermExamineInfo, submitMedium, getBriefMediumTermExamineMaterial} from '../api';
|
||||
import './index.scss';
|
||||
|
||||
function StudentSubmit(props){
|
||||
const {form, checkedTaskId, checkTime1, studentRegId, history} = props;
|
||||
const {getFieldDecorator, validateFieldsAndScroll } = form;
|
||||
const [detail, setDetail] = useState(undefined);
|
||||
const [reload, setReload] = useState(undefined);
|
||||
const [fileList, setFileList] = useState(undefined);
|
||||
// 学生提交材料时间
|
||||
const submitTime = new Date().getTime() < new Date('2022-08-21 24:0').getTime();
|
||||
// 学生查看导师评分时间
|
||||
const lookTime = new Date().getTime() > new Date('2022-08-24 24:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime();
|
||||
|
||||
useEffect(()=>{
|
||||
if(!checkTime1){
|
||||
history.push("/glcc");
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
// 查询是否已经提交考核材料
|
||||
if(lookTime){
|
||||
// 8.25学生可见导师评分
|
||||
checkedTaskId && getMediumTermExamineInfo(checkedTaskId).then(res=>{
|
||||
if(res && res.message === "success"){
|
||||
setDetail(res.data);
|
||||
}
|
||||
})
|
||||
}else{
|
||||
// 25号之前不可见
|
||||
checkedTaskId && getBriefMediumTermExamineMaterial(checkedTaskId).then(res=>{
|
||||
if(res && res.message === "success"){
|
||||
setDetail(res.data);
|
||||
}
|
||||
})
|
||||
}
|
||||
},[checkedTaskId, reload])
|
||||
|
||||
// 学生提交材料
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
validateFieldsAndScroll((err, values) => {
|
||||
if(!err){
|
||||
const {pptAttachment, defenceVideoUrl, codeOrPrUrl} = values;
|
||||
const pptAttachmentId = pptAttachment.file.response.data.id;
|
||||
const params = {
|
||||
pptAttachmentId,
|
||||
codeOrPrUrl,
|
||||
defenceVideoUrl,
|
||||
taskId: checkedTaskId,
|
||||
studentRegId: studentRegId
|
||||
}
|
||||
submitMedium(params).then(res=>{
|
||||
if(res && res.message === "success"){
|
||||
setReload(Math.random());
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
// 检查文件上传是否符合规定
|
||||
function changeFileList(e) {
|
||||
const {fileList} = e;
|
||||
const lastFile = fileList.splice(fileList.length-1, fileList.length);
|
||||
setFileList(lastFile);
|
||||
}
|
||||
return <div className="interimBox">
|
||||
<img src={banner} alt="" className="bannerInterim"/>
|
||||
<img src={bg} alt="" className="bg1"/>
|
||||
<img src={bg} alt="" className="bg2"/>
|
||||
<div className="mainBox">
|
||||
<div className="navBox font-16"><Link to={`/glcc`} className="linkBox">开源夏令营 / </Link>提交中期考核材料</div>
|
||||
<div className="tipBox mt30">
|
||||
<div className="font-15 spanBox">材料提交说明:</div>
|
||||
<div>1、请各位学生<a href={`${main_site_url.indexOf("gitlink") !== -1 ? '/api/attachments/392565' : httpUrl+'/busiAttachments/download/169'}`} className="blueSpan">下载PPT模板</a> ,根据课题开发进展,按照PPT模板要求填写课题学习调研方案、开发进度及开发成果、下半阶段工作计划等考核材料;</div>
|
||||
<div>2、欢迎各位学生录制本课题答辩视频,将视频链接填写至下方视频介绍填写栏;</div>
|
||||
<div>3、学生提交考核材料的时间为<span className="spanBox">2022年8月12日8点至2022年8月21日24点</span>,请在截止时间前提交。若在截止时间未提交中期考核表,则默认自动放弃,该课题将自动终止;</div>
|
||||
<div>4、学生在<span className="spanBox">2022年8月25日</span>可在此页面查看自己考核成绩,若对考核成绩有异议,请及时联系导师进行更改。</div>
|
||||
</div>
|
||||
<div className="titleBox mt25 font-18">
|
||||
<img src={img1} alt="" width={24} className="mr5"/>
|
||||
中期考核
|
||||
</div>
|
||||
{submitTime && !detail ? <Form className="referBox" onSubmit={submit} colon={false}>
|
||||
<Form.Item label="答辩视频" className="referItem">
|
||||
{getFieldDecorator('defenceVideoUrl', {
|
||||
rules: [{ required: true, message: '请输入视频链接!'}, {pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,message: "请正确输入链接"}],
|
||||
})(<Input placeholder="请输入视频链接" maxLength={900}/>)}
|
||||
</Form.Item>
|
||||
<Form.Item label="代码/pr地址" className="referItem">
|
||||
{getFieldDecorator('codeOrPrUrl', {
|
||||
rules: [{pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,message: "请正确输入链接"}],
|
||||
})(<Input placeholder="请输入代码或pr链接" maxLength={900}/>)}
|
||||
</Form.Item>
|
||||
<Form.Item label="PPT附件" className="referItem oneCont">
|
||||
{getFieldDecorator('pptAttachment', {
|
||||
rules: [{ required: true, message: '请上传PPT附件!' }],
|
||||
})(<Upload className="avatar-uploader" action={httpUrl + `/busiAttachments/upload`} onChange={changeFileList} fileList={fileList}>
|
||||
<Button className="uploadBox"><Icon type="upload" /> 上传</Button></Upload>)}
|
||||
</Form.Item>
|
||||
<Form.Item className="referItem oneCont">
|
||||
<Button style={{width: '100px', height: '36px'}} className="mt30 ml20" type="primary" htmlType="submit">提交</Button>
|
||||
</Form.Item>
|
||||
</Form> : <div className={`reviewBox referBox resultBox ${!detail ? 'nullData' : ''}`}>
|
||||
<div className="flexBox">
|
||||
<div className="mustSpan mb20"> 答辩视频<span className="blueBg ml10"><a href={detail && detail.defenceVideoUrl} target="_blank">{detail && detail.defenceVideoUrl}</a></span></div>
|
||||
<div>代码/pr地址<span className="blueBg ml10"><a href={detail && detail.codeOrPrUrl} target="_blank">{detail && detail.codeOrPrUrl}</a></span></div>
|
||||
<div className="mustSpan ppt mb20"> PPT附件{detail && <i className="iconfont icon-lianjie3 font-13 mr5 ml10"></i>}{detail ? <a className="pptAttachment mr10" href={`${httpUrl}/busiAttachments/download/${detail.pptAttachment.id}`}>{`${detail.pptAttachment.fileName}`}</a> : <Button className="uploadBox ml10" disabled><Icon type="upload" /> 上传</Button>}{detail && detail.pptAttachment && detail.pptAttachment.fileSizeString}</div>
|
||||
</div>
|
||||
{!detail && <div className="nullDateTip font-15">很遗憾,您在指定时间内未提交考核材料</div>}
|
||||
{lookTime && detail && !detail.glccTutorEvaluation && <div className="font-15 nullDateTip">您的课题导师暂未评分,请提醒导师尽快提交中期考核评分</div>}
|
||||
{detail && detail.glccTutorEvaluation && <div className="tutorRes">
|
||||
<div>您的课题导师对您的中期考核评价如下,如有异议,请及时联系导师进行更改。</div>
|
||||
<div className="mt10 mb10 smallTil">
|
||||
工作态度: <span className="blueSpan ml5">{detail.glccTutorEvaluation.workAttitudeEvaluation}</span><br/>
|
||||
开发进度: <span className="blueSpan ml5">{detail.glccTutorEvaluation.developProgressEvaluation}</span><br/>
|
||||
项目完成质量: <span className="blueSpan ml5">{detail.glccTutorEvaluation.projectQualityEvaluation}</span><br/>
|
||||
总体评分: <span className="blueSpan ml5">{detail.glccTutorEvaluation.totalityEvaluation}</span><span className={`passStatusBox ml20 font-15 ${detail.glccTutorEvaluation.totalityEvaluation === 'D' ? '' : 'pass'}`}>{detail.glccTutorEvaluation.totalityEvaluation === 'D' ? '未通过' : '通过'}</span>
|
||||
</div>
|
||||
<div className="flexBox"><div><span className="smallTil">导师评语: </span><span className="ml10 tutorRemark">{detail.glccTutorEvaluation.comment}</span></div></div>
|
||||
</div>}
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
export default Form.create()(StudentSubmit);
|
|
@ -0,0 +1,183 @@
|
|||
import React, {useState, useEffect, useCallback} from "react";
|
||||
import { Form, Input, Icon, Button, Tabs, Radio, message } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import bg from '../img/bgPng.png';
|
||||
import { getAuditList, getMediumTermExamineInfo, submitTutorEvaluation, updateTutorEvaluation, hasAuditRole } from '../api';
|
||||
import './index.scss';
|
||||
import '../check/index.scss'
|
||||
import Nodata from "../../forge/Nodata";
|
||||
import {httpUrl} from '../fetch';
|
||||
const { TabPane } = Tabs;
|
||||
const { TextArea } = Input;
|
||||
|
||||
function TutorReview(props){
|
||||
const {form, current_user, showNotification, checkTime2, history} = props;
|
||||
const {getFieldDecorator, setFieldsValue, validateFieldsAndScroll, resetFields } = form;
|
||||
const [taskId, setTaskId] = useState();
|
||||
const [taskList, setTaskList] = useState([]);
|
||||
const [wordNum, setWordNum] = useState(0);
|
||||
const [detail, setDetail] = useState(undefined);
|
||||
const [reload, setReload] = useState(undefined);
|
||||
// 导师提交过之后是否是编辑状态
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if(!checkTime2){
|
||||
history.push("/glcc");
|
||||
}else if (!current_user.login) {
|
||||
history.push('/login?go_page=/glcc/middle/examination');
|
||||
}else{
|
||||
hasAuditRole({ userId: current_user.user_id }).then(res => {
|
||||
if (!(res && res.message == 'success' && res.data.hasRole)) {
|
||||
history.push('/glcc');
|
||||
}
|
||||
})
|
||||
}
|
||||
getAuditList({ userId: current_user.user_id, pass: 1 }).then(res => {
|
||||
if (res.message === 'success') {
|
||||
const filterStuNull = res.data.rows.filter(item=>{return item.studentName !== null})
|
||||
setTaskList(filterStuNull);
|
||||
filterStuNull.length && setTaskId(filterStuNull[0].id);
|
||||
} else {
|
||||
res && showNotification(res.message || '查询课题列表失败');
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
// 查询是否已经提交考核材料
|
||||
taskId && getMediumTermExamineInfo(taskId).then(res=>{
|
||||
if(res && res.message === "success"){
|
||||
setDetail(res.data);
|
||||
if(res && res.data && res.data.glccTutorEvaluation){
|
||||
setWordNum(res.data.glccTutorEvaluation.comment.length);
|
||||
setFieldsValue({...res.data.glccTutorEvaluation});
|
||||
}
|
||||
}
|
||||
})
|
||||
},[taskId, reload])
|
||||
|
||||
// 学生提交材料
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
validateFieldsAndScroll((err, values) => {
|
||||
if(!err){
|
||||
const params = {
|
||||
...values,
|
||||
mediumTermExamineMaterialId: detail.id,
|
||||
tutorUserId: detail.studentRegId
|
||||
}
|
||||
if(detail.glccTutorEvaluation){
|
||||
params['id'] = detail.glccTutorEvaluation.id;
|
||||
updateTutorEvaluation(params).then(res=>{
|
||||
if(res && res.message === "success"){
|
||||
setIsEdit(false);
|
||||
message.success("修改成功");
|
||||
setReload(Math.random());
|
||||
}
|
||||
})
|
||||
}else{
|
||||
submitTutorEvaluation(params).then(res=>{
|
||||
if(res && res.message === "success"){
|
||||
message.success("您已成功评分");
|
||||
setReload(Math.random());
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const RadioGroup = <Radio.Group disabled={detail && detail.glccTutorEvaluation && !isEdit}>
|
||||
<Radio value={'S'}>S</Radio>
|
||||
<Radio value={'A'}>A</Radio>
|
||||
<Radio value={'B'}>B</Radio>
|
||||
<Radio value={'C'}>C</Radio>
|
||||
<Radio value={'D'}>D</Radio>
|
||||
</Radio.Group>;
|
||||
|
||||
const helper = useCallback(
|
||||
(label, name, rules, widget, className) => (
|
||||
<Form.Item label={label} className={`referItem ${className}`}>
|
||||
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
|
||||
</Form.Item>
|
||||
),[taskId, reload]
|
||||
);
|
||||
|
||||
return <div className="interimBox glcc-check">
|
||||
<img src={bg} alt="" className="bg1"/>
|
||||
<img src={bg} alt="" className="bg2"/>
|
||||
<div className="mainBox">
|
||||
<div className="navBox font-16"><Link to={`/glcc`} className="linkBox">开源夏令营 / </Link>导师拟定考核结果</div>
|
||||
<div className="tipBox mt30">
|
||||
<div className="font-15 spanBox">导师考核说明:</div>
|
||||
<div>1、请各位导师根据“工作态度”“开发进度”“项目完成质量”“总体评分”四个角度,根据学生提交的考核材料与实际开发情况客观地进行打分。打分标准分为:S:特别优秀、A:优秀、B:良好、C:合格、D:不合格五个等级。</div>
|
||||
<div>2、“总体评分”这一项将决定学生是否通过本次考核。若总体评分为“S、A、B、C”,则视为通过中期考核。若该结果为“D”,则该课题中期考核不通过,课题将自动终止。请各位导师谨慎做出评价。</div>
|
||||
<div>3、导师提交打分结果后,可对考核结果进行更改,更改考核结果截止日期为<span className="spanBox">2022年8月25日24点</span></div>
|
||||
<div>4、北京时间<span className="spanBox">2022年8月26日20点</span>前GLCC官网将公布中期考核结果,敬请留意。</div>
|
||||
</div>
|
||||
<div className="tutorContent">
|
||||
<Tabs className="task-tabs" onChange={(e) => { resetFields(); setTaskId(e); setIsEdit(false); setWordNum(0); }} activeKey={taskId + ''}>
|
||||
{
|
||||
taskList.map((item, i) => {
|
||||
return <TabPane tab={`课题${i + 1}`} key={item.id} >
|
||||
<div className="task-title-stuName font-16">{item.studentName} — {item.taskName}</div>
|
||||
</TabPane>
|
||||
})
|
||||
}
|
||||
</Tabs>
|
||||
{detail ? <div className="reviewBox">
|
||||
{/* 已评分提示语句 */}
|
||||
{detail.glccTutorEvaluation && !isEdit && <div className="successReviewBox font-15">您已评分成功! 在审核期间,您可对评分结果进行更改</div>}
|
||||
{/* 学生提交的资料 */}
|
||||
<div className="stuCont pl20 pr20">
|
||||
<div className="title font-16 mb15"><span className="blueBox"></span>基本信息</div>
|
||||
<div className="flexBox">
|
||||
<div> 答辩视频:<span className="blueSpan ml10"><a className="blueSpan" href={detail.defenceVideoUrl} target="_blank">{detail.defenceVideoUrl}</a></span></div>
|
||||
<div>代码/pr地址:<span className="blueSpan ml10"><a className="blueSpan" href={detail.codeOrPrUrl} target="_blank">{detail.codeOrPrUrl}</a></span></div>
|
||||
<div> PPT附件:<i className="iconfont icon-lianjie3 font-13 mr5 ml10"></i><a className="mr10 pptAttachment" href={`${httpUrl}/busiAttachments/download/${detail.pptAttachment.id}`}>{detail.pptAttachment.fileName}</a>{detail.pptAttachment.fileSizeString}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="title font-16 pl20 mt25"><span className="blueBox"></span>导师评分</div>
|
||||
{/* 导师打分 */}
|
||||
<Form className="referBox tutor" onSubmit={submit} labelAlign="left" labelCol={{ span: 5 }} colon={false}>
|
||||
{helper('工作态度',
|
||||
'workAttitudeEvaluation',
|
||||
[{ required: true, message: "请打分" }],
|
||||
RadioGroup
|
||||
)}
|
||||
{helper('工作进度',
|
||||
'developProgressEvaluation',
|
||||
[{ required: true, message: "请打分" }],
|
||||
RadioGroup
|
||||
)}
|
||||
{helper('项目完成质量',
|
||||
'projectQualityEvaluation',
|
||||
[{ required: true, message: "请打分" }],
|
||||
RadioGroup
|
||||
)}
|
||||
{helper('总体评分',
|
||||
'totalityEvaluation',
|
||||
[{ required: true, message: "请打分" }],
|
||||
RadioGroup
|
||||
)}
|
||||
<div className="remarkBox">
|
||||
{helper('填写评语',
|
||||
'comment',
|
||||
[{ required: true, message: "请输入评语!" }],
|
||||
<TextArea rows={8} placeholder="请对学生项目表现进行总体评价" className="contentBox" maxLength={500} onChange={(e)=>{setWordNum(e.target.value.length);}} disabled={detail.glccTutorEvaluation && !isEdit}/>,
|
||||
'oneCont'
|
||||
)}
|
||||
<div className="wordNum">{wordNum} / 500</div>
|
||||
</div>
|
||||
<Form.Item className="referItem oneCont">
|
||||
{(!detail.glccTutorEvaluation || (detail.glccTutorEvaluation && isEdit)) && <Button style={{width: '100px', height: '36px'}} className="mt20" type="primary" htmlType="submit">{detail.glccTutorEvaluation ? '保存' : '提交'}</Button>}
|
||||
{detail.glccTutorEvaluation && !isEdit && <Button style={{width: '100px', height: '36px'}} className="mt20" type="primary" onClick={()=>{setIsEdit(true)}}>修改</Button>}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>:<Nodata _html="该课题学生暂未提交中期考核材料,请提醒学生尽快提交"/>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
export default Form.create()(TutorReview);
|